Vue3 效率提升
# Vue3 效率提升
Vue3 的效率提升主要变现在哪些方面?
# 静态提升
下面的静态节点会被提升
- 元素节点
- 没有绑定动态内容
以前 Vue2 的时候我们的template会存在一个预编译过程 就是将 template 里面的内容通过render函数 构建成虚拟 dom
// Vue2
render(){
createVNode('h1',null,'Hello My friend!')
}
现在 Vue3呢 认为你这个啥也不是 就是个普普通通的 元素节点 默认他不会改变 就会把这个节点提取出来。
const hoisted = createVNode('h1',null,'Hello My friend!')
function render(){
// ... 直接使用 hoisted
}
这样能保证我们的节点只会创建一个,而不是随着部分内容改变重新创建。
如下:
我们创建一个静态组件(内部全是静态的dom)观察 vite 编译出的资源
<template>
<div>
<ul>
<li>hello</li>
<li>beta</li>
<li>gama</li>
<li>stupid</li>
</ul>
</div>
</template>
<script>
export default {
}
</script>

静态属性会被提升
比如说一个节点虽然他的内容是动态的,但是他的属性是静态的,这个属性会被提升。
将代码做部分修改:
<template>
<div>
<ul>
<li class="sofeia">{{ user.name }}</li>
<li>beta</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: "zhang san" },
};
},
};
</script>
观察编译后的资源

# 预字符串化
什么是预字符串化呢
就是 当编译器遇到大量连续的静态内容,会直接将其便以为一个普通字符串节点
// 模板
<template>
<div>
<ul>
<li class="sofeia">{{ user.name }}</li>
<li>beta</li>
</ul>
<ul>
<li>hello</li>
<li>hello</li>
<li>hello</li>
</ul>
<ul>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
<li>kalasica</li>
</ul>
<ul>
<li>music</li>
<li>music</li>
<li>music</li>
<li>music</li>
<li>music</li>
</ul>
<div>{{ user.name }}</div>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: "zhang san" },
};
},
};
</script>
观察编译后的内容:

我们看到编译后的代码将连续的静态节点变成了一个字符串
const _hoisted_3 = /*#__PURE__*/
_createStaticVNode("<ul><li>hello</li><li>hello</li><li>hello</li></ul><ul><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li><li>kalasica</li></ul><ul><li>music</li><li>music</li><li>music</li><li>music</li><li>music</li></ul>", 3)
限制情况:只有连续的节点且大于二十个
Vue2 的时候 遇到这样的节点树就是会 render 函数生成虚拟dom···直到构建完整棵树。
这样只需要将关注点放在动态节点,而无需关心静态节点问题。
# 缓存事件处理函数
Vue3 同理也认为你绑定的事件是不会改变的
Vue3 中有很多个参数 大多数的参数都是 catch对象 用来缓存我们的数据
<template>
<button @click="handleClick">click</button>
</template>
<script>
export default {
methods: {
handleClick() {},
},
};
</script>
我们来看下编译后的内容:
_createElementBlock("button",
{
onClick: _cache[0] ||
(_cache[0] = (...args) =>($options.handleClick && $options.handleClick(...args)))
},
"click")
可以看到在会访问缓存对象,如果缓存中没有 则会保存到缓存中。
# Block Tree
Vue2 在比对新旧树的时候,并不知道哪些节点是静态的,哪些是动态的,因此只能一层一层比较,这就浪费了大部分事件在比对静态节点上。
<template>
<form>
<div>
<label for="">账号:</label>
<input type="text" v-model="user.name" />
</div>
<div>
<label for="">密码:</label>
<input type="text" v-model="user.pawd" />
</div>
</form>
</template>
<script>
export default {
data() {
return {
user: {
name: "xxxx",
pawd: "xxx",
},
};
},
};
</script>
在 Vue2 的时候 比对两棵树 会使用 petch 算法 但是 我们来看上面的模板,像 form、div 、label 这些静态节点又不会改变为什么还要比对呢?
但是 Vue3 中就不是这样子啦
Vue3 会在创建VDom 的时候识别动态节点然后将动态节点的信息保存到 根节点中,当我们页面发生改变后,直接找到根节点中存储的动态节点然后循环遍历直接比对动态节点,越过静态节点。
动静比:静态节点越多动静比越大
这样提升的效率很高。
# Patch Flag
Patch Flag 的作用就是标记我们修改的类型
还是上面的模板
我们看他的编译内容:
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return (
_openBlock(),
_createElementBlock("form", null, [
_createElementVNode("div", null, [
_hoisted_1,
_withDirectives(
_createElementVNode(
"input",
{
type: "text",
"onUpdate:modelValue":
_cache[0] ||
(_cache[0] = ($event) => ($data.user.name = $event)),
},
null,
512 /* NEED_PATCH */
),
[[_vModelText, $data.user.name]]
),
]),
_createElementVNode("div", null, [
_hoisted_2,
_withDirectives(
_createElementVNode(
"input",
{
type: "text",
"onUpdate:modelValue":
_cache[1] ||
(_cache[1] = ($event) => ($data.user.pawd = $event)),
},
null,
512 /* NEED_PATCH */
),
[[_vModelText, $data.user.pawd]]
),
]),
])
);
}
我们看这个地方:
_createElementVNode(
"input",
{
type: "text",
"onUpdate:modelValue":
_cache[1] ||
(_cache[1] = ($event) => ($data.user.pawd = $event)),
},
null,
512 /* NEED_PATCH */
),
在创建Vdom的时候发现后面跟了一个数字 512 这个数字就是一个标记值,表示我们节点改变的类型
再比如:
我们修改了模板
<template>
<form>
<div>
<label for="">账号:</label>
<!-- <input type="text" v-model="user.name" /> -->
<span>{{ user.name }}</span>
</div>
<div>
<label for="">密码:</label>
<!-- <input type="text" v-model="user.pawd" /> -->
<span>{{ user.pawd }}</span>
</div>
</form>
</template>
<script>
export default {
data() {
return {
user: {
name: "xxxx",
pawd: "xxx",
},
};
},
};
</script>
发现后面的数字变成了 1
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(),
_createElementBlock("form", null, [_createElementVNode("div", null, [_hoisted_1, _createCommentVNode(" <input type=\"text\" v-model=\"user.name\" /> "), _createElementVNode("span", null, _toDisplayString($data.user.name), 1 /* TEXT */
)]), _createElementVNode("div", null, [_hoisted_2, _createCommentVNode(" <input type=\"text\" v-model=\"user.pawd\" /> "), _createElementVNode("span", null, _toDisplayString($data.user.pawd), 1 /* TEXT */
)])]))
}
后面的注释解释 是 TEXT 意思是 文本类型。