Vue更新流程nextTick详解 接上一篇初始化流程
前言
这一篇主要会讲解vue的异步更新策略,包括虚拟dom和diff详细过程(还是最好对着源码流程看
)源码下载以及配置调试工具的过程在上一篇都有讲解这篇文章不在赘述
上一篇链接:https://juejin.cn/post/6923105370596933645
建议
本篇幅可能需要阅读半个小时左右,可以泡上一杯咖啡和花茶慢慢看,但是我觉得花上一些时间弄懂这些还是值得的
我们今天从core/observe/index.js
开始看
-
这个方法将数据变成响应式之后
-
当数据发生变化就会触发
dep.notify
进入更新流程
- 复制了
subs
副本(这个subs数组里面的参数是在执行defineReactive
get
的时候将watcher
放进去的) - 执行
update
开始更新watcher
队列
this.lazy
当计算属性computed生成的watcher就会执行这个this.sync
是当用户主动设置同步方式执行才会执行的操作一般不会这样做- 我们这次主要看执行
queueWatcher
异步更新的过程
-
首先可以看到
has
会根据watcher
里面的id
进行去重判断(has就是一个对象) -
将
watcher
放到queue
中等待将来一起更新 -
在往下继续走我们看到了
nextTick
注意:这里的nextTick就是我们在页面中用的this.$nextTIck和vue.nextTick -
我们可以先看一下当前在异步流程中
nexTIck
传入的参数flushSchedulerQueue
,这对我们后面的理解比较重要
- 之后我们会主要看这个
run
方法,目前先看到这里 重点是要分清nextTick
接收的是组件中传来的方法还是异步更新过程中传入的方法这个很重要
- 我们可以看到这里将传进来的
cb
方法包装到一个方法中push到了callbacks
内部 - 当前方法内部做了错误兼容,并且未来会执行
cb
方法 - 接下来会判断
pending
如果不是处于挂起的状态我们就可以开始执行了timerFunc
timerFuncs
是全局的变量
- 内部首选的是使用
promise.then
以微任务的方式去执行 - 如果当前环境不支持
promise
的情况会继续向下走使用MutationObserver
的方式去执行 - 如果以上的方式都不支持就会使用
setTimeout
的方式
- 复制了
callbacks
副本 - 当前执行的方法 如果是异步更新流程情况下那么执行的就是我们上面所说的
flushSchedulerQueue
- 我们继续看
flushSchedulerQueue
内部的run方法,到这里就和上面说的串起来了
- 方法内部主要执行的是
get
方法
- 方法内部执行
getter
方法 getter
方法就是watcher
实例化的时候传进来的第二个方法,也就是当前组件的更新方法- 总结来说
flushSchedulerQueue
内部的run
方法实际调用的就是当前组件的更新函数 - 如果你看过上一篇文章应该能知道这个更新函数是在哪里实例化传进来的,没看过也没关系我们现在重新去看一次
- 将更新方法赋值给
updateComponent
- 更新方法内部执行的是
_update
方法这里接收的第一个参数是组件的渲染函数_render
也就是说将来_update
方法内部接收的是_render
返回的值也就是转化完的vdom
new Watcher
的时候将updateComponent
传进去,这里的updateComponent
对应的就是上面说的getter
- 我们可以直接看到
_render
返回值就是一个vnode
- 82行就是转化
vnode
的过程 render.call
接收了两个参数,最后的vm.$createElement
就是我们render
方法中的那个h函数
(下一篇文章中讲怎么将模板语法树转换成渲染函数的时候,会讲怎么转换vnode
的过程)
- 这里会判断
prevVnode
如果存在证明是更新过程否则就是初始话流程 - 我们可以看到都会调用
__patch__
方法不同的是初始化流程传的第一个参数是宿主元素,更新过程传的是上一个的vnode - 这里走完之后页面也就更新完成了
- 比较两个VNode,只有三种类型操作:属性更新、文本更新、子节点更新
-
比较的过程主要是通过双指针的方式向中间靠拢做比较
-
首部和首部进行比较、尾部和尾部进行比较、老节点的首部和新节点的尾部进行比较、老节点的尾部和新节点的首部进行比较、主要以老节点列表为准去替换老节点的位置
-
新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren
-
如果新节点有子节点而老节点没有子节点,先清空老节点的文本内容,然后为其新增子节点
-
当新节点没有子节点而老节点有子节点的时候,则移除该节点的所有子节点
-
当新老节点都无子节点的时候,只做文本的替换
- 这里我们可以看到使用
key
来判断是否往下继续比较 - 当你在页面中绑定了
key
是下标的话只能是在页面中看着不报错实际和不绑定key
操作是一样的 - 不绑定默认就是
undefined
还会继续往后比较,如果绑定的是下标的情况那么默认两组下标都是一样的也会往后走这样key
就失去作用了所以大家尽量在开发的时候还是用唯一的uuid
(如果能确定两组节点不会进行更新也可以绑定下标为了页面不提示error
) - 后面还有
tag
等属性会继续判断
结语
创作不易如果大家觉得有帮助不妨点个赞?
新年了 最后给大家拜个年 新年快乐、万事吉祥
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!