Repository: vuejs/vue
Pull request: Add handleError during event handling

Currently handleError is used to handle errors during lifecycle hooks and component rendering. This handles most errors thrown by the component however event handlers are not handled. So a component like:

```
<template>
  <button @click="error()">I Throw Errors</button>
</template>

<script>
  {
    methods: {
      error() { throw new Error('thrown!'); }
    }
  }
</script>
```

Will simple throw an error and the error will never be passed to `handleError` nor is one able to use `Vue.config.errorHandler` for error reporting. For my specific use case I wanted to make sure that the plugin I use to report errors to Honeybadger would catch and report errors in event handlers, but more generally any error reporting that relies on `errorHandler` will require this.

It makes sense to me that, for consistency, Vue would want to handle errors in event handlers the same way as is done lifecycle hooks and rendering. This is consistent with the changes requested and completed in #5198 and #4370.

As this is my first PR, I hope these changes are up to the code standard for the project, but if there is anything else needed just let me know and I'll fix it.

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [ ] Bugfix
- [x] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [x] Yes
- [ ] No

If yes, please describe the impact and migration path for existing applications:

While it is unlikely any running apps would have depended on this behaviour. This introduces a change which will result in exceptions thrown by event handlers to be handled by `handleError` and any function assigned to `Vue.config.errorHandler`. Since errors are not re-thrown by `handleError` any code that was depending on errors in events not being handled could theoretically break. I thought it would be worthwhile to note this.

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [x] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**


Changed files:
filename: src/core/instance/events.js
patch: @@ -1,7 +1,13 @@
 /* @flow */
 
+import {
+  tip,
+  toArray,
+  hyphenate,
+  handleError,
+  formatComponentName
+} from '../util/index'
 import { updateListeners } from '../vdom/helpers/index'
-import { toArray, tip, hyphenate, formatComponentName } from '../util/index'
 
 export function initEvents (vm: Component) {
   vm._events = Object.create(null)
@@ -121,7 +127,11 @@ export function eventsMixin (Vue: Class<Component>) {
       cbs = cbs.length > 1 ? toArray(cbs) : cbs
       const args = toArray(arguments, 1)
       for (let i = 0, l = cbs.length; i < l; i++) {
-        cbs[i].apply(vm, args)
+        try {
+          cbs[i].apply(vm, args)
+        } catch (e) {
+          handleError(e, vm, `event handler for "${event}"`)
+        }
       }
     }
     return vm

filename: test/unit/features/error-handling.spec.js
patch: @@ -11,7 +11,8 @@ describe('Error handling', () => {
     ['beforeCreate', 'beforeCreate hook'],
     ['created', 'created hook'],
     ['beforeMount', 'beforeMount hook'],
-    ['directive bind', 'directive foo bind hook']
+    ['directive bind', 'directive foo bind hook'],
+    ['event', 'event handler for "e"']
   ].forEach(([type, description]) => {
     it(`should recover from errors in ${type}`, done => {
       const vm = createTestInstance(components[type])
@@ -215,6 +216,19 @@ function createErrorTestComponents () {
     }
   }
 
+  // event errors
+  components.event = {
+    beforeCreate () {
+      this.$on('e', () => { throw new Error('event') })
+    },
+    mounted () {
+      this.$emit('e')
+    },
+    render (h) {
+      return h('div')
+    }
+  }
+
   return components
 }