Vue 3.0 在编译方面有哪些优化?
1. Vue.js 2.x 通过标记静态节点,优化 diff 的过程
2. Vue.js 3.x 通过标记和提升所有的静态根节点,diff 的时候只需要对比动态节点内容
- Fragments ( 升级 Vetur 插件 )
- 静态提升
- Patch flag
- 缓冲事件处理函数
详细解释:
此处我们用到线上编译器来查看 vue 2.x 与 vue3.x 的编译区别~
1. 首先看一下,当文件内部,不包含任何内容时,Vue2.x 编译是空的,Vue 3.x 编译内部包含 render 函数,返回为null
2. 我们先放入一个 Dom, 可以看到 vue2.x 和 vue 3.x 编译的部分完完全全重构了,之前 Vue2.x 采用,_c 的模式创建标签,_v 为 Vnode 节点, 而当前的 Vue 3.x 通过 _createBlock 生成 block tree
- Vue 2.x 数据更新并触发重新渲染的粒度是组件级的,单个组件内部需要遍历该组件的整个 vnode 树
- Vue.js 3.0 做到了通过编译阶段对静态模板的分析,编译生成了 Block tree。Block tree 是一个将模版基于动态节点指令切割的嵌套区块,每个区块内部的节点结构是固定的。每个区块只需要追踪自身包含的动态节点。
3. 新引入Fragments(片段)特性:Vue 3.x 模板中不需要再创建一个唯一的根节点,模板里可以直接放文本内容或者很多同级的标签, Vue2.x 需要唯一的节点
4. 静态提升:静态节点都会被提升到render 的外部,只有初始化时会被创建,再次调用render时不会再次创建,可以直接重用这些静态节点对应的vnode
5. Patch flag
首先看下面这个案例,模版中有三个div标签,其中只有最后一个标div签的TEXT部分是动态的
在之前的VDOM中,如果msg值发生改变,整个模版中的所有元素都需要重新渲染。但在Vue3.0中,在这个模版编译时,编译器会在动态标签末尾加上 /* Text*/
PatchFlag。只能带patchFlag 的 Node 才被认为是动态的元素,会被追踪属性的修改。并且 PatchFlag 会标识动态的属性类型有哪些,比如这里 的TEXT 表示只有节点中的文字是动态的。
6. cacheHandler 缓存事件处理函数减少了不必要的更新操作
正常情况下,当绑定一个事件:
<div> <p @click="handleClick">静态代码</p> </div>
模版会被编译为
export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("p", { onClick: _ctx.handleClick }, "静态代码", 8 /* PROPS */, ["onClick"]) ])) }
其中事件会每次从全局上下文中获取。而当开启了cacheHandler之后
export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("p", { onClick: _cache[1] || (_cache[1] = ($event, ...args) => (_ctx.handleClick($event, ...args))) }, "静态代码") ])) }
编辑器会为你动态创建一个内联函数,内联函数里面再去饮用当前组件上最新的handler。之后编辑器会将内联函数缓存。每次重新渲染时如果事件处理器没有变,就会使用缓存中的事件处理而不会重新获取事件处理器。这个节点就可以被看作是一个静态的节点。这种优化更大的作用在于当其作用域组件时,之前每次重新渲染都会导致组件的重新渲染,在通过handler缓存之后,不会导致组件的重新渲染了。