1、静态提升
2、预字符串化
3、缓存事件处理函数
4、Block Tree
5、PatchFlag
一、静态提升
在vue3中的app.vue文件如下:
在服务器中,template中的内容会变异成render渲染函数。
最终编译后的文件:
1.静态节点优化
那么这里为什么是两部分呢?因为这就是vue3中的静态节点优化。(注img元素是纯静态节点);因为静态节点没有绑定数据等,是不会响应式发生变化的那么就不需要放入render函数每次渲染的,所以编译器将静态节点提升到render函数外。
在vue2中会将所有的节点都放入render函数,所以对性能会相对影响。
// vue2 的静态节点
render(){
createVNode("h1", null, "Hello World")
// ...
}
// vue3 的静态节点,不在渲染函数中,这样静态节点只是被创建一次
const hoisted = createVNode("h1", null, "Hello World")
function render(){
// 直接使用 hoisted 即可
}
2.静态属性提升
不仅仅是静态节点,那么对于一些绑定了数据等动态节点,编译器也会智能化监控。
<div class="user">
{{user.name}}
</div>
//可以把属性对象提提取出来
const hoisted = { class: "user" }
function render(){
createVNode("div", hoisted, user.name)
// ...
}
如上代码,该节点虽然绑定了动态数据,但是它的css样式属性是静态的。
所以会将该节点的属性进行提升,从而减少了内存占用。
二、预字符串化
<template>
<div class="menu-bar-container">
<div class="logo">
<h1>logo</h1>
</div>
<ul class="nav">
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
<li><a href="">menu</a></li>
</ul>
<div class="user" :class="user.name">
// 只有这里是动态的,其余都是静态的
<span :class="user.name">{{ user.name }}</span>
</div>
</div>
</template>
如果一个组件内容出现大量连续的形态节点(20个)时,如果用render函数去一个个渲染,将损耗大量性能,但是vue3中,会将这些静态内容字符串化,渲染时直接返回静态的节点。
编译结果:
vue2中会有大量的虚拟节点
vue3中直接变成一个字符串了。SSR中作用非常明显。
三、缓存事件处理函数
<button @click="count++">plus</button>
// vue2 中onClick每次都是函数
render(ctx){
return createVNode("button", {
onClick: function($event){
ctx.count++;
}
})
}
// vue3 中会缓存函数,有的话直接返回,没有的进行缓存
render(ctx, _cache){
return createVNode("button", {
onClick: cache[0] || (cache[0] = ($event) => (ctx.count++))
})
}
如上代码,当我们绑定事件时,vue2会在每次触发函数时去调取函数,而vue3中会将函数进行缓存,触发时查看是否有缓存,没有就进行缓存,后续触发时直接拿取缓存中的函数,从而减少性能损耗。
四、Block Tree
Vue.js 2.x 的数据更新并触发重新渲染的粒度是组件级的,单个组件内部 需要遍历该组件的整个 vnode 树。在 2.0 里,渲染效率的快慢与组件大小成正相关:组件越大,渲染效率越慢。并且,对于一些静态节点,又无数据更新,这些遍历都是性能浪费。
Vue.js 3.0 做到了通过编译阶段对静态模板的分析,编译生成了 Block tree。 **Block tree 是一个将模版基于动态节点指令切割的嵌套区块,每个 区块内部的节点结构是固定的,每个区块只需要追踪自身包含的动态节点。**所以,在 3.0 里,渲染效率不再与模板大小成正相关,而是与模板中动态节点的数量成正相关。
<form>
<div>
<label>账号:</label>
<input v-model="user.loginId" />
</div>
<div>
<label>密码:</label>
<input v-model="user.loginPwd" />
</div>
</form>
如上代码,当一代码的结构基本稳定,但是部分结构会变化时:
注:vue2会挨个挨个比较前后变化的节点数,当发现不一样时重新渲染。
而vue3中依托强大的编译器,会在首节点的‘block’节点中记录当前树的哪部分是动态的,然后响应时直接寻找动态的节点进行比对,跳过一些节点,不用每个节点都对比,从而大大提升了渲染效率。
左block:[inopu1,input2]
右block:[inopu1,input2]
五、PatchFlag(比对标记)
前面说的是跳过一些静态节点,PatchFlag说的是对比一个具体的节点的优化方式,这个节点哪块是动态的
<div class="user" :class="user.name">
<span :class="user.name">{{ user.name }}</span>
</div>
有如上代码,我们会发现这一段代码中帮顶了三个动态数据,那么vue3的编译器就会在render函数中进行标记。
vue2中需要对比元素类型、属性、再递归子节点,vue3中只需要根据表示对比元素内容。