Vue - 模板计算过程
Vue 的模板计算过程应该是 Vue 源码中最大头的东西,也是响应式数据外另一个核心。
其实自己也没读完,或者弄得非常明白,所以打算边写边读,加深理解。
这篇主要写 Vue 在模板计算前的一些准备工作。以及其触发的场景之类的。
渲染前的调用过程
new Vue()
当我们启动一个 Vue 项目的时候,毫无意外 new Vue(options)
会是一切的起点,而 new 一个 Vue 的实例实际上主要执行的是 Vue.prototype._init
。
init
Vue.prootytpe._init
在 core/instance/init.js 中定义。
_init
函数做的事情很多,初始化各个参数,调用各种底层 API, 调用各个生命周期函数,最后会执行 vm.$mount(vm.$options.el)
。从这里开始挂载我们的模板到页面上,但在此之前还需要解析模板上各种自定义的指令。
$mount
Vue.prototype.$mount
在 platforms/web/runtime/index.js 文件中定义。
|
|
这里简单说一下 hydrating 这参数,我们是不需要太关心的,因为这是 vue-ssr 渲染时才会用上,所以之后我们都会忽略这个参数。
这里我们发现 $mount 只是一层装饰,核心在于 mountComponent
这个函数的执行。
在 platforms/web/entry-runtime-with-compiler.js 中会进行一层装饰
这里省略的代码就是整个模板从计算 -> 渲染 -> 挂载中的第一步:计算。
这里将一个 render Function 的字符串形式准备好,存储到 vm._render 中,等待模板的下一步处理过程。
mountComponent
mountComponent
定义在 /core/instance/lifecycle.js。
mountComponent
这个函数主要做以下几件事:
- 保存挂载的父元素到 $el 属性上;
- 如果我们传递的 options 里面没有定义 render 函数,则默认为生成一个空的 vnode 实例。并且非生产环境会报错;
- 执行
beofreMount
生命周期的钩子函数; - 定义 updateComponent, 作为 expOrFn 保存到一个 Watcher 的实例中,实例保存到
vm._watcher
的属性中; - 执行
mount
生命周期的钩子函数;
这里最核心的就是下面的代码:
这段函数最核心的地方就是定义好了 updateComponent,并生成一个 Watcher 的实例,这里定义一个 watcher,那么它的 value 就是我们最终模板生成的 dom,而 expOrFn 就是我们更新 dom 的函数,看到最后面就会明白这是一个怎样的闭环了。
在 /core/instance/lifecycle.js
中的 lifecycleMixin
中我们就可以看到 Vue.prototype.$forceUpdate
是通过调用 watcher 实例上的 update 方法完成的更新视图。
而 updateComponent 函数的定义也有几个地方需要了解一下:
- vnode 参数的准备来自于
Vue.prototype._render
,用于将模板的 DOM 转换成 vnode 对象。 vm._update
就是继续向下承接的函数,会在那里面完成最终的渲染(这一层层的函数包裹,哇,看得我头皮发麻 🙄 )。
update
Vue.prototype._update
定义在 /core/instance/lifecycle.js。
|
|
这里的 patch 就是最后我们所说的那个大家伙了,之前所有的工作,都是只是准备工作。
这里很简单的一个判断是,当前渲染的 vm 实例是根组件,还是某个子组件。
patch
Vue.prototype.__patch__
定义比较繁复,从 platform/runtime/index.js 开始,到 ./patch.js(这里有两个参数 nodeOps & modules 稍后会讲一下)最后在 core/vdom/patch.js 中看到完完整整的实现。
|
|
|
|
最后我们返回了一个闭包函数,所以 vm.__patch__
实际上是 createPatchFunction 的返回结果,而 vm.__patch__
的执行结果是一个真实的 DOM 树。
所以最后我们可以看到 patch 执行前的主要工作是函数间的不断调用,每个函数单元又会做一些些准备。
两个工具
|
|
就是这里的两个参数,nodeOps 和 modules。
- nodeOps 是一组 DOM 操作方法的集合;
- modules 是一组指令生命周期的方法的集合;
nodeOps
在 /platforms/web/runtime/node-ops.js 文件中,我们可以看到大量 DOM 操作方法,这里就是一个工具类,在 patch 函数计算模板的过程中,会经常用到这些工具。
modules
modules 是一个合并数组,由 core/vdom/modules/index.js & web/runtime/modules/index 中的多个指令解析工具组合而成。
最终是这样一个数组
而每个对象,都是类似的结构:
|
|
这里是有约定在的,约定每个解析指令的工具都提供相同命名的钩子,在 patch 执行过程中用于解析模板上的各种指令。
总结
其实整个模板计算的闭环都已经讲完了,但是依然晕的不行,各种文件翻来翻去,各种调用,一大堆不知道什么时候就定义好的参数和方法。哇,真的是翻皮水 …
所以做了个脑图,抽离掉一些不那么重要的函数包裹,看一下代码核心逻辑是怎么流动的。
就先这样吧,之后再写 function patch
里面的逻辑吧。😶
作者: Listen
来源: http://swarosky44.github.io
链接: http://swarosky44.github.io/2017/07/14/Vue - 模板计算过程/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可