preface

In the previous project, we needed to collect and report global errors. Finally, we had a headache that the asynchronous errors in Vue Watch could not be reported to errorHandler. Then one day when I read the Vue code again, I found that Vue had fixed this problem in version 2.6.13. !!!!!

example

You can switch the Vue version number to see the effect, and you will see that <= 2.6.12 watch does not catch asynchronous errors

<! - vue 2.6.12 -- > < script SRC = "https://cdn.jsdelivr.net/npm/[email protected]" > < / script > < div id = "app" > "button @click='num++'>{{num}}</button> </div> <script> vue.config. errorHandler = (err, VM, info) => {console.log(' Error collected: ', err) } new Vue({ el: '#app', data: { num: 100 }, watch: {async num() {// add await to catch async error await this.errorfnc ()}}, methods: ErrorFnc () {return new Promise((resolve, reject) => {reject(' Promise error ')})}, Async errorFnc() {// throw 'async error '//},}}) </script>Copy the code

How is Vue solved

2.6.12

Vue.prototype.$watch = function ( expOrFn: string | Function, cb: any, options? : Object ): Function { const vm: Component = this if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, The options)} options = options | | {} options. The user = true / / Watcher inside the callback function and as below, Const watcher = new watcher (VM, expOrFn, cb, options) if (options.immediate) {try {// Audit cb.call(vm, expOrFn, cb, options) const watcher = new watcher (VM, expOrFn, cb, options) if (options.immediate) {try { watcher.value) } catch (error) { handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`) } } return function unwatchFn () { watcher.teardown() } }Copy the code

2.6.13

Vue.prototype.$watch = function ( expOrFn: string | Function, cb: any, options? : Object ): Function { const vm: Component = this if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, The options)} options = options | | {} options. The user = true / / Watcher inside the callback function and as below, Const watcher = new url (VM, expOrFn, cb, audit, audit, audit, audit, audit, audit, audit, audit) options) if (options.immediate) { const info = `callback for immediate watcher "${watcher.expression}"` pushTarget() // InvokeWithErrorHandling (cb, vm, [watcher. Value], vm, info) popTarget() } return function unwatchFn () { watcher.teardown() } }Copy the code

Compare the version

The difference is that the callback is executed by invokeWithErrorHandling. If it is a promise, it will be caught and reported by handleError.

export function invokeWithErrorHandling ( handler: Function, context: any, args: null | any[], vm: any, info: string ) { let res try { res = args ? handler.apply(context, args) : handler.call(context) if (res && ! res._isVue && isPromise(res) && ! res._handled) { res.catch(e => handleError(e, vm, info + ` (Promise/async)`)) // issue #9511 // avoid catch triggering multiple times when nested calls res._handled = true } } catch (e) { handleError(e, vm, info) } return res }Copy the code

thinking

One might ask, why don’t you try and catch your own error message, or what does that do?

  1. Try and catch yourself. It’s a lot of repetition.

  2. This is a small fix for Vue, but for an online project, failing to report all of your bugs can affect the user experience, lead to user churn, and even cost the company property.

How does Vue collect and report errors

For us developers, it is better not to manually report errors, which will lead to a lot of repetitive work. It is better to just focus on our normal business logic, while for Vue project, Vue will automatically report our errors. We just need to make sure that we write in a certain way so that the errors will not be lost.

The first step

The only place we need to report errors globally is Vue errorHandler. Vue will report all errors to this function. You can either apply Sentry directly or call the background error reporting interface in this function.

The second step

We have identified where the error was reported, and what we need to do is make sure that all errors are caught by the Vue. For synchronous tasks, errors are caught directly, and for asynchronous tasks, we have to use a certain way of writing.

Asynchronous error

The most common part of our project is interacting with the background, as follows

Writing a

This is one of the most common things I’ve seen in a project. Once we use THEN for asynchronous tasks, it means that our errors will not be caught by the Vue. If we have an error in the THEN callback, we have to write a.catch at the end to catch the error in the THEN callback. This way of writing adds a lot of work for us developers.

Mounted () {this.getData()}, methods: {{getData (). HTTP get (' XXX '). Then (data = > {} / / XXX, error = > {/ / report only their asynchronous error})}}Copy the code

Write two

We can just replace our then with async await and all errors will be caught and much cleaner

Async mounted() {// async mounts () {// async mounts () {}, methods: { async getData() { const data = await http.get('xxx') // xxx } }Copy the code

How to ensure that everyone uses async syntax development

If you have a project where everyone can follow this, you don’t have to look further.

For the development project, the developer is not controllable, the coding style is also changeable, and even if you remember which way to write, there will be negligence in the actual development, or can use tools to solve the problem without verbal constraints.

With the help of eslint

Eslint-plugin-leon-rule: Eslint-plugin-leon-rule: Eslint-plugin-leon-rule: eslint-plugin-leon-rule: eslint-plugin-leon-rule: eslint-plugin-leon-rule:

If you want to develop your own ESLint plugin, you can refer to this article: How to write an ESLint plugin by hand

The last