[咖聊] Vue 执行过程

1,590 阅读2分钟

品着咖啡聊代码,岂不乐哉?

昨日举杯饮瑞幸,忽然脑海中浮现一张 Vue 的生命线图。借着这股“咖啡因”劲,画出了下面这幅图:

Vue 执行过程.png

每个 Vue 项目中,都会调用 new Vue() 去初始化根实例。在 Vue 内部中,就会执行 Vue.prototype._init 方法,然后做一系列的初始化工作,比如事件、渲染、依赖注入、数据状态等,细节现在不谈,后面再单独搞一篇文章说明每个函数的作用。

vue_init.png

这里我们只看 initState 和实例挂载过程。在 initState 执行时,会对 propsdatamethodscomputedwatcher 分别初始化。

initState.png

这里我们只看 initData,因为 data 才是顶图中的主角。 initData 做了什么伟大的事情让它变得如此光鲜亮丽呢?

initData.png

删除了 2/3 的开发环境才有的代码,initData 也就只做了两件事——把 data 上的属性通过 proxy 代理到 vm 下(为什么我们能够直接通过 vm.name 访问到私有属性 _data 下的 name,原因就在这里!为了避免我的描述让你懵XX,补一个 🌰 )

proxy.png

整个 Vue 初始化过程到这里算结束啦!接下来就进入了数据驱动和响应式阶段——从调用 observe 开始:

observe.png

new Observer(value) 做的事很简单,判断 value 是数组的话就调用 observeArray ,对数组每一项单独调用 observe 变成响应式;是对象的话就调用 walk 。最终都会调用 defineReactive

defineReactive.png

defineReactive 使得对象被读取的时候会触发 getter 函数,被设值的时候会触发 setter 函数。那第一次触发 getter 是什么时候?

答案是实例执行 $mount 进行挂载的时候。如果我们用的是 SFC 方式写组件,还会经过 compile 过程——compileoptimizegenerate。具体每一个环节做了什么,后面再来一起讨论。然后会执行到 mountComponent

mountComponent.png

上述代码实例 render watcher

响应式原理.png

在生成 VNodevm._render 函数就会触发数据的 getter,从而发生依赖收集——把当前的 Watcher 对象放到 Depsubs 数组中。当数据被修改时,又会触发 setter 函数,通知依赖收集的结果更新,从而触发 render Watcher 调用 update 重新渲染。这里的更新渲染也不是实时更新的,会维护一个队列做异步更新。详细过程后面举 🌰 讲。

还有两个细节就是 VNode 的生成和 DOM 节点的生成过程。VNode 就是用 JavaScript 对象描绘 DOM 节点的过程。理解 VNode 可以跳转到 认识虚拟 DOM

diff.jpeg

数据更新后,会对比新、老的 VNode TreeDiff 过程),得到差异数据信息。再把差异通过 patchVNode 应用到 DOM 树上。

闲聊到这里,相信你已经对从 new Vue 到生成 DOM 的过程有一个初印象。我们接着喝☕️。