讲解

Vue 模板是合法的 HTML,加上一套扩展语法,最终被编译成渲染函数。最常用的语法是文本插值:双大括号 {{ message }} 把数据渲染成文本。插值里可以放任意 JavaScript 表达式({{ count + 1 }}{{ ok ? '是' : '否' }}),但不能放语句(if、for 不行),也不能放多条表达式。数据是响应式的,值变了插值自动更新。

属性绑定用 v-bind,简写为冒号:<img :src="url"><button :disabled="loading">。注意绑定后属性的值是 JavaScript 表达式而不是字符串——:count="0" 传的是数字 0,count="0" 传的是字符串 "0",这个区别在组件传参时经常咬人。事件绑定用 v-on,简写为 @:<button @click="add">,下一章详述。

还有三个一次性但很有用的指令:v-html 把数据当 HTML 渲染(只对可信内容使用,用户输入直接渲染等于开门迎接 XSS);v-show 用 CSS display 控制显隐,适合高频切换;v-once 只渲染一次,之后数据变了也不更新,适合完全静态的块。

Vue 3 模板还支持多根节点(不再需要包一层无意义的 div),以及 <template> 标签做逻辑分组——它参与条件/循环渲染但自身不产生 DOM 节点。理解「模板不是运行时字符串拼接,而是编译产物」很重要:编译器会做静态提升、缓存事件 handler 等优化,所以模板的写法会影响(通常是改善)运行时性能。

示例

表达式求值规则、v-bind 的类型差异,先用纯 JS 过一遍(在本教程构建时被真实执行):

import assert from 'node:assert/strict';

// 插值里只能放表达式:三元 OK,语句不 OK
function renderStatus(ok) {
  return ok ? '在线' : '离线'; // 模板里 {{ ok ? '在线' : '离线' }} 等价
}
assert.strictEqual(renderStatus(true), '在线');
assert.strictEqual(renderStatus(false), '离线');

// v-bind 的值是表达式:':count="0"' 是数字,'count="0"' 是字符串
const bound = { num: 0, str: '0' };
assert.strictEqual(bound.num === 0, true); // 数字
assert.strictEqual(bound.str === '0', true); // 字符串
assert.notStrictEqual(bound.str, 0);

// v-html 的风险演示:用户输入里的脚本必须过滤
const userInput = '<img src=x onerror="alert(1)">Hello';
function escapeHtml(s) {
  return s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
const safe = escapeHtml(userInput);
assert.ok(!safe.includes('<img'));
console.log('转义后:', safe);

// 三元嵌套不如 computed 清晰的边界演示
const level = (score) => (score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格');
assert.strictEqual(level(95), '优秀');
assert.strictEqual(level(30), '不及格');
console.log('成绩分级:', [95, 75, 30].map(level).join('、'));

模板中的对应写法:

<script setup>
import { ref, computed } from 'vue';

const score = ref(88);
const avatar = ref('/avatar.png');
const level = computed(() =>
  score.value >= 90 ? '优秀' : score.value >= 60 ? '及格' : '不及格',
);
</script>

<template>
  <img :src="avatar" :alt="level" />
  <p>分数 {{ score }},评级:{{ level }}</p>
  <button :disabled="score < 60">允许进入下一章</button>
</template>

常见坑

  • 插值里写语句{{ if (ok) { ... } }} 直接编译错误;模板只接受单个表达式。
  • 把 v-html 用在用户输入上:等于把 XSS 请进门,要么不用,要么先经过消毒库(如 DOMPurify)。
  • v-bind 传类型翻车count="0" 传字符串、:count="0" 传数字,给组件传 prop 时尤其要注意。
  • v-show 当权限控制:v-show 只是 display:none,节点仍在 DOM 里,敏感内容必须 v-if(或后端不下发)。
  • 表达式里塞太多逻辑:三元套三元就该收进 computed 了,模板保持「一眼能读完」。

小结

插值放表达式、属性用冒号、事件用 @;v-html 只渲染可信内容;多根节点与 template 分组让结构更干净。下一章讲两个最常用的结构性指令:v-if 与 v-for。