You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** * 通知 dep 中的所有 watcher,执行 watcher.update() 方法 */notify(){// stabilize the subscriber list firstconstsubs=this.subs.slice()// 遍历 dep 中存储的 watcher,执行 watcher.update()for(leti=0,l=subs.length;i<l;i++){subs[i].update()}}
/** * 由 刷新队列函数 flushSchedulerQueue 调用,如果是同步 watch,则由 this.update 直接调用,完成如下几件事: * 1、执行实例化 watcher 传递的第二个参数,updateComponent 或者 获取 this.xx 的一个函数(parsePath 返回的函数) * 2、更新旧值为新值 * 3、执行实例化 watcher 时传递的第三个参数,比如用户 watcher 的回调函数 */run(){if(this.active){// 调用 this.get 方法constvalue=this.get()if(value!==this.value||// Deep watchers and watchers on Object/Arrays should fire even// when the value is the same, because the value may// have mutated.isObject(value)||this.deep){// 更新旧值为新值constoldValue=this.valuethis.value=valueif(this.user){// 如果是用户 watcher,则执行用户传递的第三个参数 —— 回调函数,参数为 val 和 oldValtry{this.cb.call(this.vm,value,oldValue)}catch(e){handleError(e,this.vm,`callback for watcher "${this.expression}"`)}}else{// 渲染 watcher,this.cb = noop,一个空函数this.cb.call(this.vm,value,oldValue)}}}}
watcher.get
/src/core/observer/watcher.js
/** * 执行 this.getter,并重新收集依赖 * this.getter 是实例化 watcher 时传递的第二个参数,一个函数或者字符串,比如:updateComponent 或者 parsePath 返回的函数 * 为什么要重新收集依赖? * 因为触发更新说明有响应式数据被更新了,但是被更新的数据虽然已经经过 observe 观察了,但是却没有进行依赖收集, * 所以,在更新页面时,会重新执行一次 render 函数,执行期间会触发读取操作,这时候进行依赖收集 */get(){// 打开 Dep.target,Dep.target = thispushTarget(this)// value 为回调函数执行的结果letvalueconstvm=this.vmtry{// 执行回调函数,比如 updateComponent,进入 patch 阶段value=this.getter.call(vm,vm)}catch(e){if(this.user){handleError(e,vm,`getter for watcher "${this.expression}"`)}else{throwe}}finally{// "touch" every property so they are all tracked as// dependencies for deep watchingif(this.deep){traverse(value)}// 关闭 Dep.target,Dep.target = nullpopTarget()this.cleanupDeps()}returnvalue}
Vue 源码解读(4)—— 异步更新
当学习成为了习惯,知识也就变成了常识。 感谢各位的 点赞、收藏和评论。
新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn
文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。
封面
前言
上一篇的 Vue 源码解读(3)—— 响应式原理 说到通过
Object.defineProperty
为对象的每个 key 设置 getter、setter,从而拦截对数据的访问和设置。当对数据进行更新操作时,比如
obj.key = 'new val'
就会触发 setter 的拦截,从而检测新值和旧值是否相等,如果相等什么也不做,如果不相等,则更新值,然后由dep
通知watcher
进行更新。所以,异步更新
的入口点就是 setter 中最后调用的dep.notify()
方法。目的
深入理解 Vue 的异步更新机制
nextTick 的原理
源码解读
dep.notify
关于 dep 更加详细的介绍请查看上一篇文章 —— Vue 源码解读(3)—— 响应式原理,这里就不占用篇幅了。
watcher.update
queueWatcher
nextTick
timerFunc
flushCallbacks
flushSchedulerQueue
watcher.run
watcher.get
以上就是 Vue 异步更新机制的整个执行过程。
总结
面试官 问:Vue 的异步更新机制是如何实现的?
答:
Vue 的异步更新机制的核心是利用了浏览器的异步任务队列来实现的,首选微任务队列,宏任务队列次之。
当响应式数据更新后,会调用 dep.notify 方法,通知 dep 中收集的 watcher 去执行 update 方法,watcher.update 将 watcher 自己放入一个 watcher 队列(全局的 queue 数组)。
然后通过 nextTick 方法将一个刷新 watcher 队列的方法(flushSchedulerQueue)放入一个全局的 callbacks 数组中。
如果此时浏览器的异步任务队列中没有一个叫 flushCallbacks 的函数,则执行 timerFunc 函数,将 flushCallbacks 函数放入异步任务队列。如果异步任务队列中已经存在 flushCallbacks 函数,等待其执行完成以后再放入下一个 flushCallbacks 函数。
flushCallbacks 函数负责执行 callbacks 数组中的所有 flushSchedulerQueue 函数。
flushSchedulerQueue 函数负责刷新 watcher 队列,即执行 queue 数组中每一个 watcher 的 run 方法,从而进入更新阶段,比如执行组件更新函数或者执行用户 watch 的回调函数。
完整的执行过程其实就是今天源码阅读的过程。
面试关 问:Vue 的 nextTick API 是如何实现的?
答:
Vue.nextTick 或者 vm.$nextTick 的原理其实很简单,就做了两件事:
将传递的回调函数用
try catch
包裹然后放入 callbacks 数组执行 timerFunc 函数,在浏览器的异步任务队列放入一个刷新 callbacks 数组的函数
链接
感谢各位的:点赞、收藏和评论,我们下期见。
当学习成为了习惯,知识也就变成了常识。 感谢各位的 点赞、收藏和评论。
新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn
文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。
The text was updated successfully, but these errors were encountered: