本篇主要讲述Vue2中组件间通信。目前 Vue 最新版本为Version

props与自定义事件

这是Vue推荐使用的通信方法

Vue中通过props属性可以实现父组件向子组件的参数传递。

所有的 prop 都使得其父子之间形成了一个 单向下行绑定 :父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。

在子组件中定义props属性

1
2
3
4
5
Vue.component('blog-post', {
// 在 JavaScript 中是 camelCase 的
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})

在父组件中传入对应的props属性

1
2
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

Vue中通过自定义事件可以实现子组件向父组件的参数传递。

在子组件中触发事件

1
this.$emit('myEvent', data)

在父组件中接受事件并进行处理

1
<my-component v-on:my-event="doSomething"></my-component>

provide与inject

2.2.0+ 新增

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代组件注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。祖先组件中通过provide来提供变量,然后在子孙后代组件中通过inject来注入变量。不论子孙后代有多深,只要调用了inject那么就可以注入provide中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在祖先组件的生命周期内,子孙后代组件都可以调用。

provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.component('child', {
inject: ['somedata'], //得到父组件传递过来的数据
data() {
return {
mymessage: this.somedata
}
},
template:`
<div>
<input type="tet" v-model="mymessage">
</div>
`
})
Vue.component('parent', {
provide:{
somedata: 'test' //提供给子组件的数据
},
template:`
<div>
<p>this is parent compoent!</p>
<child></child>
</div>
`
})

props与.sync

2.3.0+ 新增

.sync 是一个自定义事件修饰符,可以实现对props的“双向绑定”
实际上sync只是props&自定义事件的一个语法糖

1
<text-document v-bind:title.sync="doc.title"></text-document>

实际上是以下写法的一个缩写

父组件

1
2
3
4
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>

子组件
1
this.$emit('update:title', newTitle)

v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
所以可以在父组件中使用 v-model="message" 然后在子组件中通过 $emit('input', newMessage) 来改变父组件上v-model绑定的值message。
但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。

$parent与$children/$refs

不推荐使用

在子组件中可以直接通过 this.$parent.$data 来访问和修改父组件的值,
在父组件中也可以通过 this.$children[0].$data 来访问和修改子组件的值
也可以在引用子组件时通过添加ref来使得父组件中可以通过 this.$refs.child.$data 来访问和修改子组件的值

$attr与$listener

2.4.0+新增

$attr与$listener实现了一种跨父子的参数传递方法

$attr包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。

$listener包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件

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
56
57
58
59
60
61
62
Vue.component('C', {
template:`
<div>
<input type="text"
v-model="$attrs.messagec"
@input="passCData($attrs.messagec)">
</div>`,

methods: {
passCData(val){
//触发父组件A中的事件
this.$emit('getCData', val)
}
}
})
Vue.component('B', {
data(){
return {
mymessage: this.message
}
},
template:`
<div>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
<!-- C组件中能直接触发getCData的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
<!-- 通过v-bind 绑定$attrs属性,C组件可以直接获取到A组件中传递下来的props(除了B组件中props声明的) -->
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
`,
props:['message'], //得到父组件传递过来的数据
methods:{
passData(val){
//触发父组件中的事件
this.$emit('getChildData',val)
}
}
})
Vue.component('A', {
data(){
return {
message:'hello b', //传递给子组件的数据
messagec:'hello c' //传递给c组件的数据
}
},
template:`
<div>
<p>this is parent compoent!</p>
<B :message="message" @getChildData="getChildData"
v-bind="$attrs" v-on="$listeners" ></B>
</div>
`,
methods: {
//执行子组件触发的事件
getChildData(val){
console.log('这是来自B组件的数据')
},
//执行C组件触发的事件
getCData(val){
console.log("这是来自C组件的数据:" + val)
}
}
})

Event Bus 中央事件总栈 $emit $on

为了实现非父子间的通信,我们可以通过通过使用一个空的Vue实例作为中央事件总栈来处理所有的事件,通过Vue自带的$emit $on来进行组件间通信。

1
2
let bus = new Vue()
Vue.prototype.bus = bus

甚至可以用app.js中的Vue实例来处理

1
2
3
4
5
6
7
8
9
10
/* app.js*/
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App },
beforeCreate () {
Vue.prototype.bus = this
}
})

这样在组件中我们就可以通过以下方式处理

1
2
this.bus.$emit('some-event', data)
this.bus.$on('some-event', ()=>{})

vuex

vuex