vue 中的 v-if 和 v-for 
vue 中有一个经典的面试题:v-if 和 v-for 能够一起使用吗?
简单的回答是:
- vue2 中不可以
- vue3 中可以
再深入回答一点可能会提到两个命令优先级的问题,但不知道为什么是这样的。这篇文章从原理来理解这个问题。
重新认识两者优先级 
我们分别使用官方提供的 Vue Template Explorer 和 Vue SFC Playground 来查看 vue2 和 vue3 中对 template 编译后的代码。
示例代码为:
<div>
  <span v-for="item of 9" :key="item" v-if="item % 2 === 0">{{ item }}</span>
</div>2
3
vue2 
vue2 中编译上面代码后的结果为:
function render() {
  with (this) {
    return _c(
      "div",
      _l(9, function (item) {
        return (item % 2 === 0)
          ? _c(
              "span",
              {
                key: item,
              },
              [_v(_s(item))]
            )
          : _e();
      }),
      0
    );
  }
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
模板代码被编译为了 render 函数,其中的核心部分已经高亮显示(_l 函数部分)。
可以看到先用 _l 函数进行循环,循环产生的内容中再判断 v-if 的条件。如果为真创建 DOM 元素(_c);如果为假创建注释元素(_e)。所以 vue2 中 v-for 的优先级高于 v-if。
另外上面的 v-for 代码最终只会渲染 4 个元素,但执行期间仍然完整的循环了 9 次,造成了性能浪费。这也是为什么 vue2 中 v-if 和 v-for 不推荐一起使用的原因。
vue3 
vue3 中编译上面代码后的结果为:
const __sfc__ = {};
import {
  renderList as _renderList,
  Fragment as _Fragment,
  openBlock as _openBlock,
  createElementBlock as _createElementBlock,
  toDisplayString as _toDisplayString,
  createElementVNode as _createElementVNode,
  createCommentVNode as _createCommentVNode,
} from "vue";
function render(_ctx, _cache) {
  return (
    _openBlock(),
    _createElementBlock("div", null, [
      _ctx.item % 2 === 0
        ? (_openBlock(),
          _createElementBlock(
            _Fragment,
            { key: 0 },
            _renderList(9, (item) => {
              return _createElementVNode(
                "span",
                { key: item },
                _toDisplayString(item),
                1 /* TEXT */
              );
            }),
            64 /* STABLE_FRAGMENT */
          ))
        : _createCommentVNode("v-if", true),
    ])
  );
}
__sfc__.render = render;
__sfc__.__file = "src/App.vue";
export default __sfc__;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
模板代码同样被编译为了 render 函数,其中的核心部分已经高亮显示(_createElementBlock 函数第三个参数部分)。
可以看到 vue3 中先判断了 v-if 中的条件,为真时循环创建 DOM 元素,为假时创建注释节点。所以 vue3 中 v-if 的优先级高于 v-for 的优先级,解决了 vue2 中性能浪费的问题。
也正是因为 vue3 中优先处理了 v-if,所以导致了 v-if 处条件报错(从编辑器可以看到,报 item 不存在错误)。因为先处理 v-if 后模板相当于变成了:
<template v-if="item % 2 === 0">
  <span v-for="item of 9" :key="item">{{ item }}</span>
</template>2
3
此时 item 当然是不存在的。
总结 
vue2 中 v-for 的优先级高于 v-if,会造成性能浪费,所以不推荐一起使用(如果开启了相关 lint 规则会报错,未开启的话实际上可以使用)。
vue3 中 v-if 的优先级高于 v-for,解决了性能浪费的问题(所以没有了相关 lint 规则,始终能够一起使用)。但是 v-if 不能使用 v-for 中的条件变量。
推荐始终避免同时使用 v-if 和 v-for,可以用 computed 属性过滤后再循环渲染,或者将 v-for 提升到外层标签来解决需求。