跳至内容
模板语法与常用指令

模板语法与常用指令

Vue 使用基于 HTML 的模板语法,将 DOM 声明式地绑定到组件数据。编译后模板会转为高效的虚拟 DOM 渲染函数。本文覆盖 Vue 3 最常用的模板语法和内置指令。

文本插值

用双花括号 {{ }} 渲染文本,支持任意 JavaScript 表达式:

<template>
  <p>{{ message }}</p>
  <p>{{ count + 1 }}</p>
  <p>{{ ok ? '是' : '否' }}</p>
  <p>{{ list.join(', ') }}</p>
  <p>{{ user.name.toUpperCase() }}</p>
</template>
{{ }} 只能包含单个表达式,不能写语句(如 iffor)。复杂逻辑放到 computed 计算属性中。

原始 HTML:v-html

{{ }} 会转义 HTML 标签,如需渲染原始 HTML 用 v-html

<template>
  <div v-html="richContent"></div>
</template>

<script setup>
const richContent = '<strong>加粗</strong>文字'
</script>
v-html 会导致 XSS 风险,永远不要对用户输入使用 v-html,只用于可信的内容。

属性绑定:v-bind / :

v-bind(简写 :)将属性值绑定到响应式数据:

<template>
  <!-- 绑定单个属性 -->
  <img :src="imageUrl" :alt="imageAlt">
  <button :disabled="isLoading">提交</button>
  <a :href="link" :target="openInNew ? '_blank' : '_self'">链接</a>

  <!-- 绑定 class对象语法 -->
  <div :class="{ active: isActive, 'text-danger': hasError }"></div>

  <!-- 绑定 class数组语法 -->
  <div :class="[baseClass, isActive ? 'active' : '']"></div>

  <!-- 绑定 style -->
  <p :style="{ color: textColor, fontSize: fontSize + 'px' }">文字</p>

  <!-- 一次绑定多个属性v-bind 不带参数 -->
  <div v-bind="{ id: 'box', class: 'wrapper' }"></div>
</template>

<script setup>
import { ref } from 'vue'

const imageUrl = ref('/logo.png')
const isActive = ref(true)
const hasError = ref(false)
const isLoading = ref(false)
</script>

事件监听:v-on / @

v-on(简写 @)监听 DOM 事件:

<template>
  <!-- 内联处理器 -->
  <button @click="count++">点击</button>

  <!-- 方法处理器 -->
  <button @click="handleClick">点击</button>

  <!-- 传参($event 获取原生事件对象 -->
  <button @click="handleClick('hello', $event)">点击</button>

  <!-- 事件修饰符 -->
  <form @submit.prevent="onSubmit">...</form>    <!-- 阻止默认行为 -->
  <div @click.stop="onClick">...</div>           <!-- 阻止冒泡 -->
  <div @click.once="onClick">...</div>           <!-- 只触发一次 -->
  <div @click.self="onClick">...</div>           <!-- 只有点击自身触发 -->

  <!-- 按键修饰符 -->
  <input @keyup.enter="onEnter">                 <!--  Enter 触发 -->
  <input @keyup.ctrl.enter="onCtrlEnter">        <!--  Ctrl+Enter -->
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)

function handleClick(msg, event) {
  console.log(msg, event)
}

function onSubmit() {
  console.log('表单提交')
}
</script>

常用事件修饰符速查

修饰符说明
.preventevent.preventDefault()
.stopevent.stopPropagation()
.once只触发一次后移除监听
.self只在事件源为自身时触发
.capture使用捕获模式
.passive提升滚动性能(不能与 .prevent 同用)

条件渲染:v-if / v-show

<template>
  <!-- v-if条件为 false 元素不存在于 DOM -->
  <div v-if="status === 'loading'">加载中</div>
  <div v-else-if="status === 'error'">出错了</div>
  <div v-else>{{ data }}</div>

  <!-- v-show条件为 false 元素存在但 display:none -->
  <div v-show="isVisible">常切换的内容</div>
</template>
对比v-ifv-show
渲染方式真正销毁/重建 DOM切换 display CSS
初始渲染条件为假时不渲染总是渲染
切换开销高(重新挂载组件)低(仅改 CSS)
适用场景条件很少变化频繁切换
v-ifv-for 同时使用时,v-if 优先级更高(Vue 3)。若需要一起用,把 v-for 放在外层 <template> 上。

列表渲染:v-for

<template>
  <!-- 遍历数组 -->
  <ul>
    <li v-for="(item, index) in list" :key="item.id">
      {{ index + 1 }}. {{ item.name }}
    </li>
  </ul>

  <!-- 遍历对象 -->
  <ul>
    <li v-for="(value, key, index) in user" :key="key">
      {{ key }}: {{ value }}
    </li>
  </ul>

  <!-- 遍历数字范围 -->
  <span v-for="n in 5" :key="n">{{ n }} </span>

  <!-- 分组渲染不产生额外 DOM 节点 -->
  <template v-for="item in list" :key="item.id">
    <dt>{{ item.name }}</dt>
    <dd>{{ item.desc }}</dd>
  </template>
</template>
:key 是必填项。Vue 通过 key 识别列表中每个节点,缺少 key 会导致渲染错误和性能下降。key 应该是稳定唯一的值(如 id),不要用数组 index(列表有增删时会出错)。

表单绑定:v-model

v-model 实现表单元素与数据的双向绑定,是 :value + @input 的语法糖:

<template>
  <!-- 文本输入 -->
  <input v-model="text" placeholder="请输入">
  <p>输入内容{{ text }}</p>

  <!-- 多行文本 -->
  <textarea v-model="description"></textarea>

  <!-- 复选框单个绑定 boolean -->
  <input type="checkbox" v-model="agree"> 同意协议

  <!-- 复选框多个绑定数组 -->
  <input type="checkbox" value="Vue" v-model="selected">
  <input type="checkbox" value="React" v-model="selected">
  <p>已选{{ selected }}</p>

  <!-- 单选按钮 -->
  <input type="radio" value="male" v-model="gender"> 
  <input type="radio" value="female" v-model="gender"> 

  <!-- 下拉选择 -->
  <select v-model="city">
    <option value="">请选择</option>
    <option value="bj">北京</option>
    <option value="sh">上海</option>
  </select>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('')
const description = ref('')
const agree = ref(false)
const selected = ref([])
const gender = ref('')
const city = ref('')
</script>

v-model 修饰符

修饰符效果
.lazy改为监听 change 事件(失焦后更新)
.number自动将输入值转为数字
.trim自动去除首尾空白
<input v-model.lazy="text">        <!-- 失焦才更新 -->
<input v-model.number="age">       <!-- 转数字 -->
<input v-model.trim="username">    <!-- 去空格 -->

其他常用指令

指令说明示例
v-once只渲染一次,不追踪更新<span v-once>{{ title }}</span>
v-pre跳过编译,显示原始 Mustache<span v-pre>{{ raw }}</span>
v-cloak配合 CSS 防止闪烁(SPA 一般不需要)[v-cloak] { display:none }
v-memo记忆化子树,依赖不变时跳过渲染(性能优化)<div v-memo="[a, b]">

自定义指令

<script setup>
// 局部自定义指令(v-focus)
const vFocus = {
  mounted(el) {
    el.focus()
  }
}
</script>

<template>
  <input v-focus placeholder="自动聚焦">
</template>

全局注册(在 main.ts 中):

app.directive('focus', {
  mounted(el) { el.focus() }
})
最后更新于