Vue 3.0 编译做了哪些优化
diff方法优化
vue2.x中的虚拟dom是进行全量的对比。而vue3.0新增了静态标记PatchFlag
。在与上次虚拟节点进行对比的时候,只对比带有PatchFlag
的节点,并且可以通过flag的信息得知当前节点要对比的具体内容
<div>
<div>我是一个静态节点</div>
<div>我是一个动态节点{{a + b}}</div>
</div>
编译后
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_createElementVNode("div", null, "我是一个静态节点"),
_createElementVNode("div", null, "我是一个动态节点" + _toDisplayString(_ctx.a + _ctx.b), 1 /* TEXT */)
]))
}
我们先看vue源码中对PatchFlag的定义
再看vue源码中对于createElementVNode
的定义
可以看到带有
{{a + b}}
动态渲染的节点 编译成_createElementVNode
的时候 第四个参数patchFlag为1,这就代表了这个节点在diff对比的时候需要参与对比
hoistStatic(静态提升)
vue2.x中无论元素是否参与更新,每次都会重新创建,然后再渲染。vue3.0中对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可。
前面的编译没打开静态提升, 我们打开静态提升看一看
<div>
<div>我是一个静态节点</div>
<div>我是一个动态节点{{a + b}}</div>
</div>
编译后
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "我是一个静态节点", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_createElementVNode("div", null, "我是一个动态节点" + _toDisplayString(_ctx.a + _ctx.b), 1 /* TEXT */)
]))
}
// Check the console for the AST
可以看到静态节点被提取了出来 放在了render
函数外面 命名为_hoisted_1
每次调用render
函数的时候就不会再去执行_createElementVNode
去创建节点 直接复用就可以了
cacheHandlers (事件侦听器缓存)
默认情况下,如onClick事件会被视为动态绑定,所以每次都会追踪它的变化,但是因为是同一个函数,所以不用追踪变化,直接缓存起来复用即可。
继续上代码
<div @click="handleClick" >我有一个onClick事件</div>
未开启事件侦听器缓存编译后
import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = ["onClick"]
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", { onClick: _ctx.handleClick }, "我有一个onClick事件", 8 /* PROPS */, _hoisted_1))
}
// Check the console for the AST
可以看到编译后的代码_createElementBlock
中patchFlag为8需要追踪对比,并且在_ctx
上下文中获取事件handle
我们打开事件侦听器缓存再编译
import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", {
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.handleClick && _ctx.handleClick(...args)))
}, "我有一个onClick事件"))
}
// Check the console for the AST
可以看到 patchFlag没有定义(patchFlag默认为0) 并且第一次从_ctx
中获取到之后会缓存到_cache
中,
后续从_cache
中获取