普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月31日首页

Vue2(三)——模板语法

2026年1月31日 17:18

v2.cn.vuejs.org/v2/guide/sy…

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 的**“门面”**。

  1. 声明式 (Declarative) vs 命令式 (Imperative):
    • 命令式(如 jQuery): 你需要一步步告诉浏览器怎么做(“找到 id 为 app 的 div,清空内容,插入一个 span,设置 span 的文字...”)。
    • 声明式(Vue): 你只告诉 Vue 你想要什么结果(“这里要显示 {{ message }}”),具体的脏活累活(DOM 操作)交给 Vue 去处理。
  1. 基于 HTML:
    • 这大大降低了学习门槛。设计师或后端开发人员也能看懂 Vue 代码,因为它长得就像普通的 HTML。
    • 这也意味着现有的 HTML 解析器都能处理它,不会像某些非标语法那样导致编辑器报错。

第二层:怎么变?—— 编译与虚拟 DOM (Compilation & VDOM)

原文: “在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。”

这讲的是 Vue 的**“转换过程”**。浏览器其实看不懂 v-if{{ }},所以 Vue 在代码运行前(或运行时)做了一次“翻译”。

  1. 编译 (Compile):

Vue 有一个编译器,它会把你的 HTML 模板字符串“翻译”成一段 JavaScript 代码。这段代码就叫 渲染函数 (Render Function)

  1. 虚拟 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 的**“超能力”**。

  1. 响应系统 (Reactivity):

Vue 会“监视”你的数据。当数据变化时,它不仅知道数据变了,还精确地知道哪个组件依赖了这个数据。

  1. Diff 算法 (最小化更新):

当数据变化,渲染函数会重新执行,生成新的虚拟 DOM 树。

Vue 会拿着 新树旧树 做对比(Diff)。

    • Vue 发现: “哦,只有这个 div class 变了,其他都没变。”
    • 结果:Vue 只去更新真实 DOM 里那个 divclass,其他不动。

这就是为什么 Vue 即使在处理庞大页面时依然很快的原因。


第四层:给高手的“后门” —— Render 函数 & JSX

原文: “如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量... 直接写渲染 (render) 函数...”

这段话是说,模板虽然好用,但有时候不够灵活。

  • 模板的局限: 比如你要写一个组件,根据 props 动态生成 h1h6 标签。用模板写,你可能得写 6 个 v-if
  • JS 的力量: 如果用渲染函数,你只需要写一行 JS:return h('h' + this.level, ...)

Vue 并不强迫你用模板,它完全支持你像 React 那样写代码(JSX),这给了高级开发者极大的灵活性。

总结分析

这段话其实揭示了 Vue 的架构分层

层次 作用 对应原文
顶层 (API) 易用性 基于 HTML 的模板,声明式绑定
中间层 (Compiler) 转化 模板 \rightarrow 渲染函数 \rightarrow 虚拟 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 的值是 nullundefinedfalse,则 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 } }}

模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 MathDate 。你不应该在模板表达式中试图访问用户定义的全局变量。

大白话就是指定访问 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>

Vue2(二)——创建一个Vue实例

2026年1月31日 10:15

Vue 实例学习笔记

来源:v2.cn.vuejs.org/v2/guide/in…

一、创建一个 Vue 实例

1.1 基本语法

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

var vm = new Vue({
  // 选项对象
})

关于变量名 vm:虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。

1.2 选项对象

当创建一个 Vue 实例时,你可以传入一个选项对象。这篇教程主要描述的就是如何使用这些选项来创建你想要的行为。

⭐ 选项对象是什么?

选项对象就是在 new Vue({ ... }) 时传入的那个大括号包裹的对象,包含各种配置项,例如:

var vm = new Vue({
  el: '#app',        // 选项1:挂载点
  data: {            // 选项2:数据
    message: 'Hello'
  },
  created: function() {  // 选项3:生命周期钩子
    console.log('实例已创建')
  }
})

在这个例子中,eldatacreated 都是选项 property(选项属性)


二、Vue 应用的结构(重点理解)

2.1 根实例 + 组件树

原文:"一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。"

这句话的意思是:

  1. 根 Vue 实例:每个 Vue 应用有且只有一个根实例,通过 new Vue() 创建
  2. 组件树:在这个根实例下面,可以嵌套多个子组件,这些组件形成树状结构

用一个形象的比喻:

  • 根 Vue 实例 = 树的(只有一个)
  • 组件树 = 树的枝叶(可以有很多层,可以重复使用)

2.2 组件树示例

一个 todo 应用的组件树可以是这样的:

根实例 (Root Vue Instance - 通过 new Vue() 创建)
 └─ TodoList (组件)
    ├─ TodoItem (组件)
    │  ├─ TodoButtonDelete (组件)
    │  └─ TodoButtonEdit (组件)
    └─ TodoListFooter (组件)
       ├─ TodosButtonClear (组件)
       └─ TodoListStatistics (组件)

2.3 代码示例

// 1. 创建根实例(唯一的入口)
var vm = new Vue({
  el: '#app',
  data: {
    todos: [...]
  }
})

// 2. 在这个根实例中,可以嵌套多个组件
// 每个组件也是 Vue 实例,但不是通过 new Vue() 创建,而是通过 Vue.component() 定义

2.4 重要概念

所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象(一些根实例特有的选项除外)。

  • 根实例:通过 new Vue() 创建
  • 子组件:通过 Vue.component() 或组件选项创建

三、数据与方法

3.1 响应式系统

当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生"响应",即匹配更新为新的值。

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})

// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

3.2 响应式的限制

只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。

如果你添加一个新的 property,比如:

vm.b = 'hi'

那么对 b 的改动将不会触发任何视图的更新。

解决方法:如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值:

data: {
  newTodoText: '',
  visitCount: 0,
  hideCompletedTodos: false,
  todos: [],
  error: null
}

3.3 使用 Object.freeze()

使用 Object.freeze() 会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj
})
<div id="app">
  <p>{{ foo }}</p>
  <!-- 这里的 `foo` 不会更新! -->
  <button v-on:click="foo = 'baz'">Change it</button>
</div>

3.4 实例 Property 与方法

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。

var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

四、实例生命周期钩子

4.1 什么是生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

4.2 常用的生命周期钩子

  • created:实例创建完成后调用
  • mounted:实例挂载到 DOM 后调用
  • updated:数据更新导致视图重新渲染后调用
  • destroyed:实例销毁后调用

示例:

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

4.3 ⭐ 避免使用箭头函数(重要)

不要在选项 property 或回调上使用箭头函数,比如:

// ❌ 错误写法
created: () => console.log(this.a)

vm.$watch('a', newValue => this.myMethod())

⭐ 什么是"选项 property"?

选项 property 就是在创建 Vue 实例时传入的选项对象的各个属性,例如:

new Vue({
  data: { ... },      // data 是一个选项 property
  created: function() {},  // created 是一个选项 property
  methods: { ... },   // methods 是一个选项 property
  computed: { ... }   // computed 是一个选项 property
})

为什么不能使用箭头函数?

因为箭头函数并没有 thisthis 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致以下错误:

  • Uncaught TypeError: Cannot read property of undefined
  • Uncaught TypeError: this.myMethod is not a function

✅ 正确写法

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // 使用普通函数,this 指向 Vue 实例
    console.log(this.a)
  },
  methods: {
    myMethod: function() {
      console.log(this.a)
    }
  }
})

// 或者使用简写形式(ES6)
new Vue({
  data: {
    a: 1
  },
  created() {
    console.log(this.a)
  },
  methods: {
    myMethod() {
      console.log(this.a)
    }
  }
})

简单记忆

  • 选项 property(如 datacreatedmethodscomputed)的值如果是函数,必须用普通函数,不能用箭头函数
  • 这样 this 才能正确指向 Vue 实例

五、生命周期图示

下图展示了实例的生命周期:

     创建 Vue 实例
          ↓
    beforeCreate(实例创建前)
          ↓
    created(实例创建后)← 可以在这里访问 data、methods
          ↓
    beforeMount(挂载前)
          ↓
    mounted(挂载后)← DOM 已经渲染完成
          ↓
      [运行中]
          ↓
    beforeUpdate(数据更新前)
          ↓
    updated(数据更新后)
          ↓
    beforeDestroy(销毁前)
          ↓
    destroyed(销毁后)

六、总结

6.1 核心概念

  1. 每个 Vue 应用通过 new Vue() 创建一个根实例
  2. 根实例下可以嵌套多个组件,形成组件树
  3. 所有 Vue 组件都是 Vue 实例

6.2 重要注意事项

  1. 响应式限制:只有在创建实例时存在于 data 中的 property 才是响应式的
  2. 避免箭头函数:在选项 property 中不要使用箭头函数,否则 this 指向会出错
  3. 实例 property:使用 $ 前缀的属性和方法,如 vm.$datavm.$elvm.$watch()

6.3 选项对象结构

new Vue({
  // 挂载点
  el: '#app',

  // 数据
  data: { ... },

  // 方法(不要用箭头函数)
  methods: {
    methodName() { ... }
  },

  // 生命周期钩子(不要用箭头函数)
  created() { ... },
  mounted() { ... },

  // 计算属性
  computed: { ... }
})
❌
❌