3、模板语法与指令:插值表达式、v-bind、v-model、v-for、v-if/v-show、事件处理
模板语法,说白了就是 Vue 用来把数据「映射」到 DOM 上的那套规则。我刚接触 Vue 时,觉得这东西不就是写写花括号嘛?后来才发现,这里面的门道还真不少。今天咱们就把最核心的六个知识点掰开揉碎聊一聊。
3.1 插值表达式:最基础的「数据填坑」
插值表达式用双花括号 {{ }} 包裹。它的作用很简单——把 JavaScript 表达式的计算结果渲染到页面上。
<template>
<p>{{ message }}</p>
<p>{{ count + 1 }}</p>
<p>{{ isActive ? '激活' : '未激活' }}</p>
</template>
<script setup>
const message = '你好,Vue3!'
const count = 10
const isActive = true
</script>
if (x) { ... },结果页面直接报错。记住,它只负责取值,不负责逻辑控制。
3.2 v-bind:动态绑定 HTML 属性
有时候你需要动态控制一个标签的属性,比如图片的 src、按钮的 disabled。这时候 v-bind 就派上用场了。它可以把数据绑定到 HTML 属性上。
<template>
&img :src="imageUrl" alt="动态图片" />
<button :disabled="isSubmitting">提交</button>
<div :class="{ active: isActive, error: hasError }">状态盒子</div>
</template>
<script setup>
const imageUrl = 'https://example.com/logo.png'
const isSubmitting = true
const isActive = false
const hasError = true
</script>
v-bind: 可以简写为 :。我个人习惯一直用简写,少打几个字,代码也更清爽。class 和 style 的绑定比较特殊,支持对象和数组语法,这在做动态样式时非常实用。
null 或 undefined,该属性会被移除。我曾经踩过这个坑——以为传个 false 就行,结果属性还在,只是值变成了 false 字符串。
3.3 v-model:双向绑定的「魔法」
v-model 是 Vue 最让人舒服的特性之一。它把数据绑定和事件监听合二为一。说白了,就是「数据变,视图变;视图变,数据也变」。
<template>
<input v-model="username" placeholder="请输入用户名" />
<p>你输入的是:{{ username }}</p>
<!-- 等价于下面这种写法 -->
<input :value="username" @input="username = $event.target.value" />
</template>
<script setup>
import { ref } from 'vue'
const username = ref('')
</script>
在 Vue3 中,v-model 默认绑定的是 modelValue 属性,触发的是 update:modelValue 事件。如果你自定义组件,记得按这个约定来。另外,Vue3 允许一个组件上使用多个 v-model,比如 v-model:title 和 v-model:content,这在封装复杂表单组件时特别好用。
3.4 v-for:列表渲染的「循环器」
渲染列表数据,v-for 是标配。它支持遍历数组和对象。
<template>
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index + 1 }}. {{ item.name }}
</li>
</ul>
<!-- 遍历对象 -->
<div v-for="(value, key, index) in user" :key="key">
{{ key }}: {{ value }}
</div>
</template>
<script setup>
const items = [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橘子' }
]
const user = { name: '张三', age: 28, city: '北京' }
</script>
这里有个关键点——:key 必须加。为什么?Vue 的虚拟 DOM 靠 key 来识别节点,没有 key 或者用 index 当 key,在列表增删时会出现渲染错乱。我在项目中遇到过:用 index 做 key,结果删除第二项后,第三项的 input 框内容跑到了第二项上。嗯,从那以后我再也不敢偷懒了。
3.5 v-if / v-show:条件渲染的「双胞胎」
这两个指令都用来控制元素的显示与隐藏,但机制完全不同。
- v-if:真正的条件渲染。条件为假时,元素根本不会出现在 DOM 中。
- v-show:只是切换 CSS 的
display属性。元素始终在 DOM 中。
<template>
<div v-if="isLoggedIn">欢迎回来,用户!</div>
<div v-else>请先登录</div>
<div v-show="isVisible">我始终在 DOM 中,只是被隐藏了</div>
</template>
<script setup>
const isLoggedIn = false
const isVisible = true
</script>
怎么选?我的经验是:如果元素频繁切换(比如选项卡、弹窗),用 v-show 性能更好。如果切换不频繁,或者条件判断涉及复杂的计算,用 v-if 更合适。另外,v-if 可以和 v-else-if、v-else 配合使用,形成多分支条件。
<template> 标签,把 v-if 放在上面。
3.6 事件处理:让页面「活」起来
没有事件,页面就是一张静态海报。Vue 用 v-on(简写 @)来绑定事件。
<template>
<button @click="handleClick">点击我</button>
<input @keyup.enter="handleEnter" placeholder="按回车触发" />
<div @click.stop="handleInner">阻止冒泡</div>
</template>
<script setup>
const handleClick = (event) => {
console.log('按钮被点击了', event)
}
const handleEnter = (e) => {
console.log('你按了回车,输入内容是:', e.target.value)
}
const handleInner = () => {
console.log('只触发内部点击,不会冒泡到父元素')
}
</script>
Vue 提供了丰富的事件修饰符:.stop(阻止冒泡)、.prevent(阻止默认行为)、.once(只触发一次)、.capture(捕获模式)等。还有按键修饰符,比如 .enter、.esc、.delete。这些修饰符可以链式使用,比如 @click.stop.prevent。
3.7 综合示例:一个简单的待办事项列表
把上面这些知识点串起来,写一个简单的待办事项列表。你想想看,是不是正好用到了插值、v-bind、v-model、v-for、v-if 和事件处理?
<template>
<div>
<h2>我的待办事项</h2>
<input v-model="newTodo" @keyup.enter="addTodo" placeholder="输入新事项,按回车添加" />
<ul>
<li v-for="(todo, index) in todos" :key="todo.id">
<span :class="{ completed: todo.done }" @click="toggleTodo(index)">
{{ todo.text }}
</span>
<button @click="removeTodo(index)">删除</button>
</li>
</ul>
<p v-if="todos.length === 0">暂无待办事项,添加一个吧!</p>
<p v-show="todos.length > 0">共 {{ todos.length }} 项</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const newTodo = ref('')
const todos = ref([
{ id: 1, text: '学习 Vue3 模板语法', done: false },
{ id: 2, text: '练习指令用法', done: true }
])
let nextId = 3
const addTodo = () => {
if (newTodo.value.trim() === '') return
todos.value.push({
id: nextId++,
text: newTodo.value,
done: false
})
newTodo.value = ''
}
const toggleTodo = (index) => {
todos.value[index].done = !todos.value[index].done
}
const removeTodo = (index) => {
todos.value.splice(index, 1)
}
</script>
这个例子虽然简单,但涵盖了模板语法的核心用法。你可以在本地跑一下,感受一下数据驱动视图的魅力。说白了,Vue 就是帮你把「数据」和「界面」之间的映射关系管理好,剩下的就是写业务逻辑了。