本文主要讲述 vue内部运行机制
阅读本文可收获
1.对vue内部运行机制有系统性的了解
2.数据劫持 + 发布订阅者模式 实现响应式
3.编译过程简单了解
4.vue的异步批量更新策略
5.为什么引入vNode 以及 diff 算法
6.patch 是如何比较虚拟节点并得出差异的
内部流程总览图
1.简述内部流程
1.1 new Vue()初始化 与 数据劫持
实例化vue对象new Vue()
,会调用_init()
函数进行初始化,这个过程会初始化 生命周期、事件、 props、 methods、 data、 computed 与 watch 等。并通过Object.defineProperty
进行数据劫持,设置get,set,用于后期的依赖收集
以及响应式
!
1.2 $mount 挂载
初始化结束,会执行$mount
进行挂载,此时会判断是否存在render funtion
,如果存在则执行,否则会进入compile
编译阶段;
在main.js,会存render funtion
执行render 渲染最终挂载到 #app
这个dom中
v2
new Vue({router,render: (h) => h(App),
}).$mount('#app');
v3
createApp(App).use(store).use(router).mount("#app")
如果是页面组件或者业务组件会对 template
进行编译,进而得到 render function
,执行render
得到 vNode
, 进行 patch
过程最终进行渲染并挂载到对应的文档dom
$mount()
本质是想将render渲染模板挂载到文档dom中;
1.3 compile() 编译
编辑阶段会调用compile()
,可以分成 parse
、optimize
与 generate
三个阶段,最终需要得到 render function;
parse
会用正则等方式解析 template 模板中的指令、class、style等数据,形成AST。
我们大概看下AST是什么样的
<div :class="c" class="demo" v-if="isShow"><span v-for="item in sz">{
{item}}</span>
</div>
{/* 标签属性的map,记录了标签上属性 */'attrsMap': {':class': 'c','class': 'demo','v-if': 'isShow'},/* 解析得到的:class */'classBinding': 'c',/* 标签属性v-if */'if': 'isShow',/* v-if的条件 */'ifConditions': [{'exp': 'isShow'}],/* 标签属性class */'staticClass': 'demo',/* 标签的tag */'tag': 'div',/* 子标签数组 */'children': [{'attrsMap': {'v-for': "item in sz"},/* for循环的参数 */'alias': "item",/* for循环的对象 */'for': 'sz',/* for循环是否已经被处理的标记位 */'forProcessed': true,'tag': 'span','children': [{/* 表达式,_s是一个转字符串的函数 */'expression': '_s(item)','text': '{
{item}}'}]}]
}
optimize
的主要作用是标记 static 静态节点(还标记了注释节点,文本节点等) 这是 Vue 在编译过程中的一处优化,后面当 update
更新界面时,会有一个 patch
的过程, diff 算法
会直接跳过静态节点,从而减少了比较的过程,优化了 patch
的性能。
generate
是将 AST 转化成 render function 字符串的过程,得到结果是 render 的字符串以及 staticRenderFns 字符串。
在经历过 parse
、optimize
与 generate
这三个阶段以后,组件中就会存 render function 了。render function 会被转化成 VNode
节点.
VNode 是就是一个json对象,用来描述改节点; 例如
Virtual DOM
{tag: 'div', /*说明这是一个div标签*/children: [ /*存放该标签的子节点*/{tag: 'a', /*说明