乐趣区

关于vue.js:Vue-修复了-watch-的-BUG

前言
在之前的我的项目中,须要做全局谬误的收集和上报,最初有个头疼的问题就是 Vue watch 中的异步谬误无奈上报到 errorHandler 外面,而后在某一天我再次浏览 Vue 代码的时候,发现他在 2.6.13 版本上修复了这个问题,开心!!!

例子
大家能够切换 Vue 的版本号,来看看成果,你会发现 <= 2.6.12 版本的 watch 都不会捕捉到异步谬误

<!-- vue 2.6.12 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>

<div id="app">
    <button @click='num++'>{{num}}</button>
</div>

<script>
    Vue.config.errorHandler = (err, vm, info) => {console.log('收集到谬误:', err)
    }

    new Vue({
        el: '#app',
        data: {num: 100},
        watch: {async num() {
                // 加 await 是为了捕捉异步谬误
                await this.errorFnc()}
        },
        methods: {errorFnc() {return new Promise((resolve, reject) => {reject('promise 谬误')
                })
            },
            // 或者 async 函数
            // async errorFnc() {
            //     throw 'async 谬误'
            // },
        }
    })
</script>
复制代码

Vue 是如何解决的
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, options)
    }
    options = options || {}
    options.user = true
    // Watcher 外面执行回调函数和上面一样,就不贴代码了
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      try {
        // 间接执行回调函数
        cb.call(vm, watcher.value)
      } catch (error) {handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    return function unwatchFn () {watcher.teardown()
    }
}
复制代码

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, options)
    }
    options = options || {}
    options.user = true
    // Watcher 外面执行回调函数和上面一样,就不贴代码了
    const watcher = new Watcher(vm, expOrFn, cb, 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()
    }
}
复制代码

比照版本
咱们发现两个版本不同的是执行回调函数的形式变了,通过 invokeWithErrorHandling 执行回调函数,如果是 promise 的话会被 catch,从而被 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
}
复制代码

思考
有人可能会问,为什么不 try catch 本人上报错误信息,或者这个有什么用?

本人 try catch,反复工作量极大。

对 Vue 来说这个是一个很小的修复,但对于一个线上我的项目来说,如果无奈上报你的所有谬误,那么有些中央就可能会影响到用户体验,产生用户的散失,甚至让公司财产损失。

Vue 如何进行谬误收集和上报
对于咱们开发者来说,最好是不必手动上报谬误,这会带来很多重复性的工作,咱们最好只用关注咱们的失常的业务逻辑,而对于 Vue 工程来说,Vue 会主动上报咱们的谬误,咱们只有保障肯定的写法,谬误就不会失落。

第一步
咱们全局只须要一个上报谬误的中央,那就是 Vue 的 errorHandler,Vue 会把所有谬误上报到这个函数,你能够间接利用 sentry,或者在这个函数外面调用后盾的谬误上报接口。

第二步
咱们确定了上报谬误的中央,上面要做的就是保障所有谬误能被 Vue 捕捉到,同步工作的谬误会被间接捕捉,而异步工作的谬误,咱们必须应用肯定的写法。

异步谬误
我的项目中咱们最多的是和后盾交互,如下

写法一
这个是我在我的项目中见的最多的写法,一旦应用了 then 来解决异步工作,就意味着咱们的谬误不会被 Vue 捕捉,如果咱们 then 回调函数外面呈现了谬误,咱们还得在最初面写一个 .catch 来捕捉 then 回调函数外面的谬误,这种写法给咱们开发者加大了很多的工作量。

mounted() {
    // 不会捕捉到谬误
    this.getData()},
methods: {getData() {http.get('xxx').then(data => {// xxx}, error => {// 只能本人上报异步谬误})
    }
}
复制代码

写法二
咱们只用换成 async await 来代替咱们 then 的写法,所有谬误就会被捕捉到,而且更加简洁

async mounted() {
    // 应用 await 能够捕捉到异步谬误
    await this.getData()},
methods: {async getData() {const data = await http.get('xxx')
        // xxx
    }
}
复制代码

如何保障所有人应用 async 语法开发
如果你的我的项目中大家都能够恪守这种写法,那就不必往下看了。

对于开发我的项目来说,开发者是不可控的,编码格调也是变幻无穷,而且就算记住了哪种写法,在理论开发的时候也会有忽略,还是能用工具解决的就不必口头去束缚。

借助 eslint
基于 eslint 咱们能够很轻松制订一套规定,但有一些规定是没有的,就须要咱们本人开发,我对下面 async 语法的束缚写了一个插件:eslint-plugin-leon-rule,大家能够参考下源码或者应用。

最初
如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163 互相学习,咱们会有业余的技术答疑解惑

如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点 star: https://gitee.com/ZhongBangKeJi/CRMEB 不胜感激!

退出移动版