Vue2(三)——模板语法
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染 (render) 函数,使用可选的 JSX 语法。
上面这段原封不动,来自官方,我比较菜,看不懂,下面AI的分析
第一层:写什么?—— 声明式模板 (Declarative Templates)
原文: “Vue.js 使用了基于 HTML 的模板语法... 声明式地将 DOM 绑定至数据... 所有模板都是合法的 HTML。”
这讲的是 Vue 的**“门面”**。
- 声明式 (Declarative) vs 命令式 (Imperative):
-
- 命令式(如 jQuery): 你需要一步步告诉浏览器怎么做(“找到 id 为 app 的 div,清空内容,插入一个 span,设置 span 的文字...”)。
-
声明式(Vue): 你只告诉 Vue 你想要什么结果(“这里要显示
{{ message }}”),具体的脏活累活(DOM 操作)交给 Vue 去处理。
- 基于 HTML:
-
- 这大大降低了学习门槛。设计师或后端开发人员也能看懂 Vue 代码,因为它长得就像普通的 HTML。
- 这也意味着现有的 HTML 解析器都能处理它,不会像某些非标语法那样导致编辑器报错。
第二层:怎么变?—— 编译与虚拟 DOM (Compilation & VDOM)
原文: “在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。”
这讲的是 Vue 的**“转换过程”**。浏览器其实看不懂 v-if 或 {{ }},所以 Vue 在代码运行前(或运行时)做了一次“翻译”。
- 编译 (Compile):
Vue 有一个编译器,它会把你的 HTML 模板字符串“翻译”成一段 JavaScript 代码。这段代码就叫 渲染函数 (Render Function) 。
- 虚拟 DOM (Virtual DOM):
渲染函数执行后,不会直接去动真的 DOM(因为操作真 DOM 很慢),而是生成一个 JavaScript 对象树,这个对象树就是虚拟 DOM。它就像是真实 DOM 的一份“轻量级蓝图”。
举个例子:
- 你的模板:
HTML
<div :id="dynamicId">Hello</div>
- Vue 编译后的渲染函数 (伪代码):
JavaScript
function render() {
return h('div', { id: this.dynamicId }, 'Hello')
}
第三层:怎么跑?—— 响应式与智能更新 (Reactivity & Diffing)
原文: “结合响应系统,Vue 能够智能地计算出... 把 DOM 操作次数减到最少。”
这讲的是 Vue 的**“超能力”**。
- 响应系统 (Reactivity):
Vue 会“监视”你的数据。当数据变化时,它不仅知道数据变了,还精确地知道哪个组件依赖了这个数据。
- Diff 算法 (最小化更新):
当数据变化,渲染函数会重新执行,生成新的虚拟 DOM 树。
Vue 会拿着 新树 和 旧树 做对比(Diff)。
-
- Vue 发现: “哦,只有这个
div的class变了,其他都没变。” - 结果:Vue 只去更新真实 DOM 里那个
div的class,其他不动。
- Vue 发现: “哦,只有这个
这就是为什么 Vue 即使在处理庞大页面时依然很快的原因。
第四层:给高手的“后门” —— Render 函数 & JSX
原文: “如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量... 直接写渲染 (render) 函数...”
这段话是说,模板虽然好用,但有时候不够灵活。
-
模板的局限: 比如你要写一个组件,根据 props 动态生成
h1到h6标签。用模板写,你可能得写 6 个v-if。 -
JS 的力量: 如果用渲染函数,你只需要写一行 JS:
return h('h' + this.level, ...)。
Vue 并不强迫你用模板,它完全支持你像 React 那样写代码(JSX),这给了高级开发者极大的灵活性。
总结分析
这段话其实揭示了 Vue 的架构分层:
| 层次 | 作用 | 对应原文 |
|---|---|---|
| 顶层 (API) | 易用性 | 基于 HTML 的模板,声明式绑定 |
| 中间层 (Compiler) | 转化 | 模板 渲染函数 虚拟 DOM |
| 底层 (Engine) | 性能 | 响应式系统 + 智能 Diff 计算 |
| 扩展层 (Flexibility) | 灵活性 | 可选 JSX / Render 函数 |
插值
文本
双大括号 Mustache
<span>Message: {{ msg }}</span>
Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。
可以使用v-once ,只执行一次插值,后面值变化不会更新
<span v-once>这个将不会改变: {{ msg }}</span>
原始HTML
v-html
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
整个span 的内容 会被替换成 property 值 rawHtml直接作为 HTML
注意:XSS 攻击,并且只支持常规html,不支持解析自定义组件
Attribute
v-bind 执行 绑定 Attribute
<div v-bind:id="dynamicId"></div>
注意与 HTML的不同 ( https://juejin.cn/post/7598447519823101993 里面的布尔属性提到了)
对于布尔 attribute (它们只要存在就意味着值为 true),v-bind 有不同,在这个例子中:
<button v-bind:disabled="isButtonDisabled">Button</button>
如果 isButtonDisabled 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。
使用 JavaScript 表达式
插值模板还支持三目运算符和数学表达式,以及库函数操作
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
错误例子
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
大白话就是指定访问 new Vue({}) 时传入的对象里面的 data,methods computed等,以及库函数,外部定义的变量,只要没传入 new Vue({}) 里面就不能访问
指令
指令 (Directives) 是带有 v- 前缀的特殊 attribute。
指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。
当表达式的值改变时,将影响DOM
即 Model 影响 -> DOM
<p v-if="seen">现在你看到我了</p>
v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素
参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。
例如,v-bind 指令可以用于响应式地更新 HTML attribute:
<a v-bind:href="url">...</a>
在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">...</a>
在这里参数是监听的事件名。我们也会更详细地讨论事件处理。
动态参数
用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data property attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href。
使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
在这个示例中,当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus。
动态参数值的约束
- 预期是求出一个字符串
- 异常情况为null,null表示移除该绑定
- 动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。
<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>
使用没有空格或引号的表达式,或者计算属性替代。
在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>
例子如下
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<a v-bind:[someAttr]="value">掘金</a>
</div>
</body>
<script>
var app = new Vue({
el: "#app",
data: {
value: "https://https://juejin.cn/",
someAttr: "href",
},
});
</script>
</html>
![]()
![]()
修改之后的代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<a v-bind:[someAttr]="value">掘金</a>
</div>
</body>
<script>
var app = new Vue({
el: "#app",
data: {
value: "https://https://juejin.cn/",
someattr: "href",
},
});
</script>
</html>
![]()
![]()
修饰符
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀
可以用于阻止事件冒泡或者阻止表单提交
例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit">...</form>
缩写
缩写不是必须的
v-bind 缩写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
v-on 缩写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>