Vue使用一种基于HTML的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的DOM上。所有的Vue模板都是语法层面合法的HTML,可以被符合规范的浏览器和HTML解析器解析。

template内部的HTML字符串,内部有一些Vue的特性,例如插值表达式、自定义属性和指令。
Vue提供一套模版编译系统来编译Vue的模版语法,解析流程如下: :::info 开发者写的template
➡️ 分析HTML字符串
➡️ 生成AST
➡️ 解析表达式、自定义属性、指令
➡️ 生成虚拟DOM
➡️ 解析为真实的DOM
➡️ render渲染到页面上 :::

为什么需要虚拟DOM呢?
假设我页面上现在有一个span元素

  1. <span>Vue2</span>
  1. const oDom = document.getElementsByTagname("span")[0];
  2. oDom.onclick = function(){
  3. oDom.innerText = "Vue2";
  4. }

综上,我们点击span标签的时候把内容更改为Vue2,但是我们页面上本来就是Vue2,所以不需要对内容进行更改。如果使用真实DOM进行对比会比较麻烦,如果使用了虚拟DOM对象的数据进去对比就方便了很多,所以虚拟DOM能将新的DOM和旧的DOM进行对比来判断数据是否发生了变化,来决定是否要更新视图。

除了使用template,Vue还支持render+h函数:

  1. import { createApp, h } from "vue";
  2. const App = {
  3. render() {
  4. return h("div", {},
  5. [
  6. h("h1", { class: "title" }, this.title),
  7. h("p", {}, "This is content")
  8. ]);
  9. },
  10. data() {
  11. return {
  12. title: "this is my title",
  13. };
  14. },
  15. };
  16. createApp(App).mount("#app");

插值表达式

插值表达式内只能书写JS表达式,不能书写语句、函数、声明等!

  1. const App = {
  2. template: `
  3. <h1 class="title">{{ title }}</h1>
  4. <p>{{ var a = 1 }}</p> 这是一个语句,而非表达式
  5. <p>{{ if (ok) { return message } }}</p> 条件控制也不支持
  6. `,
  7. data() {
  8. return {
  9. title: "this is my title",
  10. };
  11. },
  12. };

插值表达式的想法来源于Mustache八字胡。
GitHub - janl/mustache.js: Minimal templating with {{mustaches}} in JavaScript

我们可以利用这个库实现一个简单的案例:

  1. import mustache from "mustache";
  2. let data = {
  3. title: "This is My Title",
  4. };
  5. let html = mustache.render(`<h1>{{ title }}</h1>`, data);
  6. console.log(html); // <h1>This is My Title</h1>

以上案例我们利用Mustache就将{{}}内的数据进行了替换,但是**Vue**并没有使用**Mustache**库,只是灵感来源于它。
Mustache并不支持HTML书写的插值,Vue的底层有一套自己的模版编译系统,所以能支持Vue内置的属性。

Vue 的内置指令

Vue开发中,所有书写在Vue模版中v-*都是指令。为什么叫做指令呢?指令可以告诉模版应该按照什么样的逻辑进行渲染或绑定行为。
Vue提供了大量的内置指令,如v-ifv-showv-forv-html…同时Vue还支持我们给Vue拓展指令(也就是自定义指令)。

这里我们挑几个指令讲述一下使用时的注意点:

  • v-html,在插值中是不会解析HTML字符串的,因为插值是JS表达式,没有对DOM的操作。 ```vue
  1. 不要试图将`v-html`作为子模版的显示,因为`Vue`本身有一套底层的模版编译系统,而不是直接使用字符串进行渲染,这会导致语法无法被解析,你应该把子模版放到子组件中,让模版的重用和组合更加强大。
  2. ```vue
  3. <template>
  4. <!-- 不要这样做 -->
  5. <div v-html="child"></div>
  6. </template>
  7. <script>
  8. export default{
  9. data(){
  10. return{
  11. child: `<button @click="handleClickBtn">点击按钮</button>`
  12. }
  13. }
  14. }
  15. </script>

另外需要注意,v-html是动态渲染的HTML,使用的基本都是innerHTML属性进行赋值,innerHTML很容易导致XSS的攻击!!!

  1. let text = "<img src="123" onerror="alert(123)" />" // 图片加载失败将会执行 alert
  2. document.body.innerHTML = text;
  • v-once用于让template一次插值后,再也不更新,这个指令同时会影响到子组件!!! ```vue

  1. - `v-bind`用于动态绑定属性,布尔型的属性依据`true/false`值来决定`attribute`是否应该存在于该元素上。`disabled`就是最常见的例子之一。
  2. `isButtonDisabled `为真值或一个空字符串 (即`<button disabled="">`) 时,元素会包含这个`disabled attribute`。而当其为其他假值时`attribute`将被忽略。
  3. ```vue
  4. <button :disabled="isButtonDisabled">Button</button>

如果你有像这样的一个包含多个attributeJavaScript对象,通过不带参数的v-bind,你可以将它们绑定到单个元素上:

  1. <template>
  2. <div v-bind="objectOfAttrs"></div>
  3. <!-- 和下面的等效的 -->
  4. <div v-bind:id="objectOfAttrs.id" v-bind:class="objectOfAttrs.class"></div>
  5. </template>
  6. <script>
  7. export default{
  8. data(){
  9. return{
  10. objectOfAttrs:{
  11. id: 'container',
  12. class: 'wrapper'
  13. }
  14. }
  15. }
  16. }
  17. </script>