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不胜感激 !

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
你需要知道的 19 个 console 实用调试技巧
众所周知,浏览器的开发者工具为我们提供了强大的调试系统,可以用来查看DOM树结构、CSS样式调试、动画调试、JavaScript代码断点调试等。今天我们就来看看console调试的那些实用的调试技巧。 如今,我们项目的开发通常会使用React、Vue等前端框架,前端调试也变得更加有难度,除了使用React Dev Tools,Vue Dev Tools等插件之外,我们使用最多的就是console.log(),当然多数情况下,console.log()就能满足我们的需求,但是当数据变得比较复杂时,console.log()就显得有些单一。其实console对象为我们提供了很多打印的方法,下面是console对象包含的方法(这里使用的是Chrome浏览器,版本为 95.0.4638.54(正式版本) (arm64)): console 对象提供了浏览器控制台调试的接口,我们可以从任何全局对象中访问到它,如果你平时只是用console.log()来输出一些变量,那你可能没有用过console那些强大的功能。下面带你用console玩玩花式调试。 一、基本打印 1. console.log...
- 下一篇
Python量化数据仓库搭建系列2:Python操作数据库
Python量化数据仓库搭建系列2:Python操作数据库 本系列教程为量化开发者,提供本地量化金融数据仓库的搭建教程与全套源代码。我们以恒有数(UDATA)金融数据社区为数据源,将金融基础数据落到本地数据库。教程提供全套源代码,包括历史数据下载与增量数据更新,数据更新任务部署与日常监控等操作。 在上一节讲述中,我们选择了MySQL作为本系列教程的数据库,故本文着重讲解Python操作MySQL的步骤,并封装方法。在文末简单介绍Python操作MongoDB、SQLite、PostgreSQL数据库; 一、pymysql用法 1、安装pymysql模块 pip install pymysql 2、连接数据库 from pymysql import * # 打开数据库连接,数据库参数可以在MySQL界面或数据库配置文件中查看 conn = pymysql.connect(host = '数据库IP', port = '端口', user = '用户名', password = '密码', database='数据库名称') # 使用 cursor() 方法创建一个游标对象 cursor ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- 2048小游戏-低调大师作品
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Hadoop3单机部署,实现最简伪集群