Vue 内置指令

指令是 Vue 模板里以 v- 开头的特殊属性,告诉 Vue"这个元素怎么渲染、怎么响应数据变化"。常用的就那么八九个,记住它们你就能写出绝大多数页面。本章系统过一遍。

1. v-if / v-else-if / v-else:条件渲染

v-if 是"真条件渲染":表达式为 false 时,对应的 DOM 节点根本不存在。适合不常切换的场景。

<template>
  <!-- v-if:真条件渲染,不满足时 DOM 完全不存在 -->
  <p v-if="status === 'loading'">加载中...</p>
  <p v-else-if="status === 'error'">出错了</p>
  <p v-else>加载完成</p>

  <!-- 配合 template 标签包裹多个元素(不会渲染出 template) -->
  <template v-if="showPanel">
    <h3>面板标题</h3>
    <p>面板内容</p>
    <button>关闭</button>
  </template>
</template>

<script setup>
import { ref } from "vue";
const status = ref("loading");
const showPanel = ref(true);
</script>

三个注意点:

2. v-show:切换 display

v-show 不管条件真假,DOM 都会被渲染,只是通过 display: none 控制显隐。

<template>
  <!-- v-show:切换 display 样式,DOM 一直在 -->
  <div v-show="isVisible">这段一直在 DOM 里,只是被隐藏</div>

  <!-- v-if 不满足时,DOM 节点根本不存在 -->
  <div v-if="isVisible">这段在隐藏时不存在</div>
</template>

<script setup>
import { ref } from "vue";
const isVisible = ref(false);
</script>

<!-- 切换频率高 → v-show(DOM 复用,无创建开销)
     切换频率低 → v-if(节省初始渲染)
     运行时条件变化少 → v-if(更轻量) -->

v-if vs v-show 是面试常考题,记住区别:

3. v-for:列表渲染(重点)

v-for 遍历数组、对象、数字范围,生成一组元素。它是 Vue 中最容易踩坑的指令之一。

<template>
  <!-- v-for 遍历数组,必须给 :key(用唯一 id,不要用 index) -->
  <li v-for="item in products" :key="item.id">
    {{ item.name }} - ¥{{ item.price }}
  </li>

  <!-- 需要索引时 -->
  <li v-for="(item, index) in products" :key="item.id">
    {{ index + 1 }}. {{ item.name }}
  </li>

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

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

  <!-- 遍历对象数组,嵌套子组件 -->
  <user-card v-for="u in users" :key="u.id" :data="u" />
</template>

<script setup>
import { ref } from "vue";
const products = ref([
  { id: 1, name: "苹果", price: 5 },
  { id: 2, name: "香蕉", price: 3 },
]);
const user = ref({ name: "小明", age: 20, city: "北京" });
const users = ref([{ id: 1 }, { id: 2 }]);
</script>

几个要点:

4. :key 为什么这么重要?

key 是 Vue 复用 DOM 的"身份证"。看下面这个反面教材:

<template>
  <!-- ❌ 反面教材:用 index 作 key -->
  <li v-for="(item, i) in list" :key="i">{{ item.text }}</li>

  <!-- 当 list 顶部插入新元素,index 全部错位,
       Vue 会"误以为"原来的项变了,导致状态混乱、
       输入框内容串行、动画错位等诡异 bug -->

  <!-- ✅ 正确:用稳定的唯一 id -->
  <li v-for="item in list" :key="item.id">{{ item.text }}</li>
</template>

<script setup>
const list = [
  { id: "a1", text: "第一项" },
  { id: "a2", text: "第二项" },
];
</script>

假设列表是 [A, B, C],你头部插入新元素 X,变成 [X, A, B, C]。用 index 作 key 的话,Vue 会按位置匹配——它以为"位置 0 的元素从 A 变成了 X",于是全部元素被重新渲染,且如果元素内部有状态(比如输入框里打了字),状态会串行错位。用稳定 id 作 key,Vue 就能精确匹配,只创建一个新 DOM 节点。

所以记一条: 用稳定的唯一 id,不要用 index。如果列表纯粹是展示型(无状态、不会重排),用 index 也勉强可以;但养成用 id 的习惯永远不会错。

5. v-bind:属性绑定(缩写 :)

把数据绑定到元素的属性上。支持动态属性名、class、style 的特殊语法:

<template>
  <!-- v-bind 缩写 :,绑定属性 -->
  <img :src="avatar" :alt="name" />
  <a :href="url">链接</a>

  <!-- 动态属性名:[propName] -->
  <a :[attrName]="url">链接</a>

  <!-- class 绑定:对象/数组/三目 -->
  <div :class="{ active: isActive, error: hasError }"></div>
  <div :class="['btn', isActive ? 'btn-on' : 'btn-off']"></div>
  <div :class="baseClass"></div>

  <!-- style 绑定:对象/数组 -->
  <div :style="{ color: 'red', fontSize: size + 'px' }"></div>
  <div :style="[baseStyle, overrideStyle]"></div>
</template>

<script setup>
import { ref } from "vue");
const avatar = ref("/a.png");
const name = ref("小明");
const url = ref("/page");
const attrName = ref("href");
const isActive = ref(true);
const hasError = ref(false);
const size = ref(16);
</script>

class 和 style 的对象/数组语法是 Vue 模板的精髓——让样式也"响应式"起来。:[attrName]="url" 这种动态参数语法是 Vue 3 才有的,适合写高复用的通用组件。

6. v-on:事件绑定(缩写 @)

v-on 监听 DOM 事件,缩写 @。支持链式修饰符:

<template>
  <!-- v-on 缩写 @,事件绑定 -->
  <button @click="onClick">点我</button>
  <input @input="onInput" @focus="onFocus" />

  <!-- 修饰符:链式 -->
  <form @submit.prevent="onSubmit">  <!-- .prevent 阻止默认 -->
    <button @click.stop="onSave">    <!-- .stop 阻止冒泡 -->
      <a @click.once="onJump">链接</a> <!-- .once 只触发一次 -->
    </button>
  </form>

  <!-- 按键修饰符 -->
  <input @keyup.enter="onSearch" @keyup.esc="onCancel" />

  <!-- v-model:表单双向绑定(后续专门一章) -->
  <input v-model="keyword" />
  <p>{{ keyword }}</p>
</template>

修饰符让事件处理更声明式——你不用在回调里手动 e.preventDefault()e.stopPropagation()。常用修饰符:

7. v-model:表单双向绑定

v-model:value + @input 的语法糖,让表单元素和数据双向同步。它在《表单与 v-model》一章详讲。

8. v-html 与 v-text

这两个指令分别用于渲染原始 HTML 和文本:

9. v-once 与 v-memo:性能优化

这两个是高级特性,新手用不上,知道有就行。

10. 自定义指令

除了内置指令,Vue 还允许你自己造指令。比如一个 v-focus(元素挂载时自动聚焦):

<script setup>
import { ref, onMounted } from "vue";

// 自定义指令:在 <script setup> 里以 v 开头的小驼峰变量名注册
const vFocus = {
  mounted: (el) => el.focus(),
};

const vHighlight = {
  mounted(el, binding) {
    el.style.background = binding.value || "yellow";
  },
};
</script>

<template>
  <input v-focus />
  <p v-highlight="'lightblue'">这段高亮</p>
</template>

实际项目里,自定义指令用得不多——大部分需求用组件就能解决。但偶尔遇到"操作 DOM"的需求(聚焦、拖拽、tooltip),指令是更优雅的封装方式。

小结

这一章你系统过了一遍 Vue 的所有内置指令。重点记:v-if vs v-show 的区别、v-for 必须配 key(用 id 不用 index)、修饰符的链式写法。下一篇我们把事件处理单独展开讲——它是前端交互的核心。

← 上一篇 Vue 计算属性与侦听器

下一篇 Vue 事件处理

✈️💬