Vue.js的核心是通过基于HTML的模板语法声明式地将数据绑定到DOM结构中,即通过模板将数据显示在页面上,如下所示。

<div id="container">{{content}}</div>
<script>
var vm = new Vue({ 
el: "#container",
data: {  
content: "strick" 
}
});
</script>

  其中<div>元素的内容是一个模板的插值,vm是一个Vue实例。

一、实例

  如果要使用Vue的功能,那么需要通过Vue()构造函数创建一个Vue实例,而Vue实例相当于MVVM模式中的ViewModel。注意,所有的Vue组件(后面篇章将会分析)都是Vue实例。

1)选项对象

  Vue的构造函数能接收一个选项对象,包含数据、计算属性、方法、模板、生命周期钩子等成员。上面代码中的el是Vue实例的挂载目标,既可以是CSS选择器,也可以是DOM元素;data是Vue实例的数据对象,其属性会被加到Vue的响应式系统中,当修改data的属性时,视图会响应变更而重新渲染,即vm实例的对应属性也会更新,反之亦然,如下所示。

var data = {
 content: "strick"
};
var vm = new Vue({
 el: "#container",
 data: data
});
data.content = "freedom";
console.log(vm.content); //"freedom" vm.content = "justify";
console.log(data.content); //"justify"

  注意,如果data属性使用了箭头函数,那么this不会指向vm实例。

  在实例被创建之后,就能通过vm.$data访问原来的数据对象,而vm.content是vm.$data.content的简写。注意,被冻结后的对象(即调用了Object.freeze()方法),其属性是无法响应式的。

  除了$data属性之外,Vue实例还提供了很多其它的属性和方法,它们都会以“$”符号为前缀,而为了避免与内置的冲突,Vue实例不会代理以“_”或“$”开头的用户自定义的属性和方法。

2)生命周期

  Vue实例的生命周期包括初始化数据、编译模板、挂载、渲染、更新和销毁等,每个阶段都存在对应的钩子,以便执行相关的业务逻辑。由于生命周期钩子都会自动把this和实例绑定在一起,因此不要用箭头函数来声明钩子。

  常用的8个生命周期可分为4组(如下所列),每组有一个名称带before前缀,顾名思义,先于另一个钩子执行,图1描绘了实例的生命周期。

图1  Vue实例的生命周期

  (1)beforeCreate:实例初始化之后回调,无法访问data、methods、computed等之中的数据或方法。

  (2)created:实例创建完成后回调,可访问data、methods、computed等之中的数据或方法,由于还未挂载到DOM中,因此不能成功读取$el。

  (3)beforeMount:实例挂载之前回调,将要使用的模板编译成render()函数。

  (4)mounted:实例挂载到DOM后回调,已替换模板中的插值,可获取el中的DOM元素,但要注意,不能保证其子组件也已被挂载。

  (5)beforeUpdate:数据更新时回调,发生在虚拟DOM之前,可操作现有DOM元素,例如移除其事件监听器等。

  (6)updated:DOM重新渲染后回调,可执行依赖于DOM的操作,但要在此期间尽量不要更改状态,以免陷入死循环,并且不能保证其子组件也已被重绘。

  (7)beforeDestroy:实例销毁之前回调,此时实例还存在,this仍然能指向它。

  (8)destroyed:实例销毁后回调,会解除数据绑定、移除事件、销毁子组件等。

  除了这8个钩子之外,还有3个钩子,如下所列。

  (1)activated:<keep-alive>元素激活时回调。

  (2)deactivated:<keep-alive>元素停用时回调。

  (3)errorCaptured:捕获到后代组件的错误时回调。

二、模板语法

  Vue的模板是一段特殊的HTML代码,其语法包括插值、指令和修饰符。虽然Vue的模板语法非常简洁,但是在内部Vue会进行一系列操作,例如将模板编译成虚拟DOM的渲染函数render(),结合响应系统最大程度的优化DOM操作次数以及用最少的代价渲染组件等。

1)插值

  Vue会以插值的方式将数据传递给模板,而插值可以是文本、HTML代码、特性和表达式。

  (1)文本插值是最常见的数据绑定形式,其写法与Mustache中的占位符类似,也需要用两个花括号包裹数据。当和v-once指令配合时,能实现单次插值,即阻止数据变化时的视图更新。如下代码所示,在修改数据对象的text属性后,两个<p>元素所生成的内容会有所不同,具体可参考对应的注释。

<div id="container">
<!-- <p>strick</p> -->
<p>{{text}}</p>
<!-- <p>text</p> -->
<p v-once>{{text}}</p>
</div>
<script>
var data = { 
text: "text"
};
var vm = new Vue({ 
el: "#container",
data: data
});
data.text = "strick";
</script>

  (2)由于模板占位符中的数据会被解释成普通文本(为了预防XSS攻击),因此如果要输出HTML代码,需要使用v-html指令。如下代码所示,第一个<p>元素在输出HTML标签时,它的两个特殊字符都被转义了。

<div id="container">
<!-- <p>&lt;span&gt;content&lt;/span&gt;</p> -->
<p>{{html}}</p>
<!-- <p><span>content</span></p> -->
<p v-html="html"></p>
</div>
<script>
var vm = new Vue({ 
el: "#container",
data: {
html: "<span>content</span>"
}
});
</script>

  (3)如果要将数据对象的属性值插到DOM元素的特性(即定义在HTML标签中的标准或非标准属性)中,那么得使用v-bind指令,如下代码所示。注意,当属性值为null、undefined或false时,相应的特性不会被输出到元素中。

<div id="container">
<!-- <p id="row"></p> -->
<p v-bind:id="id"></p>
</div>
<script>
var vm = new Vue({ 
el: "#container",
data: {
id: "row"
}
});
</script>

  (4)模板占位符还支持表达式运算,如下代码所示。注意,语句是不被允许的,并且在表达式中,只能访问白名单里的全局变量,例如Math和Date。

<div id="container">
<!-- <p>success</p> -->
<p>{{result ? "success" : "failure"}}</p>
</div>
<script>
var vm = new Vue({ 
el: "#container",
data: {
result: true
}
});
</script>

2)指令

  Vue中的指令(Directives)是一组以“v-”为前缀的DOM元素特性,它能接收一个表达式或参数。其职责是告知Vue如何处理提供给它的数据,并且当表达式的结果发生变化时,将其产生的影响反映到DOM上。

  指令和参数之间会用冒号隔开,例如前文用于更新DOM元素特性的v-bind。还有一个常用的v-on指令,用于监听事件,如下所示,其中click是事件类型,dot是事件处理程序。

<button v-on:click="dot">提交</button>

  Vue为v-bind和v-on两个指令提供了专用的缩写(如下所示),分别用“:”和“@”符号表示。

<!-- v-bind的缩写 -->
<p :id="id"></p>
<!-- v-on的缩写 -->
<button @click="dot">提交</button>

  从Vue 2.6.0开始,引入了动态参数的概念,在冒号后面跟一个用方括号包裹的表达式,如下所示,其中type是数据对象的属性,其值会作为参数来使用。

<button v-on:[type]="dot">提交</button>

  动态参数中的表达式会有一些语法约束,例如运算结果得是字符串类型、不能包含空格和引号、避免驼峰方式的变量命名,如下所示。

<button v-on:[1234567]="dot">提交</button>
<button v-on:[type + ""]="dot">提交</button>
<button v-on:[eventType]="dot">提交</button>

  在DOM中使用模板时,eventType会被强制转换成全小写的eventtype,从而就无法在数据对象中读取到它的值了。

3)修饰符

  Vue的修饰符(Modifier)是一种以“.”开头的特殊后缀,能让指令完成某种特殊行为,例如用.prevent修饰符取消默认操作,即调用事件对象的preventDefault()方法,如下所示。

<form v-on:submit.prevent="dot"></form>

三、过滤器

  过滤器可用来格式化模板中的文本,存在于占位符和v-bind指令中,紧跟在表达式之后,其写法如下所示,name是数据对象的属性,lowercase是一个过滤器,两者用“|”符号隔开。

{{ name | lowercase }}
<button v-bind:name="name | lowercase"></button>

  注意,自Vue 2.0起,所有的内置过滤器(例如capitalize、uppercase、json等)都已被移除,官方推荐按需加载更专业的库来实现过滤。

1)创建

  Vue允许用户自定义过滤器,可在实例的filters选项中注册局部过滤器,如下所示。

var vm = new Vue({
filters: {
lowercase: function(value) {
return value.toLowerCase();
}
}
});

  也可以在创建Vue实例之前,通过Vue.filter()方法注册全局过滤器,如下所示。

Vue.filter("lowercase", function (value) {
return value.toLowerCase();
});
var vm = new Vue({...});

  当局部过滤器和全局过滤器重名时,会优先采用局部过滤器。

2)链式调用

  多个过滤器可通过“|”符号串联实现链式调用,如下所示。

{{ name | lowercase | capitalize }}

  lowercase过滤器会接收name的值,然后将其计算结果传给capitalize过滤器。

3)传递参数

  由于过滤器本质上还是一个函数,因此它支持多个参数的传入,如下所示,compare过滤器会接收三个参数,分别是number和threshold两个数据对象的属性,以及一个常量10。

<p>{{number | compare(10, threshold)}}</p>

  注意,Vue 2.0取消了用空格来标记过滤器参数的方式,下面的调用是无效的。

<p>{{number | compare 10 threshold }}</p>

四、计算属性

  在模板中适合简单的声明式逻辑,而应避免频繁的进行复杂计算,这样既不利于维护,也会让模板结构变得臃肿而混乱。为了能合理的执行复杂表达式,Vue引入了计算属性的概念。

  计算属性在模板中的数据绑定和普通属性一样,但需要以函数的方式来定义。在下面的代码中,newName是一个计算属性,用来让name属性重复两次再提取末尾两个字符,在它的getter函数中引用了一个指向vm实例的this。

<div id="container">
<p>{{newName}}</p>
</div>
<script>
var vm = new Vue({
el: "#container",
data: {
name: "strick"
},
computed: {
newName: function() {
return this.name.repeat(2).substr(-2);
}
}
});
</script>

  注意,计算属性往往会依赖数据对象的属性或其它计算属性,也就是说,当依赖的属性被修改时,计算属性会自动更新。

1)缓存

  计算属性和方法有一个很大的不同,那就是它能被缓存。在下面的代码中,声明了一个getName()方法,虽然它的返回结果和之前的计算属性newName的值相同,但是当依赖的name属性不发生变化时,两者的执行方式会有所不同。

var vm = new Vue({
methods: {
getName: function() {
return this.name.repeat(2).substr(-2);
}
}
});

  当多次访问newName时,读取的是其缓存的值,不会执行它的getter函数,而方法每次都会执行一遍。由于计算属性能减少冗余的运算,因此它很适合处理那些耗时且性能开销巨大的操作。

2)写入

  默认情况下只需要定义计算属性的getter函数,不过Vue也为其提供了setter函数,使得计算属性在写入时能处理更为复杂的业务逻辑,如下所示。

var vm = new Vue({
el: "#container",
data: {
price: 10.2
},
computed: {
total: {
get: function() {
return this.price * 10;
},
set: function(value) {
this.price = value + Math.round(this.price);
}
}
}
});

  当为计算属性total赋值时(如下所示),就会调用它的setter函数,并更新vm.price。

vm.total = 10;

五、响应式原理

  Vue采用了非侵入性的响应式系统,当把数据对象传给Vue实例的data属性时,Vue会通过Object.defineProperty()方法将它的每个属性替换成getter和setter两个函数,下面用一个简单的示例展示Vue的基本思路。

const data = {        //数据对象
name: "strick"
};
const proxyData = {
name: data.name
};
Object.defineProperty(data, "name", {
get() {
//注入监听逻辑,并在必要时通知变更
return proxyData.name;
},
set(value) {
//注入监听逻辑,并在必要时通知变更
proxyData.name = value;
},
configurable: true,
enumerable: true
});

  经过这波操作后,就能让Vue拥有追踪属性变化的能力,并在属性被访问和修改时通知关联的视图重新渲染。在体验响应式所带来的便利的同时,也要知晓它的一些限制,接下来会分析Vue检测对象和数组发生变动时的注意事项。

1)对象

  由于JavaScript无法监听对象属性的添加或删除,因此只有在Vue实例化时才能对数据对象的根属性做getter和setter的替换,即转换成响应式的属性。Vue不允许动态添加根级的响应式属性,这些属性必须预先声明,如下所示,虽然age是vm实例的一个根属性,但它是在实例化后声明的,所以也就无法成为响应式的属性了。

var vm = new Vue({
data: {
name: "strick" //响应式属性
}
});
vm.age = 28; //非响应式属性

  除了内部的技术限制之外,提前声明响应式属性,也便于开发人员理解代码的意图。对于已创建的实例,有两种方式声明非根级的响应式属性,第一种是用全局的Vue.set()方法或Vue实例的$set()方法,在下面的代码中,为people对象声明了一个响应式的age属性。

var vm = new Vue({
data: {
people: {
name: "freedom"
}
}
});
Vue.set(vm.people, "age", 28);

  第二种是用Object.assign()方法,可一次性添加多个属性,如下所示,将原对象和新增的属性合并成一个新对象,再赋给vm.people。

vm.people = Object.assign({}, vm.people, { age: 28, school: "university" });

2)数组

  Vue无法检测下面两种数组的变动,以vm实例的names属性为例。

  (1)通过索引设置数组的元素。

  (2)缩短数组的长度。

var vm = new Vue({
data: {
names: ["strick", "freedom"]
}
});
vm.names[1] = "justify"; //第一种变动
vm.names.length = 1; //第二种变动

  要检测第一种变动,可以使用Vue.set()方法或数组的splice()方法,而要检测第二种变动,就只能使用splice()方法了,如下所示。

//检测第一种变动
Vue.set(vm.names, 1, "justify");
vm.names.splice(1, 1, "justify");
//检测第二种变动
vm.names.splice(1, 1);

Vue躬行记(1)——数据绑定的更多相关文章

  1. Vue躬行记(3)——样式和表单

    Vue对DOM元素的class和style两个特性做了专门的增强,即对CSS类和内联样式做了一层封装,通过v-bind指令来处理它们,而接收的表达式既可以是简单的字符串.对象或数组,也可以是复杂的计算 ...

  2. Vue躬行记(2)——指令

    Vue不仅内置了各类指令,包括条件渲染.事件处理等,还能注册自定义指令. 一.条件渲染 条件渲染的指令包括v-if.v-else.v-else-if和v-show. 1)v-if 该指令的功能和条件语 ...

  3. Vue躬行记(4)——组件

    组件是可复用的Vue实例,拥有属于自己的数据.模板.脚本和样式,可避免繁重的重复性开发.由于组件都是独立的,因此其内部代码不会影响其它组件,但可以包含其它组件,并且相互之间还能通信. 一.注册 在使用 ...

  4. Vue躬行记(5)——组件通信

    组件之间除了保持独立之外,还需要相互通信,本章将介绍几种通信的方式. 一.直接访问 Vue提供了三个实例属性,可直接访问父组件.子组件和根实例,如下所列. (1)$parent:父组件. (2)$ro ...

  5. Vue躬行记(6)——内容分发

    Vue提供了一种内容分发技术,可将父组件中的内容传递给子组件的模板,实现方式参照了Web组件规范草案. 一.插槽 Vue内置了一个<slot>元素,能作为插槽(slot)存在,而插槽内可包 ...

  6. Vue躬行记(7)——渲染函数和JSX

    除了可通过模板创建HTML之外,Vue还提供了渲染函数和JSX,前者的编码自由度很高,后者对于开发过React的人来说会很熟悉.注意,Vue的模板最终都会被编译成渲染函数. 一.渲染函数 虽然在大部分 ...

  7. Vue躬行记(8)——Vue Router

    虽然Vue.js未提供路由功能,但是官方推出了Vue Router(即vue-router库),以插件的形式支持.它与Vue.js深度集成,可快速的创建单页应用(Single Page Applica ...

  8. Vue躬行记(9)——Vuex

    Vuex是一个专为Vue.js设计的状态管理库,适用于多组件共享状态的场景.Vuex能集中式的存储和维护所有组件的状态,并提供相关规则保证状态的独立性.正确性和可预测性,这不仅让调试变得可追踪,还让代 ...

  9. ES6躬行记(1)——let和const

    古语云:“纸上得来终觉浅,绝知此事要躬行”.的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓.自己研读过许多ES6相关的书籍和资料,平时工作中也会用到,但在用到时经常需要上搜索引擎中查 ...

随机推荐

  1. HDU - 4366 Successor DFS区间+线段树

    Successor:http://acm.hdu.edu.cn/showproblem.php?pid=4366 参考:https://blog.csdn.net/colin_27/article/d ...

  2. codeforces 389 D. Fox and Minimal path(构造+思维)

    题目链接:https://vjudge.net/contest/175446#problem/J 题解:显然要用最多n个点构成的图要使的得到的最短路条数有1e9次个,显然要有几个数相乘容易想到2的几进 ...

  3. CF 13E Holes

    Holes 题意:现在有一排洞,每个洞有一个弹力,能弹到ai之后的洞,球会弹到这个排的外面,现在有2个操作,0 a b 将第a个洞的弹力设为b, 1 a 将球放入第a个洞,求输出进洞的次数 和 弹出这 ...

  4. CodeForces 758 D Ability To Convert

    Ability To Convert 题意:给你一个n进制的60位的数,但是由于Alexander只会写0->9,所以他就会用10来表示十而不是A(假设进制>10); 题解:模拟就好了,先 ...

  5. Spring boot 自定义 Resolver 支持 interface 类型参数

    在编写 RestController 层的代码时,由于数据实体类定义了接口及实现类,本着面向接口编程的原则,我使用了接口作为 RestController 方法的入参. 代码大致如下(省略具体业务部分 ...

  6. 十天快速入门Python

    课程导学 001 课程定位和目标 002 课程导学 第一部分 Python快速入门 第1天 Python基本语法元素 003 Python基本语法元素 004 程序设计基本方法 005 Python开 ...

  7. Django与mongodb数据库的连接

    1.最开始需要下载一个第三方模块:mongoengine 2.下载完成之后,需要在settings中完成配置(在DATABASES后面,别问我为什么,问了我也不告诉你...) connect中传入的是 ...

  8. 线上LVM磁盘挂载

    ESC云服务器磁盘扩容 日常运行中,容器服务所在目录由于container-log.image的增加需要占用大量的磁盘空间,所以对/var/lib/docker/目录进行扩容挂载LVM数据盘. 首先查 ...

  9. 【深入浅出-JVM】(75):class 装载

    过程 装载 条件 主动使用 class时 创建一个类的实例 (new .反射.克隆.反序列化) 调用类的静态方法(invokestatic) 使用类或接口的静态字段(getstatic.putstat ...

  10. jps虚拟机进程状态工具

    jps(JVM Process Status Tool) jps工具可以列出正在运行的虚拟机进程,并显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一ID(Local Virtual Machine ...