Appearance
22|Vue 条件渲染、列表渲染与表单
页面内容不总是固定的:根据权限显示不同菜单、根据状态显示不同标签、遍历数据生成表格行。Vue 提供 v-if、v-show、v-for 三个核心指令处理这些动态渲染需求。表单处理则是前端与用户的直接交互入口,需要理解双向绑定和表单验证。
一、条件渲染
v-if
根据条件决定是否渲染元素:
vue
<template>
<div v-if="isAdmin">管理员面板</div>
<div v-else-if="isEditor">编辑面板</div>
<div v-else>只读视图</div>
</template>
<script setup>
import { ref } from "vue";
const isAdmin = ref(false);
const isEditor = ref(true);
</script>v-if 是"真正的"条件渲染——条件为 false 时,元素从 DOM 中移除;条件变为 true 时,重新创建并挂载。
v-show
vue
<div v-show="isVisible">条件内容</div>v-show 通过 CSS display: none 切换可见性,元素始终存在于 DOM 中。
v-if vs v-show
| v-if | v-show | |
|---|---|---|
| 切换开销 | 高(创建/销毁 DOM) | 低(切换 CSS) |
| 初始渲染 | 条件为 false 时不渲染 | 始终渲染 |
| 适用场景 | 条件很少变化 | 需要频繁切换 |
如果组件内部有昂贵的初始化逻辑(如发送请求、创建图表),用 v-if 避免不必要的开销;如果是标签页切换这种高频操作,用 v-show 更流畅。
在 template 上使用 v-if
template 标签本身不会渲染到 DOM,适合包裹多个元素:
vue
<template v-if="hasData">
<h2>数据列表</h2>
<table>...</table>
</template>二、列表渲染
v-for
遍历数组渲染列表:
vue
<template>
<ul>
<li v-for="server in servers" :key="server.id">
{{ server.hostname }} - {{ server.status }}
</li>
</ul>
</template>
<script setup>
import { ref } from "vue";
const servers = ref([
{ id: 1, hostname: "web-01", status: "running" },
{ id: 2, hostname: "web-02", status: "stopped" },
]);
</script>:key 是必须的,且必须是唯一标识。Vue 用 key 追踪每个节点的身份,在数据变化时高效复用或更新 DOM。不要用数组索引作为 key(除非列表只增不减且不会重排)。
获取索引
vue
<li v-for="(server, index) in servers" :key="server.id">
{{ index + 1 }}. {{ server.hostname }}
</li>遍历对象
vue
<ul>
<li v-for="(value, key) in serverInfo" :key="key">
{{ key }}: {{ value }}
</li>
</ul>v-for 和 v-if 不要同层使用
vue
<!-- 错误:Vue 会报错,优先级冲突 -->
<li v-for="item in list" v-if="item.active">{{ item.name }}</li>
<!-- 正确:用计算属性先过滤 -->
<li v-for="item in activeItems" :key="item.id">{{ item.name }}</li>
<script setup>
const activeItems = computed(() => list.value.filter((i) => i.active));
</script>三、表单绑定
v-model
v-model 在表单元素和数据之间建立双向绑定:输入框的值变化时数据自动更新,数据变化时输入框的值自动更新。
vue
<template>
<input v-model="hostname" placeholder="主机名">
<p>输入的值: {{ hostname }}</p>
</template>
<script setup>
import { ref } from "vue";
const hostname = ref("");
</script>各种表单元素
vue
<template>
<!-- 文本输入 -->
<input v-model="form.hostname" type="text">
<!-- 数字输入 -->
<input v-model.number="form.port" type="number">
<!-- 复选框 -->
<input v-model="form.active" type="checkbox"> 启用
<!-- 单选框 -->
<input v-model="form.env" type="radio" value="dev"> 开发
<input v-model="form.env" type="radio" value="prod"> 生产
<!-- 下拉选择 -->
<select v-model="form.region">
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
<!-- 多行文本 -->
<textarea v-model="form.description"></textarea>
</template>
<script setup>
import { reactive } from "vue";
const form = reactive({
hostname: "",
port: 8080,
active: true,
env: "dev",
region: "bj",
description: "",
});
</script>修饰符
| 修饰符 | 作用 | 示例 |
|---|---|---|
.lazy | 失去焦点后同步(默认是 input 事件) | v-model.lazy="text" |
.number | 自动转为数字 | v-model.number="age" |
.trim | 去除首尾空格 | v-model.trim="name" |
四、表单提交
阻止默认提交
vue
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.hostname" required>
<button type="submit">提交</button>
</form>
</template>
<script setup>
import { reactive } from "vue";
const form = reactive({
hostname: "",
});
async function handleSubmit() {
const response = await fetch("/api/servers", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(form),
});
if (response.ok) {
alert("创建成功");
form.hostname = ""; // 清空表单
}
}
</script>@submit.prevent 阻止表单默认的页面刷新提交,改由 JavaScript 处理。
五、常见错误
列表缺少 key
vue
<!-- 错误:没有 key,Vue 无法高效追踪节点 -->
<li v-for="item in list">{{ item.name }}</li>
<!-- 正确 -->
<li v-for="item in list" :key="item.id">{{ item.name }}</li>用索引作为 key
vue
<!-- 危险:列表重排或删除时,key 和实际数据对应不上 -->
<li v-for="(item, index) in list" :key="index">...</li>如果列表会增删改或排序,始终用数据的唯一标识作为 key。
直接修改 v-for 的数组元素(reactive)
js
const list = reactive([{ name: "a" }, { name: "b" }]);
// 错误:直接通过索引赋值不会触发响应
list[0] = { name: "c" };
// 正确:用 splice
list.splice(0, 1, { name: "c" });
// 如果用 ref 包装数组,可以直接赋值
const list = ref([{ name: "a" }]);
list.value[0] = { name: "c" }; // 正常响应