模板语法与数据绑定

Vue.js 的模板语法是构建用户界面的核心,它基于 HTML 扩展,提供了声明式的方式将数据与 DOM 绑定。模板语法主要分为插值语法指令语法两大类,结合双向数据绑定和事件处理机制,能够高效实现动态界面开发。本章将系统讲解模板语法的核心特性及实际应用,每个知识点均配套完整案例,帮助开发者快速掌握数据驱动视图的实现方式。

插值语法:声明式数据渲染

插值语法是 Vue 模板中最基础的数据绑定方式,通过双大括号 {{ }} 包裹 JavaScript 表达式,实现数据到视图的动态渲染。其核心功能是解析标签体内容,将表达式结果插入到 DOM 中[13]。

基本用法
插值表达式支持多种 JavaScript 表达式类型,包括变量访问、属性读取、函数调用、运算及三元表达式等:

html
复制代码
<div>
  <p>{{ nickname.toUpperCase() }}</p>  <!-- 方法调用:将昵称转为大写 -->
  <p>{{ age >= 18 ? '成年' : '未成年' }}</p>  <!-- 三元表达式:条件判断 -->
  <p>{{ user.name + '(' + user.age + ')' }}</p>  <!-- 字符串拼接 -->
  <p>{{ getGreeting() }}</p>  <!-- 函数调用:执行方法返回结果 -->
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return {
      nickname: 'vue-dev',
      age: 22,
      user: { name: '张三', age: 28 },
      getGreeting: () => 'Hello Vue!'
    }
  }
}).mount('#app');

关键限制
插值语法虽灵活,但存在以下使用限制,需特别注意:
插值表达式使用规范

  1. 仅支持表达式,不支持语句:可写 age + 1,但不能写 if (age > 18) { ... }for 循环。
  2. 标签属性中禁用:不能在标签属性(如 src="{{ imageUrl }}")中使用,需通过 v-bind 指令实现属性绑定。
  3. 数据必须预定义:表达式中使用的变量/属性必须在 datasetup 中声明,否则会报错(如 {{ hobby }} 若未定义则抛出引用错误)[14]。

命令式 vs 声明式:渲染逻辑对比
传统命令式编程需手动操作 DOM 实现数据渲染,而 Vue 的声明式语法可大幅简化流程。以渲染用户列表为例:

编程范式 实现代码 核心差异
命令式 javascript let persons = [ {id:1,name:'张三',age:18}, {id:2,name:'李四',age:20} ]; let _str = ''; persons.forEach(i => { _str += `<li>${i.id}-${i.name}-${i.age}</li>` }); document.querySelector('#list').innerHTML = _str; 需手动拼接 HTML 字符串、操作 DOM,逻辑与 DOM 操作耦合
声明式 html <ul id="list"> <li v-for="i in persons" :key="i.id"> {{i.id}}-{{i.name}}-{{i.age}} </li> </ul> javascript const app = Vue.createApp({ data() { return { persons: [ {id:1,name:'张三',age:18}, {id:2,name:'李四',age:20} ] } } }).mount('#list'); 直接描述目标结果,Vue 自动处理 DOM 更新,逻辑与视图分离[8]

指令语法:DOM 行为控制

指令是 Vue 模板的核心功能,以 v- 为前缀,用于声明式地控制 DOM 行为(如属性绑定、事件监听、条件渲染、列表循环等)。指令的值需为 JavaScript 表达式,且可直接访问实例中的数据[14]。

v-bind:动态属性绑定

功能:将标签属性与数据动态关联,数据变化时自动更新属性值。
语法v-bind:属性名="表达式",简写为 :属性名="表达式"
应用场景:动态加载图片、设置链接、绑定样式等。

案例:动态图片加载
根据数据切换显示不同图片:

html
复制代码
<div id="app">
  <img 
    :src="currentImage" 
    :alt="imageDesc" 
    :title="imageTitle"
    style="width: 200px;"
  >
  <button @click="switchImage">切换图片</button>
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return {
      // 图片列表
      images: [
        { src: 'https://picsum.photos/id/237/200/200', desc: '小狗', title: '可爱宠物' },
        { src: 'https://picsum.photos/id/1/200/200', desc: '风景', title: '自然景观' }
      ],
      currentIndex: 0  // 当前显示图片索引
    }
  },
  computed: {
    currentImage() { return this.images[this.currentIndex].src; },
    imageDesc() { return this.images[this.currentIndex].desc; },
    imageTitle() { return this.images[this.currentIndex].title; }
  },
  methods: {
    switchImage() {
      this.currentIndex = (this.currentIndex + 1) % this.images.length;
    }
  }
}).mount('#app');

Vue 3.4+ 增强特性:支持同名速记语法,当属性名与数据名相同时,可简化为 :属性名。例如 <img :id :src :alt> 等价于 <img :id="id" :src="src" :alt="alt">[15]。

v-on:事件监听

功能:绑定 DOM 事件到方法,用户触发事件时执行指定逻辑。
语法v-on:事件名="方法名/表达式",简写为 @事件名="方法名/表达式"
应用场景:按钮点击、表单提交、键盘输入等交互。

案例:计数器组件
实现点击按钮增减数字的计数器:

html
复制代码
<div id="counter">
  <h3>当前计数:{{ count }}</h3>
  <button @click="increment">+1</button>  <!-- 调用方法增加计数 -->
  <button @click="count--">-1</button>  <!-- 直接执行表达式减少计数 -->
  <button @click="add(5)">+5</button>  <!-- 传递参数 -->
</div>
javascript
复制代码
const app = Vue.createApp({
  data() { return { count: 0 }; },
  methods: {
    increment() { this.count++; },  // 增加1
    add(step) { this.count += step; }  // 增加指定步长
  }
}).mount('#counter');
v-if/v-else:条件渲染

功能:根据条件动态添加/移除 DOM 元素(而非隐藏),实现条件显示。
语法v-if="条件"v-else-if="条件"v-else(需紧跟 v-if 元素)。
应用场景:用户角色权限展示、登录状态切换、空数据提示等。

案例:用户角色权限控制
根据用户角色(管理员/普通用户)显示不同操作按钮:

html
复制代码
<div id="user-role">
  <div v-if="user.role === 'admin'">
    <h3>管理员面板</h3>
    <button>用户管理</button>
    <button>系统设置</button>
  </div>
  <div v-else-if="user.role === 'editor'">
    <h3>编辑面板</h3>
    <button>内容发布</button>
  </div>
  <div v-else>
    <h3>访客视图</h3>
    <p>请登录后操作</p>
  </div>
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return {
      user: { role: 'admin' }  // 可修改为 'editor' 或其他值测试效果
    }
  }
}).mount('#user-role');
v-for:列表渲染

功能:基于数组或对象循环生成 DOM 列表。
语法v-for="(item, index) in 数组" :key="唯一标识"index 为可选参数(索引)。
核心要求:必须绑定 :key 属性,值需为唯一不变的标识(如 ID),用于 Vue 高效更新 DOM[16]。

案例:用户列表渲染
循环展示用户信息,并支持删除操作:

html
复制代码
<div id="user-list">
  <ul>
    <!-- v-for遍历users数组,:key绑定唯一ID确保高效更新 -->
    <li v-for="(user, index) in users" :key="user.id" style="margin: 8px 0;">
      {{ index + 1 }}. {{ user.name }} ({{ user.age }}岁) 
      <button @click="deleteUser(user.id)">删除</button>
    </li>
  </ul>
  <p v-if="users.length === 0">暂无用户数据</p>
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return {
      users: [
        { id: 1, name: '张三', age: 25 },
        { id: 2, name: '李四', age: 30 },
        { id: 3, name: '王五', age: 28 }
      ]
    }
  },
  methods: {
    deleteUser(id) {
      this.users = this.users.filter(user => user.id !== id);
    }
  }
}).mount('#user-list');

为什么需要 :key
若不指定 :key,Vue 会使用“就地更新”策略,可能导致列表项状态(如输入框内容)错乱。key 确保每个节点与数据唯一对应,避免重复渲染问题[16]。

v-model:双向数据绑定

功能:在表单元素上实现数据与视图的双向同步——数据变化更新视图,用户输入更新数据。
原理:语法糖,等价于 v-bind:value="数据" + v-on:input="数据 = $event.target.value"[13]。
支持元素:input(文本/复选/单选)、textarea、select 等表单控件。

常见表单绑定场景

1. 文本框/文本域
绑定字符串类型数据:

html
复制代码
<div id="app">
  <input type="text" v-model="username" placeholder="输入用户名">
  <p>用户名:{{ username }}</p>

  <textarea v-model="description" rows="3" placeholder="输入描述"></textarea>
  <p>描述:{{ description }}</p>
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return { username: '', description: '' };
  }
}).mount('#app');

2. 复选框

  • 单个复选框:绑定布尔值(是否选中)。
  • 多个复选框:绑定数组(存储选中项的 value)。
html
复制代码
<div id="checkboxes">
  <!-- 单个复选框:布尔值绑定 -->
  <label>
    <input type="checkbox" v-model="agree"> 同意协议
  </label>
  <p>是否同意:{{ agree ? '是' : '否' }}</p>

  <!-- 多个复选框:数组绑定 -->
  <div>
    <p>选择爱好(可多选):</p>
    <label><input type="checkbox" v-model="hobbies" value="reading"> 阅读</label>
    <label><input type="checkbox" v-model="hobbies" value="sports"> 运动</label>
    <label><input type="checkbox" v-model="hobbies" value="coding"> 编程</label>
  </div>
  <p>选中的爱好:{{ hobbies.join(', ') }}</p>
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return { agree: false, hobbies: [] };
  }
}).mount('#checkboxes');

3. 下拉框

  • 单选下拉框:绑定选中项的 value
  • 多选下拉框(multiple):绑定数组。
html
复制代码
<div id="selects">
  <!-- 单选下拉框 -->
  <select v-model="city">
    <option value="">请选择城市</option>
    <option value="beijing">北京</option>
    <option value="shanghai">上海</option>
  </select>
  <p>选中城市:{{ city }}</p>

  <!-- 多选下拉框(按住Ctrl键多选) -->
  <select v-model="tags" multiple size="3">
    <option value="vue">Vue</option>
    <option value="react">React</option>
    <option value="angular">Angular</option>
  </select>
  <p>选中标签:{{ tags.join(', ') }}</p>
</div>
javascript
复制代码
const app = Vue.createApp({
  data() {
    return { city: '', tags: [] };
  }
}).mount('#selects');

单向绑定 vs 双向绑定

绑定方式 语法 数据流向 应用场景
单向绑定(v-bind) :value="data" 仅从 data → 视图 非表单元素(如图片、div)、无需用户输入的场景
双向绑定(v-model) v-model="data" data ↔ 视图 表单元素(输入框、复选框等),需同步用户输入的场景

事件修饰符:简化事件处理

事件修饰符是 v-on 的增强功能,通过 .修饰符 语法简化常见事件处理逻辑(如阻止默认行为、停止冒泡等),无需手动编写复杂代码。

常用修饰符及场景
修饰符 作用 应用场景
.prevent 阻止事件默认行为 阻止表单提交、链接跳转
.终止 停止事件冒泡 嵌套元素点击事件隔离
.once 事件仅触发一次 一次性按钮(如“同意协议”)
.self 仅元素自身触发时执行 忽略子元素冒泡的事件

案例 1:阻止表单默认提交
表单提交时默认会刷新页面,使用 .prevent 阻止:

html
复制代码
<form @submit.prevent="handleSubmit">
  <input type="text" v-model="username" required>
  <button type="submit">提交</button>
</form>
javascript
复制代码
methods: {
  handleSubmit() {
    console.log('提交数据:', this.username);  // 无需手动调用 event.preventDefault()
  }
}

案例 2:停止事件冒泡
内层元素点击时,避免触发外层元素的点击事件:

html
复制代码
<div @click="parentClick" style="padding: 20px; background: #eee;">
  外层div
  <button @click.终止="childClick">点击我(仅触发子事件)</button>
</div>
javascript
复制代码
methods: {
  parentClick() { console.log('触发外层点击'); },
  childClick() { console.log('触发按钮点击'); }  // 不会触发 parentClick
}

通过事件修饰符,可大幅减少模板中对原生事件对象(event)的直接操作,使代码更简洁、可读性更强。

总结

模板语法是 Vue 框架的核心竞争力之一,通过插值语法实现数据到视图的声明式渲染,指令系统控制 DOM 行为,双向绑定简化表单交互,事件修饰符优化事件处理。掌握这些语法不仅能提升开发效率,更能深刻理解 Vue “数据驱动视图”的设计思想。实际开发中,需注意 :key 的唯一性、插值表达式的使用限制、事件修饰符的合理搭配,以构建高效、可维护的 Vue 应用。