本篇开始,仔细地阅读Vue官方指南,并记录学习过程中思考、实践、总结的要点。 本篇包含的主要是内容是:
- 生命周期图示中要点
- 2.6.0新增的动态参数属性
生命周期图示中要点
这是官方的生命周期图示:
根据这张图示,可以总结出的要点有:
如果没有
el
option,new Vue
构造出的实例不会立即渲染到dom上,除非再次调用实例的$mount
方法,将vue实例与dom元素进行挂载:1
2
3
4
5
6
7
8
9
10
11let vue = new Vue({
el: '#app'
//,...
});
//等价于
let vue = new Vue({
//...
});
vue.$mount('#app');而且没有
el
option的实例,它的生命周期只会进行到created
这个钩子,当它被$mount
到一个dom元素之后,created
后面的生命周期才能继续。vm.$data
这个属性、vm
的计算属性、vm
的watch属性、以及vm.$watch
方法,最早要在created
这个钩子里面才能生效。只有生命周期进行到created
这个钩子的时候,vue实例才完成了injections的初始化;vm.$el
这个属性,最早要在beforeMount
这个钩子里面才能访问到;- vm实例methods内定义的行为方法,最早要在
created
这个钩子里面才能访问到; vm.$refs
这个属性内的节点引用,最早要在mounted
这个钩子里面才能访问到;当设置了
el
option,且未设置template
option的时候,会把el
的outerHTML
作为渲染模板进行编译,由于是outerHTML
,所以el元素本身也能使用vue的模板语法,比如动态地设置class属性;
以上几点都可以通过下面的示例进行说明:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55<div id="vue" :class="{enabled: enabled}">
<p ref="content">{{msg}}</p>
</div>
<script type="text/javascript">
let obj = {
msg: new Date(),
enabled: true
};
let vue = new Vue({
data: obj,
beforeCreate() {
console.log('beforeCreate', this.$el, this.$data, this.$data === obj );
//beforeCreate undefined undefined false
console.log(this.updateMsg);
//undefined
console.log(this.$refs.content);
//undefined
},
created() {
console.log('created', this.$el, this.$data, this.$data === obj);
//created undefined {__ob__: Observer} true
console.log(this.updateMsg);
//function updateMsg
console.log(this.$refs.content);
//undefined
},
beforeMount(){
console.log('beforeMount', this.$el);
//beforeMount div#vue
console.log(this.$refs.content);
//undefined
},
mounted(){
console.log('mounted', this.$el);
//mounted div#vue
console.log(this.$refs.content);
//<p></p>
},
methods: {
updateMsg(){
this.msg = new Date();
}
}
});
vue.$mount('#vue');
</script>不管有没有设置
el
option,只要设置了template
option,就会把template
直接用于底层的render函数,并根据template
创建出新的dom元素,替换掉el
option所指向的dom元素,并赋值给vm.$el
;所以更加安全地访问vm.$el
的时机点,最早是在mounted
这个钩子函数里面。template
option的应该只能包含一个顶层元素,这个顶层元素最后会被创建到dom里面,成为最终的vm.$el
;由此可知,所谓的单文件的vue组件,实际上就是把template部分定义的内容,设置为了组件的template
option而已。8个生命周期函数,除了通过
vm
实例的option来写,也可以通过以下方式,手工添加:1
2
3
4
5
6
7
8this.$on('hook:beforeDestroy',()=>{});
this.$on('hook:destroyed',()=>{});
this.$on('hook:beforeCreate',()=>{});
this.$on('hook:created',()=>{});
this.$on('hook:beforeMount',()=>{});
this.$on('hook:mounted',()=>{});
this.$on('hook:beforeUpdate',()=>{});
this.$on('hook:updated',()=>{});这个方式在需要编写一些更加通用性的功能的时候,比如plugins,会起到很大的作用。同时由于
vm
实例的生命周期,第一步就是init events and lifecyle
,所以上面的这些hook事件,最早在beforeCreate这个option里面就能使用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<div id="vue" :class="{enabled: enabled}">
<p ref="content">{{msg}}</p>
</div>
<script type="text/javascript">
let obj = {
msg: new Date(),
enabled: true
};
let vue = new Vue({
data: obj,
beforeCreate() {
this.$on('hook:created', ()=>{
console.log('another created hook');
//another created hook
})
},
created() {
console.log('created');
//created
}
});
vue.$mount('#vue');
</script>vm
的侦听属性方法,要比手工调用vm.$watch
添加的回调先执行; 计算属性晚于beforeUpdate
这个钩子函数之后执行,但是会在updated
这个钩子函数之前执行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48<div id="vue" :class="{enabled: enabled}">
<p ref="content">{{reverseMsg}}</p>
</div>
<script type="text/javascript">
let obj = {
msg: new Date() + "",
enabled: true
};
let vue = new Vue({
data: obj,
watch: {
msg(){
console.log('msg changed in watch property');
}
},
computed: {
reverseMsg(){
let r = this.msg.split('').reverse().join('');
console.log('reverse msg changed');
return r;
}
},
created() {
this.$watch('msg', ()=>{
console.log('msg changed from created');
});
},
mounted(){
this.msg = new Date() + "1";
},
beforeUpdate(){
console.log('beforeUpdate');
},
updated(){
console.log('updated');
}
});
vue.$mount('#vue');
//reverse msg changed
//msg changed in watch property
//msg changed from created
//beforeUpdate
//reverse msg changed
//updated
</script>
2.6.0新增的动态参数属性
2.6.0开始,模板里面bind元素的attributes,可以使用动态参数了,不过这个动态参数跟通常取值的模板语法不太一样。先来看它正常的使用:1
2
3
4
5
6
7
8
9
10
11
12
13<div id="vue" v-bind:[titlename]="titleValue">
</div>
<script type="text/javascript">
let vue = new Vue({
data: {
titlename: 'title',
titleValue: 'this is a test'
}
});
vue.$mount('#vue');
</script>
最后dom里面会输出:1
<div id="vue" title="this is a test"></div>
bind动态参数的用法,与bind常规参数的区别就是中括号,v-bind:[titlename]
,表示最终要bind的属性名称,由vm
实例的titlename属性值来决定。动态参数的值如果是一个非空字符串,就以该字符串作为attribute的名称,如果是null值,表示移除该attribute。
直接在html中书写vm
实例的模板(el
option或通过vm.$mount
与html中的元素挂载),要注意:当html被浏览器加载完,vm
实例还没构建前,html中与vm
实例对应的元素,如果使用了动态参数绑定attributes,动态参数名称会全部转为小写,导致一些意外的情况。比如:1
2
3
4
5
6
7
8
9
10
11
12
13<div id="vue" v-bind:[titleName]="titleValue">
</div>
<script type="text/javascript">
let vue = new Vue({
data: {
titleName: 'title',
titleValue: 'this is a test'
}
});
vue.$mount('#vue');
</script>
上面这个代码在浏览器运行会报错Property or method "titlename" is not defined on the instance but referenced during render
。因为它是直接在html中运行的,div#vue
加载到dom以后,被实例化为vm
实例前,这个元素上所有的属性名称都会被转为小写:v-bind:[titleName]
=>v-bind:[titleName]
,导致最后参与vm
实例化的时候,这个元素的outerHTML实际上是:1
<div id="vue" v-bind:[titlename]="titleValue"></div>
而不是1
<div id="vue" v-bind:[titleName]="titleValue"></div>
最后vm
实例就把<div id="vue" v-bind:[titlename]="titleValue"></div>
编译为了渲染模板,当实际渲染的时候,[titlename]
就变为了取vm
实例titlename
属性的值,而vm
实例的data
里面只定义了titleName
,没有titlename
,所以导致报错。
如果直接通过template
option指定模板,就不会有这个问题:1
2
3
4
5
6
7
8
9
10
11
12
13
14<div id="vue">
</div>
<script type="text/javascript">
let vue = new Vue({
data: {
titleName: 'title',
titleValue: 'this is a test'
},
template: `<div v-bind:[titleName]="titleValue"></div>`
});
vue.$mount('#vue');
</script>
所以单文件的Vue组件也不会有这个问题,因为它本质上利用template来构造vm
实例的。
同理,下面的写法如果直接放在html里面也是有问题的:1
2
3
4
5
6
7
8
9
10
11
12
13
14<div id="vue" v-bind:['title' + Name]="titleValue">
</div>
<script type="text/javascript">
let vue = new Vue({
data: {
titleName: 'title',
name: 'Name',
titleValue: 'this is a test'
},
});
vue.$mount('#vue');
</script>
div#vue
的v-bind:['title' + Name]
这个名称,违背了html5文档的规范:属性名不能出现空格、引号。 这种改用template写法也是不行的,Vue严格规定了动态参数不能使用一些特殊字符,否则会出现下面的警告:1
Invalid dynamic argument expression: attribute names cannot contain spaces, quotes, <, >, / or =.
动态参数不仅只有v-bind指令可以用,其它指令都能用;v-on指令使用动态参数时,修饰符不能用动态参数