# 第3章 Vue 基础介绍
本章节相关代码存放在Github (opens new window)中。
本章我们主要介绍一些 Vue 里常用的概念、语法和 API。
其实 Vue 以入门门槛低、对初学者友好而收获了一致的好评,在这种情况下,其实最新、最完整的语法和 API,当然是从官网上搜索最合适。而本章内容则配合一些对使用的理解和生动的实例,来让大家对 Vue 有更深刻的认知。
# 3.1 Vue 实例
讲 Vue 的基础,当然得先从实例说起。要怎么理解实例两个字?在 Vue 里,可以理解为每一个通过用 Vue 函数创建的,都是一个 Vue 实例:
// new Vue返回一个Vue实例
var vm = new Vue({
// 选项
});
Vue 实例和 Vue 应用是什么关系呢?官方介绍:一个 Vue 应用由一个通过new Vue()
创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。所以 Vue 实例是属于 Vue 应用的一部分,与组件树组成了 Vue 应用:
// 一个Vue应用,由根实例+组件树组成
根实例
└─ 根组件 // 此行开始,为组件树
├─ 组件1
│ ├─ 组件1-1
│ └─ 组件1-2
└─ 组件2
├─ 组件2-1
└─ 组件2-2
那组件又是什么呢?关于组件的介绍和使用,会在下一章介绍。
# 3.1.1 生命周期钩子
生命周期要怎么理解呢?结合前言中浏览器页面渲染的机制,以及《第1章 Vue 框架介绍》中 Vue 模板的解析和渲染的过程,我们知道在 Vue 中要渲染一块页面内容的时候,会有这么几个过程:
(1) 解析语法生成 AST。
(2) 根据 AST 结果,完成 data 数据初始化。
(3) 根据 AST 结果和 data 数据绑定情况,生成虚拟 DOM。
(4) 将虚拟 DOM 生成真正的 DOM 插入到页面中,此时页面会被渲染。
当我们绑定的数据进行更新的时候,又会产生以下这些过程:
(5) 框架接收到数据变更的事件,根据数据生成新的虚拟 DOM 树。比较新旧两棵虚拟 DOM 树,得到差异。
(6) 把差异应用到真正的 DOM 树上,即根据差异来更新页面内容。
当我们清空页面内容时,还有:
(7) 注销实例,清空页面内容,移除绑定事件、监听器等。
所以在整个页面或是某块页面内容(组件)中,Vue 提供了以下的一些关键的生命周期钩子:
表 3-1 Vue 生命周期说明
生命周期钩子 | 说明 | 对应上述步骤 |
---|---|---|
beforeCreate | 初始化实例前,data 、methods 等不可获取 | 1 之后,2 之前 |
created | 实例初始化完成,此时可获取data 里数据和methods 事件,无法获取 DOM | 2 之后,3 之前 |
beforeMount | 虚拟 DOM 创建完成,此时未挂载到页面中,vm.$el 可获取未挂载模板 | 3 之后,4 之前 |
mounted | 数据绑定完成,真实 DOM 已挂载到页面,vm.$el 可获取真实 DOM | 4 之后 |
beforeUpdate | 数据更新,DOM Diff 得到差异,未更新到页面 | 5 之后,6 之前 |
updated | 数据更新,页面也已更新 | 6 之后 |
beforeDestroy | 实例销毁前 | 7 之前 |
destroyed | 实例销毁完成 | 7 之后 |
我们可以更新官方的生命周期图如下:
图 3-1 Vue 生命周期说明补充前半部分
图 3-2 Vue 生命周期说明补充后半部分
生命周期钩子的使用方式也很简单,我们可以根据需要在特定的生命周期钩子里进行一些处理:
<div id="app"></div>
<script>
new Vue({
el: "#app",
template: "<div>{{ message }}</div>",
data() {
return {
message: "欢迎来到Vue的世界"
};
},
methods: {
test() {}
},
beforeCreate: function() {
// 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
console.log("beforeCreate", this.message, this.test, this.$el);
},
created: function() {
// 在实例创建完成后被立即调用
// 在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调
// 挂载阶段还没开始,$el 属性目前不可见
console.log("created", this.message, this.test, this.$el);
},
beforeMount: function() {
// 在挂载开始之前被调用:相关的 render 函数首次被调用
console.log("beforeMount", this.message, this.test, this.$el);
},
mounted: function() {
// el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
// 如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内
// mounted 不会承诺所有的子组件也都一起被挂载
// 如果希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted
console.log("mounted", this.message, this.test, this.$el);
this.$nextTick(function() {
// 此处整个视图已渲染完毕
});
},
beforeUpdate: function() {
// 数据更新时调用,发生在虚拟 DOM 打补丁之前
// 这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器
},
updated: function() {
// 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子
// 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作
},
beforeDestroy: function() {
// 实例销毁之前调用。在这一步,实例仍然完全可用
},
destroyed: function() {
// Vue 实例销毁后调用
// 调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁
}
});
</script>
页面中我们可以看到输出结果,验证了几个生命周期的data
、methods
、DOM 挂载等情况:
图 3-3 生命周期测试输出
# 3.1.2 Vue 实例基本选项说明
Vue 实例提供了非常丰富的选项(new Vue()
时传入的选项),除了上面介绍的生命周期之外,最常见的大概是这几个了:
表 3-2 Vue 实例常用选项
选项名 | 说明 | 类型 |
---|---|---|
el | 通过 CSS 选择器或者 HTMLElement 实例的方式,提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标 | string /Element |
template | 字符串模板,将会替换挂载的元素 | string |
render | 字符串模板的代替方案,该渲染函数接收一个createElement 方法作为第一个参数用来创建 VNode | (createElement: () => VNode) => VNode |
data | Vue 实例的数据对象,用于数据绑定 | Object /Function 组件只支持 Function |
props | 用于接收来自父组件的数据 | Array<string> /Object |
methods | Vue 实例的事件,可用于事件绑定 | { [key: string]: Function } |
computed | 计算属性,用于简化模板的复杂数据计算 | { [key: string]: Function or { get: Function, set: Function } } |
watch | 观察 Vue 实例变化的一个表达式或计算属性函数 | { [key: string]: string or Function or Object or Array } |
directives | 自定义指令 | Object |
filters | 过滤器 | Object |
components | 组件 | Object |
Vue 实例中的这些选项,大多数都可以作为全局实例属性来获取或者访问:
const vm = new Vue({
// ...一些选项
});
vm.$data; // 获取 data
vm.$props; // 获取 props
vm.$el; // 获取挂载元素
vm.$options; // 获取 Vue 实例的初始选项
vm.$parent; // 获取父实例
vm.$root; // 获取根实例
vm.$children; // 获取当前实例的直接子组件
vm.$refs; // 获取持有注册过 ref 特性 的所有 DOM 元素和组件实例
vm.$watch; // 观察 Vue 实例变化的一个表达式或计算属性函数
vm.$set; // 向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新
vm.$delete; // 删除对象的属性。如果对象是响应式的,确保删除能触发更新视图
这些内容,我们将在本章以及后面的几章里一一讲述。更完整的选项内容和 API,各位可以去官网查询。
# 3.1.3 DOM 相关选项
既然要讲 Vue 实例,那就从最基本的 DOM 渲染相关开始讲起。
# el
Vue 实例中,el
选项提供一个页面中已存在的 DOM 元素作为实例的挂载目标。挂载的意思是,在选中的该元素所在的位置进行页面渲染,该元素会被替换成需要渲染的页面内容。我们可以传入一个 CSS 选择器,也可以传入一个 DOM 元素。例如,页面中有一个 id 为#app 的元素,如果我们希望以<div id="app"></div>
该元素作为 Vue 实例的挂载目标,以下方式都是可以的:
new Vue({
// 1. 传入 Element 元素方式一
el: document.getElementById("app"),
// 2. 传入 Element 元素方式二
el: document.getElementsByTagName("div")[0],
// 3. 传入 CSS 选择器方式一
el: "#app",
// 4. 传入 CSS 选择器方式二
// 最好选择唯一的元素,不推荐该方式
el: "div"
});
所谓挂载元素,在实例挂载之后,元素可以用vm.$el
访问。当然,前面生命周期中我们讲了,需要在mounted
之后才能获取到:
new Vue({
el: "#app",
template: "<div>{{ message }}</div>",
data() {
return {
message: "欢迎来到Vue的世界"
};
},
mounted() {
console.log(this.$el);
}
});
如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用vm.$mount()
手动开启编译,也就是这样:
const vm = new Vue({
template: "<div>{{ message }}</div>",
data() {
return {
message: "欢迎来到Vue的世界"
};
},
mounted() {
console.log(this.$el);
}
});
// 需要的时候使用
vm.$mount("#app");
如果 render 函数和 template 属性都不存在,挂载 DOM 元素的 HTML 会被提取出来用作模板,此时,必须使用 Runtime + Compiler 构建的 Vue 库。我们来理解下这句话:
- 编译器(Compiler): 我们可以理解为用来将模板字符串编译成为 JavaScript 渲染函数的代码,也就是第1章内容中的 AST 解析部分
- 运行时(Runtime): 用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码,也就是第1章中 AST 以外的部分
一般来说,我们在部署生产代码的时候,已经将需要的代码解析编译好,这个时候我们只需要运行时的 Vue 库。在特殊情况下,有需要运行时解析编译的情况,如在线平台编写代码的时候,我们需要引入编译器的 Vue 库,该部分占完整版(Runtime + Compiler)的 30%。上面说到,如果我们需要使用挂载 DOM 元素的 HTML 作为模板,则需要运行时编译,也就需要编译器了。
那关于 render 函数和 template 属性的选项,我们来看一下。
# template
给 Vue 实例提供字符串模板,该模板将会替换挂载的元素,我们来看一个简单的代码片段:
<body>
<div id="app"></div>
<script>
new Vue({
el: "#app",
template: "<p>{{ message }}</p>",
data() {
return {
message: "欢迎来到Vue的世界"
};
},
beforeMount() {
console.log("beforeMount", this.$el);
},
mounted: function() {
console.log("mounted", this.$el);
}
});
</script>
</body>
这里挂载的元素指的是<div id="app"></div>
,当我们使用了 template 选项之后,我们在页面中可以看到最终页面中的内容是 template 中的内容,此时<div id="app"></div>
已经被替换成 template 中的<p></p>
,并将 message 中的内容替换成绑定的数据了:
图 3-4 挂载元素最终页面效果
我们也能看到,在beforeMount
生命周期中,vm.$el
获取的是挂载的元素模板,而在mounted
生命周期后则变成了 template 中的真实 DOM 元素:
图 3-5 挂载元素不同生命周期效果
如果 Vue 选项中包含 render 渲染函数,则 template 将被忽略,我们来看看渲染函数。
# render
字符串模板 template 的代替方案,该渲染函数接收一个createElement
方法作为第一个参数用来创建 VNode。第1章中我们有讲到 Vue 里使用了虚拟 DOM,而createElement
创建的便是虚拟 DOM,在 Vue 里称为 VNode。要怎么用呢,例如我们可以实现一个v-if
的能力:
new Vue({
// 该段实现:
// <p v-if="condition">condition work!</p>
// <p v-else>condition not work!</p>
render: function (createElement) {
if (this.condition) {
return createElement('p', "condition work!")
} else {
return createElement('p', 'condition not work!')
}
}
// 该段实现:
// <ul><li v-for="item in items">{{item}}</li></ul>
render: function (createElement) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item)
}))
}
});
一般来说,我们可以结合 JSX 来使用(需要添加 Babel 插件噢):
import MyComponent from "./MyComponent.vue";
new Vue({
render: function(h) {
return (
<MyComponent>
<p>Hello world!</p>
</MyComponent>
);
}
});
# 3.2 常用模板语法
关于 Vue 实例的选项,上面讲了最基本的生命周期钩子,以及 DOM 渲染相关的。而数据相关的,或许要结合模板语法来一起讲,会更好理解。
关于数据绑定、事件绑定,其实也是一种 Vue 提供的方便开发者使用的模板语法。在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。下面我们会结合对语法的理解,来介绍它们的使用。
# 3.2.1 数据绑定
数据绑定在 Vue 里有最基础的几种方式:
表 3-3 Vue 中数据绑定的常用方式
语法 | 说明 |
---|---|
插值语法{{}} | 文本插值,可配合过 Javascript 表达式和过滤器使用 |
v-once | 一次性插值,数据改变时插值处的内容不会更新 |
v-html | 可输出真正的 HTML,不会被转义为普通文本 |
v-bind: (简写: ) | 可用于绑定 DOM 属性、或一个组件 prop 到表达式 |
# 插值
我们来简单看看插值相关的绑定语法:
<template>
<div>{{ message }}</div>
<div v-once>{{ message }}</div>
<div v-html="message"></div>
<div>{{ msgHtml }}</div>
<div v-html="msgHtml"></div>
</template>
<script>
export default {
data() {
return {
message: "欢迎来到Vue的世界",
msgHtml: "<p style='color: red'>欢迎来到红色Vue的世界</p>"
};
},
created() {
this.message = "啦啦啦啦啦啦";
},
mounted() {
this.message = "略略略略略";
}
};
</script>
虽然比较简单,但有意思的地方在于,你能猜到最终的真实 DOM 渲染是这样的吗:
图 3-5 Vue 中数据绑定渲染结果
我们知道,v-once
只渲染元素和组件一次,后面的所有重新渲染过程,被绑定的该元素/组件及其所有的子节点将被视为静态内容并跳过。但在这里,最终呈现的并不是我们初始data
中的“欢迎来到 Vue 的世界”,而是created
周期中设定的“啦啦啦啦啦啦”。
前面讲 Vue 生命周期钩子的时候我们知道,真实 DOM 挂载发生在beforeMount
之后、mounted
之前,也就是说,我们在mounted
之前的生命周期中更改data
中 message 的值,都是有效的。
所以关于插值,需要注意以下两点:
(1) v-once
在mounted
生命周期之后,不可再更改。
(2) v-html
请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值,因为它很容易导致 XSS 攻击(该内容在第1章有阐述)。
# Javascript 表达式
Vue 中支持完全的 Javascript 表达式(请注意不是语句噢),因为我们可以直接在模板中做很多数据的处理和逻辑判断:
{{ price * 100 + 2000 }} {{ message.split('').reverse().join('') }} {{ type ===
'group' ? '团队' : '个人' }}
<div v-bind:class="isActived ? 'actived' : ''"></div>
<div v-bind:index="'item-' + index"></div>
<!-- 以下是语句,不是表达式 -->
{{ var message = "123" }} {{ if(type === 'group') { return '团队' } }}
# v-bind
其实v-bind
也是插值的一种,只不过它的使用方式更加灵活和多变,所以这里单独进行介绍,我们来看看以下的用法:
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc" />
<!-- 缩写 -->
<img :src="imageSrc" />
<!-- 最终会生成 `<img src="${imageSrc}">` 这样的模板 -->
<!-- 动态特性名 (2.6.0+) -->
<button v-bind:[key]="value"></button>
<!-- 动态特性名缩写 (2.6.0+) -->
<button :[key]="value"></button>
<!-- 最终会生成 `<button ${key}="${value}">` 这样的模板 -->
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName" />
<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">
<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>
<!-- 绑定一个有属性的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
</div>
除了 style 绑定、class 绑定以及一些常用属性 src 等绑定在日常开发中会使用较多外,还有一个重要的 prop 绑定,用于向子组件传递数据,例如我们有这么一个组件:
Vue.component("my-text", {
props: ["text"],
template: "<p>{{ text }}</p>"
});
则可以通过<my-text :text="myText"></my-text>
的方式使用。有关组件、子组件的内容,会在后面章节详细讲述,这里就不多再说了。
# data
data
是 Vue 实例的数据对象,是上述所有数据绑定的数据来源。
var vm = new Vue({
// 1. 接受返回对象的函数
data() {
return {
message: "欢迎来到Vue的世界"
};
},
// 2. 也可以直接棒对象
data: {
message: "欢迎来到Vue的世界"
}
});
我们已经知道,Vue 里数据的变更检测是来自于 getter/setter,从而让data
的属性能够响应数据变化。前面我们也讲到,Vue 将遍历 data 选项的 JavaScript 对象所有的属性,并使用Object.defineProperty
把这些属性全部转为 getter/setter:
// 响应式的变更检测
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// getter
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
// 依赖检测
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value;
},
// setter,最终更新后会通知噢
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
if (newVal === value || (newVal !== newVal && value !== value)) {
return;
}
if (process.env.NODE_ENV !== "production" && customSetter) {
customSetter();
}
if (getter && !setter) return;
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
// 会通知噢
dep.notify();
}
});
由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。换句话说,只有当实例被创建时就已经存在于data
中的属性才是响应式的(新增的属性等都不会触发视图的更新)。问题也是很显然,Vue 无法检测到对象属性的添加或删除,也无法检测一些特殊的数组变动:
// 只有这些操作会通知变更噢
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
// 拦截上述这些操作方法,然后通知变更
methodsToPatch.forEach(function(method) {
// cache original method
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args);
const ob = this.__ob__;
let inserted;
switch (method) {
case "push":
case "unshift":
inserted = args;
break;
case "splice":
inserted = args.slice(2);
break;
}
if (inserted) ob.observeArray(inserted);
// notify change
ob.dep.notify();
return result;
});
});
所以在更新data
中绑定的对象或者数组的时候需要注意,除了使用可触发变更检测的特殊方法之外,也可以使用vm.$set
(Vue.set
)实例方法。vm.$set
(Vue.set
)用于向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新:
// 更新数组
vm.$set(vm.items, indexOfItem, newValue);
// 更新对象
vm.$set(vm.someObject, keyOfObject, newValue);
# 3.2.2 过滤器
Vue 中可以自定义过滤器,可被用于一些常见的文本格式化,支持全局定义和组件中定义。
// 全局定义
// 千分位处理
Vue.filter('thousandth', function (value) {
if (!value) return '';
return value && value.toString().replace(/^(-?\d+?)((?:\d{3})+)(?=\.\d+$|$)/, function (all, pre, groupOf3Digital) {
return pre + groupOf3Digital.replace(/\d{3}/g, ',$&')
});
})
// 局部定义
// 乘以倍数
filters: {
multiply: function (value, times) {
if (!value) return '';
return value * ( parseInt(times) || 1);
}
}
过滤器可以用在两个地方:双花括号插值和v-bind
表达式。
<!-- 在双花括号中 -->
{{ number | thousandth }}
<!-- 在 `v-bind` 中 -->
<div v-bind:text="number | thousandth"></div>
<!-- 支持串联,可接受传参 -->
{{ number | multiply(1000) | thousandth }}
# 3.2.3 事件绑定
可以用v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码,可用@
缩写。
<template>
<button v-on:click="addCounter">Add 1</button>
<!-- 以下为缩写 -->
<button @click="addCounter">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</template>
<script>
export default {
data() {
return {
counter: 0
};
},
methods: {
addCounter() {
this.counter += 1;
}
}
};
</script>
# 事件修饰符
Vue 为v-on
提供了事件修饰符:
表 3-4 v-on
事件修饰符
修饰符 | 说明 |
---|---|
.stop | event.stopPropagation() ,阻止事件继续传播 |
.prevent | event.preventDefault() ,阻止默认事件 |
.capture | 添加事件监听器时使用事件捕获模式 |
.once | 只绑定一次 |
.enter /.tab /.esc /.space /.ctrl /.[keyCode] | 按键修饰符 |
使用方式很简单,在绑定事件后面加上修饰符就可以:
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- Alt + C -->
<input @keyup.alt.67="clear" />
# 3.3 计算属性和侦听器
computed
和watch
大概是 Vue 里除了data
、methods
、props
之外,用的最多的选项了。前面我们也讲了很多次,Vue 中数据变更之所以能更新到页面里,因为对data
执行 getter/setter 转化,然后进行侦听。而computed
和watch
属性都是基于此,我们看看一个Watcher
是怎样的:
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
// 初始化
}
// 计算getter,重新收集依赖
get () { //... }
// 添加依赖
addDep (dep: Dep) { //... }
// 清理依赖集合
cleanupDeps () { //... }
// 订阅接口,将在依赖项更新时调用
update () { //... }
// 调用接口,将被调配者调用
run () { //... }
// 该watcher的所有依赖
depend () { //... }
// 从所有依赖项的订户列表中删除自己
teardown () { //... }
}
计算属性和侦听器都离不开数据监听。
# 3.3.1 computed
有时候我们需要对绑定的数据进行一些处理,通常使用前面说过的插值的 Javascript 表达式、过滤器都可以处理完毕,但有些时候需要稍微复杂一点的逻辑计算,这种情况我们可以使用计算属性。计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算,计算属性支持读取设置:
var vm = new Vue({
data: { a: 1 },
computed: {
// 仅读取
aDouble: function() {
return this.a * 2;
},
// 读取和设置
aPlus: {
get: function() {
return this.a + 1;
},
set: function(v) {
this.a = v - 1;
}
}
}
});
vm.aPlus; // => 2
vm.aPlus = 3;
vm.a; // => 2
vm.aDouble; // => 4
# 3.3.2 watch
侦听器用来监听某些数据变化,观察 Vue 实例变化的一个表达式或计算属性函数。Vue 实例将会在实例化时调用$watch()
,遍历 watch 对象的每一个属性。watch
支持的方式有好几种,回调函数得到的参数为新值和旧值:
var vm = new Vue({
data: {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
},
watch: {
a: function(val, oldVal) {
console.log("new: %s, old: %s", val, oldVal);
},
// 方法名
b: "someMethod",
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function(val, oldVal) {
/* ... */
},
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: "someMethod",
immediate: true
},
e: [
"handle1",
function handle2(val, oldVal) {
/* ... */
},
{
handler: function handle3(val, oldVal) {
/* ... */
}
/* ... */
}
],
// watch vm.e.f's value: {g: 5}
"e.f": function(val, oldVal) {
/* ... */
}
},
methods: {
someMethod() {}
}
});
vm.a = 2; // => new: 2, old: 1
本章内容主要介绍了 Vue 实例,以及一些常用的选项和模板语法,相信大家现在已经能理解一个 Vue 实例的生命周期,以及理解一些基本语法。后面我们会基于这些基础内容,来慢慢介绍组件、指令、动画相关,以及搭配一些其他开源库来完善项目,一步步走进 Vue 的世界里。