vue 3.0学习
- 一、vue 3.0 diff算法优化
- 1.vue 3.0六大亮点
- 2.vue 3.0 如何变快的?
- 1. diff方法优化 https://vue-next-template-explorer.netlify.app
- 代码示例 vue3.0 编译模板的优化
- 附录: PatchFlags
- 2. hoistStatic静态提升
- 3. cacheHandlers事件侦听器缓存
- 4. ssr渲染
- 代码示例
一、vue 3.0 diff算法优化
1.vue 3.0六大亮点
- Performance:性能比Vue 2. x快1.2 ~2倍
- Tree shaking support: 按需编译,体积比Vue2. x更小
- Composition API: 组合API (类似React Hooks)
- Better TypeScript support: 更好的Ts支持
- Custom Renderer API: 暴露了自定义渲染API
- Fragment, Teleport (Protal),Suspense: 更先进的组件
2.vue 3.0 如何变快的?
1. diff方法优化 https://vue-next-template-explorer.netlify.app
Vue2中的虚拟dom是进行全量的对比
Vue3新增了静态标记(PatchFlag),
在与上次虚拟节点进行对比时候,只对比带有patch flag的节点 并且可以通过flag的信息得知当前节点要对比的具体内容
代码示例 vue3.0 编译模板的优化
<div>Hello World!</div>
<div>我和我的祖国</div>
<div>{{msg}}</div>
<div :id="uid">{{name}}</div>
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode("div", null, "Hello World!"),
_createVNode("div", null, "我和我的祖国"),
_createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("div", { id: _ctx.uid }, _toDisplayString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
], 64 /* STABLE_FRAGMENT */))
}
注意看第三个_createVNode结尾的“1”: Vue 在运行时会生成number(大于 0)值的PatchFlag,用作标记。
仅带有PatchFlag标记的节点会被真正追踪,且无论层级嵌套多深,它的动态节点都直接与Block根节点绑定,无需再去遍历静态节点
第四个 PatchFlag 变成了9 / TEXT, PROPS /, [“id”]
它会告知我们不光有TEXT变化,还有PROPS变化(id) 这样既跳出了virtual
dom性能的瓶颈,又保留了可以手写render的灵活性。 等于是:既有react的灵活性,又有基于模板的性能保证。
附录: PatchFlags
export const enum PatchFlags{
TEXT = 1,//动态文本节点
CLASS=1<<1,//2//动态class
STYLE=1<<2,//4//动态style
PROPS=1<<3,//8//动态属性,但不包含类名和样式
FULLPROPS=1<<4,//16//具有动态key属性,当key改变时,需要进行完整的diff比较。
HYDRATE_EVENTS=1<<5,//32//带有监听事件的节点
STABLE FRAGMENT = 1 << 6, // 64 //一个不会改变子节点顺序的fragment
KEYED FRAGMENT = 1 << 7, // 128 //带有key 属性的fragment 或部分子字节有key
UNKEYED FRAGMENT = 1 << 8, // 256 //子节点没有key 的fragment
NEED PATCH = 1 << 9, // 512 //一个节点只会进行非props比较
DYNAMIC_ SLOTS = 1 << 10, // 1024 //动态slot
HOISTED = -1, //静态节点
//指示在diff过程应该要退出优化模式
BAIL= -2
2. hoistStatic静态提升
Vue2中无论元素是否参与更新,每次都会重新创建
Vue3中对于不参与更新的元素,只会被创建一次,之后会在每次渲染时候被不停的复用
<div>Hello World!</div>
<div>我和我的祖国</div>
<div>{{msg}}</div>
<div :id="uid">{{name}}</div>
静态提升之前:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode("div", null, "Hello World!"),
_createVNode("div", null, "我和我的祖国"),
_createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("div", { id: _ctx.uid }, _toDisplayString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
], 64 /* STABLE_FRAGMENT */))
}
静态提升之后:
const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "Hello World!", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "我和我的祖国", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock(_Fragment, null, [
_hoisted_1, //直接复用
_hoisted_2, //直接复用
_createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("div", { id: _ctx.uid }, _toDisplayString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
], 64 /* STABLE_FRAGMENT */))
}
3. cacheHandlers事件侦听器缓存
默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化
但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可
事件监听缓存
<button @click="inc">按钮</button>
开启事件监听缓存之前:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("button", { onClick: _ctx.inc }, "按钮", 8 /* PROPS */, ["onClick"]))
}
开启事件监听缓存之后:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.inc(...args)))
}, "按钮"))
}
注意点:转换之后的代码,可能还看不懂,但是不要紧
我们只需要观察有没有静态标记即可
因为我们知道在Vue3的diff算法中,只有有静态标记的才会进行比较,才会进行追踪
4. ssr渲染
当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面,
即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。当静态内容大到一定量级时候, 会用_createStaticVNode方法在 客户端去生成个static node.
这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
代码示例
<div>Hello World!</div>
<div>我和我的祖国</div>
<div>{{msg}}</div>
<div :id="uid">{{name}}</div>
开启ssr渲染之前:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode("div", null, "Hello World!"),
_createVNode("div", null, "我和我的祖国"),
_createVNode("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("div", { id: _ctx.uid }, _toDisplayString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
], 64 /* STABLE_FRAGMENT */))
}
开启ssr渲染之后:
export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
const _cssVars = _ssrResolveCssVars({ color: _ctx.color })
_push(`<!--[--><div${
_ssrRenderAttrs(_cssVars)
}>Hello World!</div><div${
_ssrRenderAttrs(_cssVars)
}>我和我的祖国</div><div${
_ssrRenderAttrs(_cssVars)
}>${
_ssrInterpolate(_ctx.msg)
}</div><div${
_ssrRenderAttrs(_mergeProps({ id: _ctx.uid }, _cssVars))
}>${
_ssrInterpolate(_ctx.name)
}</div><!--]-->`)
}