3.1 模板语法与指令
本章目标: 把数据、条件与列表映射到页面,并正确设置列表的 key。
一次看懂常用语法
<script setup lang="ts">
import { ref } from 'vue';
const heading = ref('我的任务');
const showCompleted = ref(true);
const tasks = ref([
{ id: 1, title: '补齐 JS 基础', isDone: true },
{ id: 2, title: '学习模板语法', isDone: false },
]);
</script>
<template>
<main>
<h1>{{ heading }}</h1>
<label>
<input v-model="showCompleted" type="checkbox" /> 显示已完成状态
</label>
<p v-if="tasks.length === 0">暂无任务</p>
<ul v-else>
<li v-for="task in tasks" :key="task.id">
<span :class="{ completed: task.isDone }">{{ task.title }}</span>
<small v-show="showCompleted && task.isDone">已完成</small>
</li>
</ul>
</main>
</template>
<style scoped>
.completed { text-decoration: line-through; color: #67717e; }
small { margin-left: 0.75rem; }
</style>
这里的 v-model 后面会专门讲解,可以先理解为让复选框和布尔状态保持一致。
插值和属性绑定
| 语法 | 作用 | 示例 |
|---|---|---|
{{ expression }} | 输出文本 | {{ task.title }} |
v-bind:属性 / :属性 | 绑定属性值 | :disabled="busy" |
v-on:事件 / @事件 | 监听事件 | @click="save" |
:class | 绑定类名 | :class="{ completed: task.isDone }" |
:style | 绑定行内样式 | :style="{ color: textColor }" |
模板中的属性不是字符串拼接:动态属性使用 :,不要写 href="{{ url }}"。:disabled="false" 会让布尔属性处于关闭状态;普通 disabled="false" 仍然表示禁用。
插值里的内容是表达式,可以写 count + 1 或函数调用,不能写 if (...) { ... } 等语句。复杂逻辑放进脚本或 computed。
v-if 与 v-show
v-if 决定元素或组件是否创建,切换时会挂载或卸载。v-show 总是创建元素,使用 CSS display 切换可见性。
频繁显示隐藏的简单区域可以考虑 v-show;不满足条件时不应初始化的组件适合 v-if。v-else 必须紧跟对应的 v-if 或 v-else-if。
列表 key 的意义
<TaskRow v-for="task in tasks" :key="task.id" :task="task" />
key 帮助 Vue 在重排、插入和删除时识别同一个节点。任务 ID 稳定且唯一,数组下标却会随排序变化,可能导致输入框或组件内部状态跟错任务。
它们的优先级容易让循环变量不可用。先用 computed 筛选数组,再对筛选结果做 v-for;或者在外层 <template> 上分开条件与循环。
文本安全
{{ task.title }} 会按文本输出。v-html 将字符串当 HTML 插入,适合可信且经过清洗的内容。普通用户标题、评论或服务端不可信文本应使用插值,避免 XSS。
练习
把列表改成只显示未完成任务。先用 tasks.filter(...) 理解筛选,再在下一章之后的 computed 章节把它提取为派生状态。
参考答案
<li v-for="task in tasks.filter(t => !t.isDone)" :key="task.id">
{{ task.title }}
</li>
这个写法适合观察效果,业务代码更推荐 computed 命名筛选结果,使模板更清楚。空状态也要根据“筛选结果为空”判断。