为什么要标记静态根节点?
标记静态节点是为了最终得到静态根节点;
vue关于这部分的优化是当一个节点staticRoots为true,并且不在v-for中,那么第一次render的时候会对以这个节点为根的子树(vnode)进行缓存,等到下次再render的时候直接从缓存中拿,避免再次render。所以标记静态根节点是为了缓存一棵子树用的。
编译生成render函数
- 首先将template字符串解析成AST树
- 遍历AST树,标记静态节点
- 标记静态根节点
- 生成render函数
<div id="app">
<div> <span>静态根节点</span></div>
//当前节点不是静态根节点,children只有一个且是一个文本,则不是根节点。
/**
官方说明: 要使节点符合静态根的条件,它应该具有不仅仅是静态文本的子节点。
否则,静态的成本将超过始终保持新鲜的成本。
**/
<div>非静态根节点</div>
<button @click="clickBtn">{{ count }}</button>
</div>
静态节点编译成staticRenderFns函数:
// 判断是否是静态根节点
function markStaticRoots (node, isInFor) {
if (node.type === 1) {
if (node.static || node.once) {
node.staticInFor = isInFor;
}
// For a node to qualify as a static root, it should have children that
// are not just static text. Otherwise the cost of hoisting out will
// outweigh the benefits and it's better off to just always render it fresh.
if (node.static && node.children.length && !(
node.children.length === 1 &&
node.children[0].type === 3
)) {
node.staticRoot = true;
return
} else {
node.staticRoot = false;
}
if (node.children) {
for (var i = 0, l = node.children.length; i < l; i++) {
markStaticRoots(node.children[i], isInFor || !!node.for);
}
}
if (node.ifConditions) {
for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
markStaticRoots(node.ifConditions[i$1].block, isInFor);
}
}
}
}
// 如果是静态根节点则执行genStatic
function genElement (el, state) {
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
}
}
// 静态根节点生成至staticRenderFns函数中
// hoist static sub-trees out
function genStatic (el, state) {
el.staticProcessed = true;
// Some elements (templates) need to behave differently inside of a v-pre
// node. All pre nodes are static roots, so we can use this as a location to
// wrap a state change and reset it upon exiting the pre node.
var originalPreState = state.pre;
if (el.pre) {
state.pre = el.pre;
}
state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}"));
state.pre = originalPreState;
return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")")
}
生成的render函数如下:
function render() {
with(this) {
return _c('div', {
attrs: {
"id": "app"
}
}, [_m(0), _c('div', [_v("非静态根节点")]), _c('button', {
on: {
"click": clickBtn
}
}, [_v(_s(count))])])
}
}
_m = renderStatic()
render函数生成vnode阶段
// 静态根节点只需要生成一次Vnode,生成后会缓存起来,更新的时候直接从缓存中获取
function renderStatic (
index,
isInFor
) {
var cached = this._staticTrees || (this._staticTrees = []);
var tree = cached[index];
// if has already-rendered static tree and not inside v-for,
// we can reuse the same tree.
if (tree && !isInFor) {
return tree
}
// otherwise, render a fresh tree.
tree = cached[index] = this.$options.staticRenderFns[index].call(
this._renderProxy,
null,
this // for render fns generated for functional component templates
);
// 标记该节点为静态节点
markStatic(tree, ("__static__" + index), false);
return tree
}
// this.$options.staticRenderFns[0]
(function anonymous(
) {
with(this){return _c('div',[_c('span',[_v("静态根节点")])])}
})
diff阶段
function patchVnode (
oldVnode,
vnode,
insertedVnodeQueue,
ownerArray,
index,
removeOnly
) {
// 对于静态根节点,此时就直接结束对比了
if (oldVnode === vnode) {
return
}
}