Vue2中的虚拟dom是进行全量的对比 Vue3新增了静态标记(PatchFlag), 在与上次虚拟节点进行对比时候,只对比带有patch flag的节点 并且可以通过flag的信息得知当前节点要对比的具体内容
vue 3.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的灵活性,又有基于模板的性能保证。
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
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 */)) }默认情况下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算法中,只有有静态标记的才会进行比较,才会进行追踪
当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面, 即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。
当静态内容大到一定量级时候, 会用_createStaticVNode方法在 客户端去生成个static node. 这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。
开启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><!--]-->`) }