Vue中组件间通信方式总结
使用props和$emit进行组件间数据传递
这是最常用的组件通信方式,适用于父子组件间数据传递
- 父传子:子组件中props定义好要接受的数据,父组件中使用v-bind传递子组件props中定义好的数据。
- 子传父: $emit(‘自定义事件名’,arguments),子组件中使用 $emit自定义事件及一个或多个参数,父组件中监听自定义事件并作相应业务处理
使用$attrs
和$listeners
来实现父子组件间数据通信
适用场景
嵌套的多层父子组件(只有父子组件两个建议使用props
和$emit
基本步骤
- 在组件中使用v:bind="$attrs"来实现父级向子组件传递数据
- 使用v-on="$listeners"来监听子组件传递的事件和数据
- 子组件使用$attrs拿到父组件中data中定义的属性
- 在嵌套的父子组件中的html部分步骤1和步骤2都不可以缺少
代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>局部组件的使用</title>
</head>
<body>
<div id="app">
<h1>在有template选项时,#app里的内容不展示</h1>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component('A', {
template: `
<div>
{{$attrs}}
<B v-bind="$attrs" v-on="$listeners" @getCData="getCData"/>
</div>
`,
methods: {
getCData(val) {
console.log('我是A组件'+val)
}
}
})
Vue.component('B', {
template: `
<div>
{{$attrs}}
<C v-bind="$attrs" v-on="$listeners" @getCData="getCData"/>
</div>
`,
methods: {
getCData(val) {
console.log('我是B组件'+val)
}
}
})
Vue.component('C', {
template: `
<div>
{{$attrs}}
<p @click="my_click">{{$attrs.dataA}}</p>
</div>
`,
methods: {
my_click() {
this.$emit('getCData', '111')
}
}
})
let vm = new Vue({
el: '#app',
template: `
<div>
<input type="text" v-model="data"/>
<A :dataA="data" :anotherData="anotherData"/>
</div>
`,
data() {
return {
data: '这是给c的数据',
anotherData: '测试数据'
}
},
})
</script>
</html>
网页截图
从这里可以看到KaTeX parse error: Expected '}', got 'EOF' at end of input: … this.emit(‘getCData’, ‘111’)
}` 会触发getCData事件,在B和A组件中都监听了该事件,从而每点击一次C组件文本,都会在控制太打印出相应的内容。如下图所示
使用中央事件总线进行组件间通信
当组件不是父子组件关系,比如平行组件间数据通信,或者有两个平行组件A和B,A组件的子组件是C。A和C组件间的数据通信可以使用中央事件总线。
适用场景
非父子组件数据通信
基本步骤
- let bus = new Vue() 创建中央事件总线
-
bus.$emit
(‘自定义事件名’,要传递的值)使用bus.$emit
传递自定义事件和值 -
bus.$on(‘自定义事件名’, eventHandler)
使用bus.$on
在组件的mounted阶段监听自定义事件并在回调函数eventHandler
中作相应的业务处理
Vue.component('A', {
template: `
<input type="text" v-model="valueA" @click="my_click" />
`,
data() {
return {
valueA: '我是A组件的值'
}
},
methods: {
my_click() {
bus.$emit('getAData', this.valueA)
}
},
mounted() {
bus.$on('getCValue', val => this.valueA = val)
}
})
Vue.component('B', {
template: `
<C/>
`,
data() {
return {
}
}
})
Vue.component('C', {
template: `
<input type="text" v-model="valueC" @change="changeValue(valueC)"/>
`,
data() {
return {
valueC: ''
}
},
methods: {
changeValue(value) {
bus.$emit('getCValue', value)
}
},
mounted() {
bus.$on('getAData', val => {
this.valueC = val
})
}
})
let bus = new Vue()
let vm = new Vue({
el: '#app',
template: `
<div>
<A/>
<B/>
</div>
`
})
上述代码中定义了三个全局组件A,B,C。其中A和B是平行组件,C是B的子组件。
页面效果如下图
A组件中data函数定义了valueA:‘我是A组件的值’,现在想把valueA展示在C组件文本框中。通过点击事件my_click() { bus.$emit('getAData', this.valueA) }
使用bus.$emit
传递出事件getAData
和valueA
的值。
在C组件的mounted
阶段使用bus.$on('getAData', val => { this.valueC = val })
将传递来的valueA
赋值给valueC
从而将值在C组件中展示。
当C组件的值发生改变时A组件同样能监听到事件从而使文本框的值发生改变。
使用中央事件总线进行数据通信时,需要在同一个Vue实例中使用
$on
监听事件和$emit
来触发事件。上述代码中的事件监听和传递都是在VM实例中的。
在vue中组件间通信还有更多其他方式比如使用
$parent
,$children
,或者使用父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。(这两种很少使用,故不多作赘述)
以及针对大型项目中vuex都能实现组件间通信。
上面讲述的三种组件间通信方式基本满足日常开发需求,根据具体场景选择合适的方式即可。