if 优先处理
const numList = [1, 2, 3, 4];
const isShow = false;
//节点
const nodes = [];
//显示判断
if(isShow ){
for(let i in numList){
const val = numList[i];
nodes.push(val );
}
}
//最后输出 []
console.log(nodes);
for 优先处理
const numList = [1, 2, 3, 4];
const isShow = false;
//节点
const nodes = [];
//先运行
for(let i in numList){
const val = numList[i];
//判断处理
if(isShow){
nodes.push(val);
}
}
//最后输出 []
console.log(nodes);
从上面的简单代码看到 if 和 for 是不可能同时运行的,总有个先后顺序,不管是 if 还是 for 先运行结果是一样的,但你会发现当 for 先运行时会有多余的计算浪费性能。
Vue 编译 v-if 和 v-for 同时出现时处理方法也是跟上面的代码一样,总有一个先后顺序。Vu2 使用 v-for 优先,Vue3 发现问题了更改为 v-if 优先。下面通过 Vue 将模板 template 编译成 vnode 时生成的代码来看看不同版本是怎么处理的,看了源代码你才会更加踏实,不是听别人说,而是听你说。
//例子1:v-if 为独立条件,非 v-for 的条件
//组件数据,类样式组件
export default class AboutView extends Vue {
public numList = [1, 2, 3, 4];
public isShow = false;
}
//template
{{item}}
// Vue 编译模板处理
//不管3721,先运行 v-for
_vm._l(_vm.numList, function (item) {
//再判断 v-if
return _vm.isShow
? _c("div", { key: item }, [_vm._v(" " + _vm._s(item) + " ")])
: _vm._e()
})
从上面例子可以看到不管 v-if 是什么值都先运行 v-for 再作判断,明显看出性能会出现问题,_vm._e()会根据数据的长度产生对应长度的空vnode。
//例子2:v-if 为 v-for 的条件
//组件数据
export default class AboutView extends Vue {
public numList = [1, 2, 3, 4];
public isShow = true;
}
//template
{{item}}
//编译处理
// Vue 编译模板处理
//不管3721,先运行 v-for
_vm._l(_vm.numList, function (item) {
//判断值是否==2
return item == 2
? _c("div", { key: item }, [_vm._v(" " + _vm._s(item) + " ")])
: _vm._e()
})
从上面例子2可以看到 v-for 是必须的,再作判断是否显示,看似没有多余的动作,但这样做会造成逻辑复杂,性能上也是有问题的。首先_vm._e() 会产生空 vnode ,如果整个组件的其中一项数据发生变化,上面的渲染函数会重新运行,会计算数组 numList 不符合条件的项产生性能浪费。如果numList 已经计算好符合条件的数据,则 render 重新渲染不会产生多余的计算浪费性能,如下图。
小结:
综合上面两种情况当 v-if v-for 同时出现会出现性能问题,所以我们要避免同时使用两个指令。
处理方法有两种,一、在数据源处理掉 if 条件。二、两个指令分开写。
//例子1:v-if 为独立条件,非 v-for 的条件
//组件数据,ts setup
const numList = [1, 2, 3, 4];
const isShow = false;
//template
{{ item }}
//Vue编译模板
// Vue 编译模板处理
//不管3721,先运行 v-if
$setup.isShow ? (_openBlock(), _createElementBlock(
_Fragment,
{ key: 0 },
//再运行 v-for
_renderList($setup.numList, (item) => {
return _createElementVNode(
"div",
{ key: item },
_toDisplayString(item),
1
/* TEXT */
);
}),
64
/* STABLE_FRAGMENT */
)) : _createCommentVNode("v-if", true)
从 Vue3 编译代码可以看到,没有多余的计算,对性能没有影响。
//例子2:v-if 为 v-for 的条件
//数据
const numList = [1, 2, 3, 4];
const isShow = false;
//template
{{ item }}
上面这种情况可以运行,但会报不存在属性“item”提示,运行结果是什么东西都不会显示,看下面编译代码。
// Vue 编译模板处理
// 判断 _ctx.item==2,item 未定义,所以 v-for 不会运行,程序逻辑达不到你想要的效果
_ctx.item == 2 ? (_openBlock(), _createElementBlock(
_Fragment,
{ key: 0 },
// 这里进不来
_renderList($setup.numList, (item) => {
return _createElementVNode(
"div",
{ key: item },
_toDisplayString(item),
1
/* TEXT */
);
}),
64
/* STABLE_FRAGMENT */
)) : _createCommentVNode("v-if", true)
从上面代码可以看到,item 是上下文的变量,不是 for 里面的 item,所以变量是没有定义的,最后是没有结果显示。
小结:
当 v-if 为 v-for 的条件时,程序逻辑达不到你想要的效果,你就会想办法解决了。
处理方法还是那两种,一、在数据源处理掉if条件。二、两个指令分开写。
总结:
为了避免 v-if v-for 产生问题最标准的处理方法是两个指令分开写,先 v-if 还是先 v-for,这样逻辑就清晰好多。如果 v-if 是 v-for 的逻辑最好在数据源头处理掉,保持模板的逻辑干净易读,当数据产生变化时渲染函数不用再处理判断,提高性能。
//#1 两个指令分开写,template处理
//#2 先进行数据处理,你也可以使用其它方法,计算属性之类的。
const numList = [1, 2, 3, 4].filter(val => val == 2);
//#2 template
如果想看上面的的编译源代码,可以打开谷歌的按下F12,搜索上面的代码关键字numList查找,再导航到页面源代码,带?t=169xxxxx的为编译源代码,从下图可以看到render这个关键字,这样可以帮助你快速分析temaplate最后被编译成什么代码,从中学习好多表面看不到的指令实现知识,也可以帮助你以后用render函数自己实现teamplate编译渲染。
问题来源,2023.09.01去樟木头张力科技公司面试时被问到的一个问题 v-if v-for 哪个最优先?我回答Vue3哪个优先不是很肯定 ,只是用自己的思路说肯定是 v-if 优先了,这样符合代码逻辑和性能,果不奇然被我懵对了。