前言
这篇文章主要对vue中 v-if 与 v-for 同时出现时,如何进行性能优化做一个探讨
思考ing
- v-if 与 v-for 同级时(同时出现)谁的优先级比较高
- v-if 与 v-for 同级时(同时出现),怎么优化才能得到更好的性能
优先级
从代码中找答案
vue代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>v-if 与 v-for 谁的优先级高</title>
</head>
<body>
<div id="app">
<p v-for="item in userList" v-if="isLogin">{{item.name}}</p>
</div>
</body>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isLogin: true,
userList: [
{ id: 1, name: "赵云" },
{ id: 2, name: "张飞" },
],
},
});
console.log(app.$options.render);
</script>
</html>
调用
$options.render
生成代码如下:
(function anonymous() {
with (this) {
return _c(
"div",
{ attrs: { id: "app" } },
_l(userList, function (item) {
return isLogin ? _c("p", [_v(_s(item.name))]) : _e();
}),
0
);
}
});
_l()
可以在vue源码中看到是renderList()
,用于渲染列表。- 通过上面的代码可以看出,会先进行列表的循环,在循环里面进行if的逻辑判断.
- 这里永远都是先执行循环,再进行条件判断决定是否渲染.不管条件成不成立,循环是不可避免的.
从源码中找答案
在 src\compiler\codegen
或 vue.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
}
}
- 从
if else
里面是可以看出,v-for
优先于v-if
解析,也就是说 如果同时存在,v-for
永远都在外面v-if
永远都在里面
实践结论
- v-for 优先于 v-if 被解析
- 如果两者同时出现, 优先渲染列表再判断条件,无论如何判断,循环都不可避免,浪费了性能.
性能优化方案
避免上面出现的情况, 推荐的解决方案:
在外层嵌套template,在这一层进行v-if判断,然后在内部进行列表渲染.
我们来看看这么做的效果怎样:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>v-if 与 v-for 谁的优先级高</title>
</head>
<body>
<div id="app">
<template v-if="isLogin">
<p v-for="item in userList">{{item.name}}</p>
</template>
</div>
</body>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isLogin: false,
userList: [
{ id: 1, name: "" },
{ id: 2, name: "" },
],
},
});
console.log(app.$options.render);
</script>
</html>
生成代码如下:
anonymous() {
with(this){
return
_c(
'div',
{
attrs:{"id":"app"}
},
[
(isLogin)?_l((userList),function(item){return _c('p',[_v(_s(item.name))])}):_e()],2)}
}
上面代码中,会先进行条件判断,如果条件成立,才会进行列表渲染。 如果条件不成立,则不会进行列表渲染 , 通过这种方式,减少了不必要的列表渲染,大大提升了性能