Skip to content

✨ 条件和列表渲染 👌

条件渲染:v-if / v-else-if / v-else

使用 v-ifv-else-ifv-else 进行条件渲染:

vue
<template>
  <div v-if="type === 1">晴天</div>
  <div v-else-if="type === 2">阴天</div>
  <div v-else-if="type === 3">雨天</div>
  <div v-else-if="type === 4">下雪</div>
  <div v-else>未知天气</div>
</template>

<script setup>
import { ref } from "vue";
const type = ref(1);
setInterval(() => {
  type.value = Math.floor(Math.random() * 5 + 1);
}, 3000);
</script>

特性说明

  • v-if 是“真实的”按条件渲染,切换时会销毁/重建条件区块内的事件与子组件。
  • v-if 是惰性的:初次为 false 时不渲染,首次变为 true 才渲染。

条件渲染:v-show 与选择建议

v-show 通过切换 CSS display 实现显示/隐藏:

vue
<template>
  <div v-if="isShow">使用 v-if 进行条件渲染</div>
  <div v-show="isShow">使用 v-show 进行条件渲染</div>
</template>

<script setup>
import { ref } from "vue";
const isShow = ref(true);
setTimeout(() => {
  isShow.value = false;
}, 2000);
</script>

v-if vs v-show 选择

  • 频繁切换用 v-show(初始渲染成本高、切换成本低)。
  • 条件很少变化用 v-if(初始渲染成本低、切换成本高)。
  • 需要在 DOM 中完全移除节点用 v-if;仅隐藏展示用 v-show

多元素条件切换:template 包装

<template> 标签不会被渲染,可用于“成组切换多个元素”:

vue
<template>
  <template v-if="type === 1">
    <div>晴天</div>
    <p>出去旅游</p>
    <p>玩的开心</p>
  </template>
  <template v-else-if="type === 2">
    <div>阴天</div>
    <p>在家看书</p>
  </template>
  <template v-else-if="type === 3">
    <div>雨天</div>
    <p>适合睡觉</p>
  </template>
  <div v-else>未知天气</div>
</template>

<script setup>
import { ref } from "vue";
const type = ref(1);
setInterval(() => {
  type.value = Math.floor(Math.random() * 5 + 1);
}, 3000);
</script>

使用场景

当不同条件需要切换多个兄弟元素时,使用 <template> 作为占位容器更干净、易读。

列表渲染:v-for 基础与 key

基本用法:item in items,并为每项提供稳定唯一的 key

vue
<template>
  <div>
    <h2>商品列表</h2>
    <ul>
      <li v-for="product in products" :key="product.id">
        {{ product.name }} - {{ product.price }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from "vue";
const products = ref([
  { id: 1, name: "键盘", price: 99.99 },
  { id: 2, name: "鼠标", price: 59.99 },
  { id: 3, name: "显示器", price: 299.99 },
]);
</script>

key 使用建议

  • key 应使用稳定唯一的标识符(如数据的 id)。
  • 避免使用数组索引作为 key(会影响复用与过渡、可能导致状态错位)。
  • key 值为基础类型(string/number),不要传对象。

循环多个元素时,可用 <template v-for>

vue
<template>
  <div>
    <h2>商品列表</h2>
    <template v-for="product in products" :key="product.id">
      <div>{{ product.name }}</div>
      <div>{{ product.price }}</div>
      <hr />
    </template>
  </div>
</template>

细节:对象遍历 / 整数遍历 / 作用域

对象遍历时的三个参数:值、键、索引:

vue
<template>
  <div v-for="(value, key, index) in stu" :key="key">
    {{ index }} - {{ key }} - {{ value }}
  </div>
</template>

<script setup>
import { reactive } from "vue";
const stu = reactive({ name: "zhangsan", age: 18, gender: "male", score: 100 });
</script>

遍历对象的顺序

对象键的遍历顺序与键类型相关(整数键可能按数值排序);不要依赖对象遍历的顺序来呈现有序列表,建议使用数组。

整数遍历:

vue
<template>
  <div v-for="n in 10" :key="n">{{ n }}</div>
</template>

作用域与嵌套:可在内层访问外层变量(示例为项目/任务/子任务嵌套):

vue
<template>
  <ul>
    <li v-for="project in projects" :key="project.id">
      <h2>{{ project.name }}</h2>
      <ul>
        <li v-for="task in project.tasks" :key="task.id">
          <h3>{{ task.name }}</h3>
          <ul>
            <li v-for="(subtask, index) in task.subtasks" :key="index">
              {{ project.name }} - {{ task.name }} - {{ subtask }}
            </li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</template>

<script setup>
import { ref } from "vue";
const projects = ref([
  {
    id: 1,
    name: "Project A",
    tasks: [
      { id: 1, name: "Task A1", subtasks: ["Subtask A1.1", "Subtask A1.2"] },
      { id: 2, name: "Task A2", subtasks: ["Subtask A2.1", "Subtask A2.2"] },
    ],
  },
  {
    id: 2,
    name: "Project B",
    tasks: [
      { id: 1, name: "Task B1", subtasks: ["Subtask B1.1", "Subtask B1.2"] },
      { id: 2, name: "Task B2", subtasks: ["Subtask B2.1", "Subtask B2.2"] },
    ],
  },
]);
</script>

避免在同一元素同时使用 v-if 与 v-for

在同一元素上同时使用 v-ifv-for 会造成优先级与可读性问题,建议改为「先过滤、再渲染」或使用外层 <template> 包装:

vue
<!-- 不推荐:同一元素同时使用 v-for 与 v-if -->
<li v-for="todo in todos" v-if="!todo.isComplete">{{ todo.name }}</li>

推荐做法一:使用计算属性预先过滤:

vue
<template>
  <li v-for="todo in visibleTodos" :key="todo.id">{{ todo.name }}</li>
</template>

<script setup>
import { computed } from "vue";
const todos = [
  { id: 1, name: "阅读文档", isComplete: false },
  { id: 2, name: "编写示例", isComplete: true },
];
const visibleTodos = computed(() => todos.filter((t) => !t.isComplete));
</script>

推荐做法二:用 <template v-for> 包装并在子节点使用 v-if

vue
<template v-for="todo in todos">
  <li v-if="!todo.isComplete" :key="todo.id">{{ todo.name }}</li>
</template>

官方建议

不要在同一元素上同时使用 v-ifv-for。优先通过计算属性过滤数据或使用 <template> 包装,提升可读性与可维护性。

数组的侦测:变更与非变更方法

数组方法分为两类:

  • 变更方法(会修改原数组):pushpopshiftunshiftsplicesortreverse
  • 非变更方法(返回新数组,不改原数组):filterconcatslicemap

变更方法会触发响应式更新;非变更方法需要用返回值替换原值:

js
// 变更方法:直接触发响应式
setTimeout(() => {
  projects.value.push({ id: 3, name: "新项目", tasks: [] });
}, 1000);

// 非变更方法:用返回值替换原值
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/));

易错点与规避

  • 使用非变更方法时忘记替换原数组,会导致视图不更新。
  • 大量、频繁的数组变更可能造成渲染压力;可按需批量更新或使用虚拟列表优化。

小结

  • 条件渲染:频繁切换用 v-show,其他场景用 v-if;成组切换用 <template>
  • 列表渲染:为每项提供稳定唯一的 key;避免索引作为 key
  • 细节:对象/整数遍历与作用域要点清晰;避免同一元素同时 v-ifv-for
  • 数组侦测:区分变更/非变更方法;非变更需用返回值替换原数组。