Proxy响应式
PatchFlag
- 编译模板时,动态节点做标记
- 标记,分为不同的类型,如TEXT PROPS
- diff算法时,可以区分静态节点,以及不同类型的动态节点
<div>Hello World</div>
<span>{{ msg }}</span>
<span class="msg">闻人放歌</span>
<span id="name">{{ msg }}</span>
<span class="msg-class" :msg="message">{{ msg }}</span>
编译结果:
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_createElementVNode("div", null, "Hello World"),
_createElementVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createElementVNode("span", { class: "msg" }, "闻人放歌"),
_createElementVNode("span", { id: "name" }, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createElementVNode("span", {
class: "msg-class",
msg: _ctx.message
}, _toDisplayString(_ctx.msg), 9 /* TEXT, PROPS */, ["msg"])
], 64 /* STABLE_FRAGMENT */))
}
// Check the console for the AST
静态字段将不再比较,这样diff算法将少比较很多节点。
hoistStatic
- 将静态节点的定义,提升到父作用域,缓存起来
- 多个相邻的静态节点,会被合并起来
- 典型的拿空间换时间的优化策略
// 模板
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
编译结果:
import { createElementVNode as _createElementVNode, createCommentVNode as _createCommentVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, "Hello World", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createElementVNode("div", null, "Hello World", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createElementVNode("div", null, "Hello World", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_hoisted_1,
_hoisted_2,
_hoisted_3,
_createCommentVNode(" <span>{{ msg }}</span>\n<span class=\"msg\">闻人放歌</span>\n<span id=\"name\">{{ msg }}</span>\n<span class=\"msg-class\" :msg=\"message\">{{ msg }}</span> ")
], 64 /* STABLE_FRAGMENT */))
}
// Check the console for the AST
还有一个点,如果静态节点数量多,编译会自动合并,如:
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
import { createElementVNode as _createElementVNode, createCommentVNode as _createCommentVNode, createStaticVNode as _createStaticVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div><div>Hello World</div>", 10)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
_hoisted_1,
_createCommentVNode(" <span>{{ msg }}</span>\n<span class=\"msg\">闻人放歌</span>\n<span id=\"name\">{{ msg }}</span>\n<span class=\"msg-class\" :msg=\"message\">{{ msg }}</span> ")
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
}
// Check the console for the AST
cacheHandler
上代码:
<div @click="clickHandler">Hello World</div>
编译结果:
import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", {
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.clickHandler && _ctx.clickHandler(...args)))
}, "Hello World"))
}
// Check the console for the AST
从编译结果代码可以看出,如果该事件方法已被定义则直接去缓存中的方法,没有则定义。其意义就是缓存事件
SSR优化
编译前:
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>Hello World</div>
<div>{{ msg }}</div>
<div @click="clickHandler">Hello World</div>
<div class="msg-class">{{ msg }}</div>
<div class="msg-class" :msg="msg">闻人放歌</div>
编译结果:
import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "vue/server-renderer"
export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
const _cssVars = { style: { color: _ctx.color }}
_push(`<!--[--><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_cssVars)
}>${
_ssrInterpolate(_ctx.msg)
}</div><div${
_ssrRenderAttrs(_cssVars)
}>Hello World</div><div${
_ssrRenderAttrs(_mergeProps({ class: "msg-class" }, _cssVars))
}>${
_ssrInterpolate(_ctx.msg)
}</div><div${
_ssrRenderAttrs(_mergeProps({
class: "msg-class",
msg: _ctx.msg
}, _cssVars))
}>闻人放歌</div><!--]-->`)
}
// Check the console for the AST
- 静态节点直接输出,绕过了vdom
- 动态节点,还是需要动态渲染
tree-shaking
观察两者编译结果区别:
<div v-if="msg">Hello World</div>
<input v-model="msg">
// 编译结果
import { openBlock as _openBlock, createElementBlock as _createElementBlock, createCommentVNode as _createCommentVNode, vModelText as _vModelText, createElementVNode as _createElementVNode, withDirectives as _withDirectives, Fragment as _Fragment } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
(_ctx.msg)
? (_openBlock(), _createElementBlock("div", { key: 0 }, "Hello World"))
: _createCommentVNode("v-if", true),
_withDirectives(_createElementVNode("input", {
"onUpdate:modelValue": $event => ((_ctx.msg) = $event)
}, null, 8 /* PROPS */, ["onUpdate:modelValue"]), [
[_vModelText, _ctx.msg]
])
], 64 /* STABLE_FRAGMENT */))
}
// Check the console for the AST
<div v-if="msg">Hello World</div>
// 编译结果
import { openBlock as _openBlock, createElementBlock as _createElementBlock, createCommentVNode as _createCommentVNode } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_ctx.msg)
? (_openBlock(), _createElementBlock("div", { key: 0 }, "Hello World"))
: _createCommentVNode("v-if", true)
}
// Check the console for the AST
编译时,根据不同的情况,引入不同的API