Vue指南的要点笔记(九)

本篇学习指南中组件部分介绍的边界情况的要点。

访问元素&组件

访问根实例

使用vm.$root,它是所有vue实例都共享的一个对象,可以作为一个全局的store来使用,同时也可借助它实现全局的event bus。

访问父组件

使用vm.$parent可以访问到父级组件,拿到父组件实例以后,可以访问它的数据,调用它的方法。 虽然vue允许这么做,但是从编程的方式来说,这样的父子组件交互方式,会造成父子组件之间形成非常强的耦合,所以不是一个推荐的使用方式。

访问子组件实例或子元素

如果想访问子组件的实例或者是某个子dom元素,需要先给子组件或子元素用ref属性,起一个名字,它类似html中id属性的作用,如:

1
2
3
<page ref="page">
<div class="main" ref="main"></div>
</page>

然后在组件中通过vm.$refs这个属性结合子组件实例和子元素的ref名称,就能访问到它们:

1
2
this.$refs.page;//将访问到page这个子组件实例
this.$refs.main;//将访问到div.main这个dom元素

vm.$refs会把组件模板中ref有值的子组件和子元素都收集到一起,便于使用,如果ref对应的是一个自定义组件,那么访问它时就会访问这个组件的实例对象;如果ref对应的是一个dom元素,访问它时就会访问到这个dom对象。

refv-for一起使用的时候,得到的引用将会是一个包含了对应数据源的这些子组件的数组。 $refs至少要在mounted这个生命周期钩子才能访问到,而且不是响应式的,所以如果在模板或计算属性中直接使用的话, 要注意到它是非响应式的这一点。

依赖注入

在官方的例子中:

1
2
3
4
5
<google-map>
<google-map-region v-bind:shape="cityBoundaries">
<google-map-markers v-bind:places="iceCreamShops"></google-map-markers>
</google-map-region>
</google-map>

说了这样一个场景,就是google-map可能会给所有的后代组件都提供一个map对象,如果所有后代元素都非得通过$parent才能访问到这个对象的话,那么层级越深的后代组件,就要使用多层$parent才能访问到:

1
this.$parent.$parent;

而且强耦合组件的层级位置,增加或减少层级都可能导致访问出错。 vue提供了依赖注入的方式,来给后代组件提供数据和方法,它实际上是借助vue框架充当了中间平台的角色,父组件把数据和方法提供给平台,平台把数据和方法注入到后代组件,这样父组件无需关心要把数据和方法给谁,后代组件也不用考虑层级,不用考虑这些数据和方法是哪来的,这是一项代码的设计原则。

使用方法是:首先在父组件,通过provide这个option说明父组件要给后代组件提供的数据和方法:

1
2
3
4
5
provide: function () {
return {
getMap: this.getMap
}
}

然后后代组件,通过inject这个option,说明自己想要平台注入的数据和方法,它是一个数组:

1
inject: ['getMap']

后代组件可以直接通过vm.getMap访问注入的数据和方法,举例如下:

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
<div id="vue">
<google-map>
<google-map-region>
<google-map-markers></google-map-markers>
</google-map-region>
</google-map>
</div>

<script type="text/javascript">
Vue.component('google-map', {
data(){
return {}
},
provide: {
getMap: function () {
return {
render(){
return 'render';
}
};
}
},
template: `<div><slot></slot></div>`
});

Vue.component('google-map-region', {
data(){
return {}
},
inject: ['getMap'],
template: `<div><slot></slot></div>`
});

Vue.component('google-map-markers', {
data(){
return {}
},
inject: ['getMap'],
template: `<div>{{getMap().render()}}</div>`
});

let vue = new Vue({
data: {
},
el: '#vue'
});
</script>

provideinject在api文档中的类型说明为:

1
2
3
4
类型:

provide:Object | () => Object
inject:Array<string> | { [key: string]: string | Symbol | Object }

provide比较好理解,它支持对象或者是一个返回对象的函数。inject稍微复杂一下,选项是:

1
2
3
4
5
6
一个字符串数组,
或一个对象:
对象的 key 是本地的绑定名,
value 是:在可用的注入内容中搜索用的 key (字符串或 Symbol)或一个对象,该对象的:
from 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol)
default 属性是降级情况下使用的 value

示例如下:

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
<div id="vue">
<google-map>
<google-map-region>
<google-map-markers></google-map-markers>
</google-map-region>
</google-map>
</div>

<script type="text/javascript">
let map = Symbol();

Vue.component('google-map', {
data(){
return {}
},
provide: {
[map]: parseInt(Date.now() / 1000)
},
template: `<div><slot></slot></div>`
});

Vue.component('google-map-region', {
data(){
return {}
},
inject: {
m1: map
},
template: `<div>{{m1}}<slot></slot></div>`
});

Vue.component('google-map-markers', {
data(){
return {}
},
inject: {
m2: {
from: map
}
},
template: `<div>{{m2}}</div>`
});

let vue = new Vue({
data: {
},
el: '#vue'
});
</script>

组件之间的循环引用

组件之间的循环引用可以通过异步组件来解决。