对于这个问题,针对目前应用范围更广的 vue 2.6.x 来做回答。
要弄清楚优先级问题,自然要从源码中来找答案,而这个答案在:compiler/codegen/index.js: genElement
解析
这里我们直接来看看源码:
export function genElement(el: ASTElement, state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
// component or element
let code
if (el.component) {
code = genComponent(el.component, el, state)
} else {
let data
if (!el.plain || (el.pre && state.maybeComponent(el))) {
data = genData(el, state)
}
const children = el.inlineTemplate ? null : genChildren(el, state, true)
code = `_c('${el.tag}'${
data ? `,${data}` : '' // data
}${
children ? `,${children}` : '' // children
})`
}
// module transforms
for (let i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code)
}
return code
}
}
从上面的代码可以看到,这里是先处理 for 然后处理 if,那么,显然 for 的优先级是高于 if 的。
扩展
对于这个问题,可以稍微扩展一下,引申出下面两个问题:
- 如果
v-if
和v-for
同时出现在一个标签中,会不会出现什么问题? - 如果出现问题,应该去怎么优化?
- 在优化的时候,怎么让性能更好?
对于这些问题,通过一个 demo 就可以很好的说明:
<div id="demo">
<h1>v-if 和 v-for</h1>
<p v-for="a in arr" v-if="a.isShow" :key="a.txt">
{{ a.txt }}
</p>
</div>
<script>
const app = new Vue({
el: "#demo",
data: {
isShow: false,
arr: [
{
txt: 1,
isShow: false
},
{
txt: 2,
isShow: true
},
{
txt: 3,
isShow: false
}
]
}
});
</script>
这里,预期是页面上只输出 2,来看看结果:
可以看到,1 和 3 都是先生成,然后被关闭的。
从结果上看,似乎没什么太大的问题,输出和预期是相符的,但实际上这样的操作实际上是非常消耗性能的。
为什么这么说呢,我们将 render
函数打印出来看看:
;(function anonymous() {
with (this) {
return _c(
'div',
{ attrs: { id: 'demo' } },
[
_c('h1', [_v('v-if 和 v-for')]),
_v(' '),
_l(arr, function (a) {
return a.isShow ? _c('p', { key: a.txt }, [_v('\n ' + _s(a.txt) + '\n ')]) : _e()
}),
],
2
)
}
})
可以看到,这里先渲染列表,然后在循环中通过判断 isShow 来是否渲染。什么意思呢,我们来看看上面代码的 dom 结构:
可以看到,1 和 3 实际上是被渲染出来,然后再被关掉的。(这不是多此一举嘛(=。=))
那么应该怎么优化呢?
这里可以利用 vue 的计算属性将 arr 过滤一遍,将需要渲染的值过滤出来即可:
computed: {
showArr() {
return this.arr.filter(a => a.isShow);
}
}
结果如下:
dom 结构如下:
显然,这样处理之后性能肯定是高于上面的方案了。
结语
更佳阅读体验:005 - v-for 和 v-if 谁的优先级更高