展开陈述之前,我先说明一下:vue组件之间的通信在我这里指的是一个组件可以触发另一个组件的数据改变,只要可以实现这种效果,我都会视为一种通信方式。基于以上的说明的,我总结一下我所了解的几种通信方式。
第一种方式:父组件传属性到子组件
父组件传属性到子组件是最常见的方式,关键点是子组件在props属性接收传来的属性,示例:
//Father组件
<template>
<div>
<Child1 :msg="msg"></Child1>
</div>
</template>
<script>
import Child1 from './Child1.vue'
export default {
components: { Child1 },
data() {
return {
msg: '你好,child1'
}
}
}
</script>
//Child1组件
<template>
<div>
<div>{{msg}}</div>
</div>
</template>
<script>
export default {
props: ['msg'],
}
</script>
第二种方式:子组件改变父组件数据
这种场景最常见的方式是通过在子组件定义一个事件,在子组件内触发事件来实现,vue的原型对象中有一个$emit的方法,可以触发自身的事件,第一个参数指定触发的事件名,后面参数是传到绑定函数的参数。示例:
//Father组件
<template>
<div>
<Child1 @changeMsg="changeMsg" :msg="msg"></Child1>
</div>
</template>
<script>
import Child1 from './Child1.vue'
export default {
components: { Child1 },
data() {
return {
msg: '你好,child1'
}
},
methods: {
changeMsg(msg) {
this.msg = msg
}
},
}
</script>
//Child1组件
<template>
<div>
{{msg}}
<button @click="changeMsg">click</button>
</div>
</template>
<script>
export default {
components: {},
props: ['msg'],
data() {
return {
}
},
created() {},
watch: {},
computed: {},
methods: {
changeMsg() {
this.$emit('changeMsg', 'haha')
}
},
}
</script>
另外有一种不推荐用的方法是,父组件传对象到子组件,由于对象是引用类型,在子组件可以通过修改对象的某个属性。示例:
//Father组件
<template>
<div>
<Child1 :info="info"></Child1>
</div>
</template>
<script>
import Child1 from './Child1.vue'
export default {
components: { Child1 },
data() {
return {
info: { msg: '你好,child1' }
}
},
methods: {
changeMsg(msg) {
this.msg = msg
}
},
}
</script>
//Child1组件
<template>
<div>
{{info.msg}}
<button @click="changeMsg">click</button>
</div>
</template>
<script>
export default {
props: ['info'],
data() {
return {
}
},
methods: {
changeMsg() {
this.info.msg = 'haha'
}
},
}
</script>
第三种方式:借用一个新的Vue对象
这种方式一般被称为全局“bus”事件方式,在两个没有直接联系的组件之间,可以使用这种方式。实现的步骤是:先创建一个vue对象,然后在需要执行事件发生后逻辑的组件注册一个事件,最后在发起通知的地方触发事件,示例:
先在main.js创建一个全局对象
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.prototype.$bus = new Vue()
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
在我新创建的Child2组件注册一个事件
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
data() {
return {
msg: '我是Child2'
}
},
created() {
this.$bus.$on('changeMsg', (msg) => {
this.msg = msg
})
}
}
</script>
<style scoped>
</style>
在Child1组件的按钮事件触发事件:
<template>
<div>
<button @click="changeMsg">click</button>
</div>
</template>
<script>
export default {
data() {
return {
}
},
created() {},
watch: {},
computed: {},
methods: {
changeMsg() {
this.$bus.$emit('changeMsg', 'haha')
}
},
}
</script>
第四种方式:使用全局函数
这种方式是把一个方法挂在了vue原型对象上或者window对象,通过先改写函数再调用函数的方式,一定要注意注册函数的this指向问题,示例:
在main.js的代码:
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.prototype.$changeMsg = (fn, type) => {
if(type === 'on') {
Vue.prototype.$changeMsg = fn
}
}
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
在Child2组件的代码:
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
data() {
return {
msg: '我是Child2'
}
},
created() {
this.$changeMsg((msg) => {
this.msg = msg
}, 'on')
}
}
</script>
Child1组件的代码:
<template>
<div>
<button @click="changeMsg">click</button>
</div>
</template>
<script>
export default {
methods: {
changeMsg() {
this.$changeMsg('haha')
}
},
}
</script>
<style scoped>
</style>
第五种方式:使用vuex
在实际项目开发中,推荐使用vuex,上面两种方式在实际项目中是不推荐使用的。使用vuex第一步是安装vuex:
npm i vuex -D
第二步,创建一个store文件到src目录下
在index.js写上:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
msg: '你好'
},
mutations: {
changeMsg(state, msg) {
state.msg = msg
}
}
})
export default store
第三步是引入vuex,并挂载到vue原型对象上(这是通常的做法,可以选择用时再引入或者挂载在window上)
在main.js的代码:
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.prototype.$store = store
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
Child2组件的代码:
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
computed: {
msg() {
return this.$store.state.msg
}
},
}
</script>
Child1组件的代码:
<template>
<div>
<button @click="changeMsg">click</button>
</div>
</template>
<script>
export default {
methods: {
changeMsg() {
this.$store.commit('changeMsg', 'haha')
}
},
}
</script>
总结一下,在这里我共介绍了五种通信方式,其中第一和第二种是父子之间的通信,第三种到第五种是任意组件之间的通信,这里第四种方式是不好的实现,不推荐使用,其它方式则根据实际场景选用适用的一种,一个项目中基本上都是几种方式同时使用的。