更快、更强、更小
更快
节点标记patchFlag
<div>{{ text }}</div>
<div :class="c"></div>
<div :style="s"></div>
<div :name="n"></div>
<div :class="c" :style="s" :name="n">{{ text }}</div>
编译后的render函数:
const _hoisted_1 = ["name"]
const _hoisted_2 = ["name"]
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_createElementVNode("div", null, _toDisplayString(_ctx.text), 1 /* TEXT */),
_createElementVNode("div", {
class: _normalizeClass(_ctx.c)
}, null, 2 /* CLASS */),
_createElementVNode("div", {
style: _normalizeStyle(_ctx.s)
}, null, 4 /* STYLE */),
_createElementVNode("div", { name: _ctx.n }, null, 8 /* PROPS */, _hoisted_1),
_createElementVNode("div", {
class: _normalizeClass(_ctx.c),
style: _normalizeStyle(_ctx.s),
name: _ctx.n
}, _toDisplayString(_ctx.text), 15 /* TEXT, CLASS, STYLE, PROPS */, _hoisted_2)
], 64 /* STABLE_FRAGMENT */))
}
render函数执行后生成的Vnode: 下面只展示最后一个节点的vnode, vnode对象中有一个patchFlag属性;
{
"__v_isVNode": true,
"__v_skip": true,
"type": "div",
"props": {
"class": "c",
"style": "color:red;",
"name": "n"
},
"key": null,
"ref": null,
"scopeId": null,
"slotScopeIds": null,
"children": "text",
"component": null,
"suspense": null,
"ssContent": null,
"ssFallback": null,
"dirs": null,
"transition": null,
"el": null,
"anchor": null,
"target": null,
"targetAnchor": null,
"staticCount": 0,
"shapeFlag": 9,
"patchFlag": 15,
"dynamicProps": [
"name"
],
"dynamicChildren": null,
"appContext": null
}
最后在diff的时候,patchFlag>0的节点才需要更新,
并且只需要更新patchFlag对应的属性。
故标记patchFlag可以减少diff比较。
hoistStatic 静态提升
如果是静态节点,则将该静态节点作为变量存储
静态提升的元素,只会生成一次vnode,第二次执行render函数生成vnode的时候,直接复用静态提升变量存放的vnode;
故静态提升可以减少render的执行时间。
Vue3:
(function anonymous(
) {
const _Vue = Vue
const { createElementVNode: _createElementVNode } = _Vue
// 定义两个变量
const _hoisted_1 = /*#__PURE__*/_createElementVNode("title", null, "标题", -1 /* HOISTED */)
const _hoisted_2 = ["onClick"]
return function render(_ctx, _cache) {
with (_ctx) {
const { createElementVNode: _createElementVNode, toDisplayString: _toDisplayString, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock(_Fragment, null, [
_hoisted_1,
_createElementVNode("button", { onClick: clickBtn }, _toDisplayString(count), 9 /* TEXT, PROPS */, _hoisted_2)
], 64 /* STABLE_FRAGMENT */))
}
}
})
cacheHandlers 事件监听器缓存
默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化,即会进行diff比较, 但是因为是同一个函数,所以没哟必要去追踪它的变化,想办法将它直接缓存起来复用就会提升性能。
<button @click="clickBtn"></button>
const _hoisted_1 = ["onClick"]
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("button", { onClick: _ctx.clickBtn }, null, 8 /* PROPS */, _hoisted_1))
}
如上代码可以看到,onClick果然会被视为动态绑定, 它有patchFlag=8,为动态props。 开启cacheHandlers后, patchFlag就不存在了,那么这部分内容也就不会进行diff比较了。
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("button", {
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.clickBtn && _ctx.clickBtn(...args)))
}))
}
故事件监听器缓存可以减少diff比较。
使用Proxy代替Object.defineProperty实现响应式
Proxy vs Object.defineProperty
更强
Composition API 组合式编程
产生的原因、解决的问题
- 组件变大,逻辑关注点的列表增长。导致组件难以阅读和理解,尤其对于那些一开始就没有编写这些组件的人来说。
- setup选项为组合API的入口点
- setup选项在创建组件之前执行
- setup执行时尚未创建组件实例
- setup选项中没有this
- setup选项是一个函数,第一个参数是props,第二个是context
- setup中无法访问组件中声明的任何属性——本地状态、计算属性或方法
- setup函数的返回值
其他
-
Vue2使用插件可以实现类似的功能,但不推荐
-
不要为了Composition而composition
-
被定位为高级特性,因为它旨在解决的问题主要出现在大型应用中
-
我们不打算彻底修改文档来把它作为默认方案 Options API
Teleport(传送标签)
可将teleport标签包裹的元素渲染到指定父节点, 比如可将modal渲染到body节点下
<teleport to="body">
<div id="modal">
我是弹层
</div>
</teleport>
Fragments(片段)
可以有多个根节点
createRenderer
更小
Tree Shaking 按需加载