Appearance
✨ 条件和列表渲染 👌
条件渲染:v-if / v-else-if / v-else
使用 v-if、v-else-if、v-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-if 与 v-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-if 与 v-for。优先通过计算属性过滤数据或使用 <template> 包装,提升可读性与可维护性。
数组的侦测:变更与非变更方法
数组方法分为两类:
- 变更方法(会修改原数组):
push、pop、shift、unshift、splice、sort、reverse - 非变更方法(返回新数组,不改原数组):
filter、concat、slice、map
变更方法会触发响应式更新;非变更方法需要用返回值替换原值:
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-if与v-for。 - 数组侦测:区分变更/非变更方法;非变更需用返回值替换原数组。
