一 、prop
绑定:<son:fatherMes="sendSonMes" @sonSay="sonSay" />
触发:this.$emit("sonSay",data);
区别与react:react
组件更新来源于props
更新和自身state
改变,当props
改变,会默认更新组件。而在Vue
中,如果我们对父组件传过来的新的props
没有做依赖收集(template
模版收集,computed
计算属性收集),组件是不会触动更新的。
数据格式化:
computed:{
computedFatherMes(){
return this.fatherMes + ' '
}
},
数据监听:当我们根据props
改变不想更新视图,或者不想立即更新视图,那么可以用watch
来监听来自父组件的props
的变化
watch:{
fatherMes(newVaule){
console.log( newVaule)
}
},
优点:灵活简单,可以对props
数据进行计算属性,数据监听等处理。一层父组件通信方便
缺点:
1 、props篡改
基础类型修改报错:
mounted:{ this.fatherMes= 'hello ,father' }
引用类型:子属性可被篡改
mounted:{ this.fatherMes.name= 'change father name from children' }
2 、跨层级通信,兄弟组件通讯困难
父组件-子组件-子组件的子组
父子-子孙通信:这种通信方式需要我们一层一层的prop
绑定属性和方法,如果遇到更复杂的情况,实现起来比较困难。
实现子组件-> 父组件 -> 子组件
兄弟组件通信:如果想要通过父组件做媒介,那么必定会造成父组件重新渲染,为了实现兄弟组件通信付出的代价比较大。
应用场景:
正常不是嵌套很深的父子组件通信,和关系不是很复杂的兄弟组件组件通信。
二 、this.$xxx
组件和组件通过this.$children
和this.$parent
指针关联起来,
理论上,我们可以找到通过this.$childrenthis.$parent
来访问页面上的任何一个组件 ,但是实际上如何精确匹配到目标组件,确是一个无比棘手的问题。
vue
本身也不提倡这种通信方式。而且这种通信方式也有很多风险性
优点:更加的简单直接获取vue实例,对vue实例下的数据和方法直接获取或者引用。
缺点:
1、$this.children不可控性大,有一定风险(通过v-if改变组件状态,很容易出现调用错误)
这时可用ref
获取对应子组件的实例
2 、不利于组件化
组件开发思路初衷,也不是由组件外部来对内部作出一定改变,而往往是内部的改变,通知外部绑定的方法事件。反过来如果是子组件内部,主动向父组件传递一些信息,也不能确定父组件是否存在。
3 、兄弟组件深层次嵌套组件通讯困难(向上或向下获取实例风险都会逐层加大)
应用场景:
直接通过实例获取的通信方式适合已知的,固定化的页面结构,这种方式更适合页面组件,而不适合一些第三方组件库,或者是公共组件。
三 、provide inject
vue
中provide
和inject和react
的context
上下文两个作用一定程度十分相似,
// 父组件
provide(){
return { /* 将自己暴露给子孙组件 ,这里声明的名称要于子组件引进的名称保持一致 */
father:this
}
},
/* 子组件中:引入父组件 */
inject:['father'],
组件,插槽引入是一样的;
// 也可以暴露方法
provide(){
return {
/* 将通信方法暴露给子孙组件(注意绑定this) */
grandSonSay:this.grandSonSay.bind(this),
sonSay:this.sonSay.bind(this)
}
},
methods:{
/* 接受孙组件信息 */
grandSonSay(value){
this.grandSonMes = value
},
/* 接受子组件信息 */
sonSay(value){
this.sonMes = value
},
},
优点
1、适用于插槽,嵌套插槽
2、组件通信不受到子组件层级的影响
缺点:
1、不适合兄弟通讯
2、provide-inject
协调作用就是获取父级组件们提供的状态,方法,属性等,流向一直都是由父到子,
3、父级组件无法主动通信
应用场景
provide-inject
这种通信方式,更适合深层次的复杂的父子代通信,子孙组件可以共享父组件的状态,还有一点就是适合el-form
el-form-item
这种插槽类型的情景。
四 、vuex
vuex
算是vue
中处理复杂的组件通信的最佳方案
vuex声明
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export default new Vuex.Store({
state:{
fatherMes:'',
sonMes:'',
fatherMesAsync:''
},
mutations:{
sayFaher(state,value){
state.fatherMes = value
},
saySon(state,value){
state.sonMes = value
},
sayAsyncFather(state,value){
state.fatherMesAsync = value
}
},
actions:{
// 异步action
asyncSayFather({ commit },payload){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(payload)
},2000)
}).then(res=>{
commit('sayAsyncFather',res)
})
}
}
})
main.js注入store
import store from './components/vuex/store'
new Vue({
render: h => h(App),
store
}).$mount('#app')
获取和改变store
computed:{
sonMes(){
return this.$store.state.sonMes
}
},
mounted(){
console.log(this.$store)
},
methods:{
/* 触发mutations,传递数据给子组件 */
send(){
this.$store.commit('sayFaher',this.mes)
},
/* 触发actions,传递数据给子组件 */
asyncSend(){
this.$store.dispatch('asyncSayFather',this.asyncMes)
}
},
优点:
根本解决复杂组件的通信问题
支持异步组件通信
vuex
中actions
允许我们做一些异步操作,然后通过commit
可以把数据传入对应的mutation
,至于actions
为什么可以执行异步,是因为里面底层通过Promise.resolve
能够获取异步任务完成的状态。
缺点:
流程相比稍微复杂------不同的模块,需要建立独立的modules
。
应用场景:
vuex
的出现,就是解决这些比较复杂的组件通信场景。对于中大型项目,vuex
是很不错的状态管理,数据通信方案。
五 、事件总线一 EventBus
EventBus
事件总线,EventBus
所有事件统一调度,有一个统一管理事件中心,一个组件绑定事件,另一个组件触发事件,所有的组件通信不再收到父子组件的限制,那个页面需要数据,就绑定事件,然后由数据提供者触发对应的事件来提供数据
EventBus
核心思想是事件的绑定和触发
废话少说,直接看代码
export default class EventBus {
es = {}
/* 绑定事件 */
on(eventName, cb) {
if (!this.es[eventName]) {
this.es[eventName] = []
}
this.es[eventName].push({
cb
})
}
/* 触发事件 */
emit(eventName, ...params) {
const listeners = this.es[eventName] || []
let l = listeners.length
for (let i = 0; i < l; i++) {
const { cb } = listeners[i]
cb.apply(this, params)
}
}
}
export default new EventBus()
父组件
import EventBus from './eventBus'
export default {
name:'father',
components:{
son ,/* 子组件 */
brotherSon, /* 子组件 */
},
data(){
return {
mes:'',
sonMes:''/* 发送给子组件的信息 */
}
},
mounted(){
/* 绑定事件 */
EventBus.on('sonSay',this.sonSay)
},
methods:{
/* 传递给子组件 */
send(){
EventBus.emit('fatherSay',this.mes)
},
/* 接受子组件信息 */
sonSay(value){
this.sonMes = value
},
},
}
子组件
import EventBus from './eventBus'
export default {
name:'son',
data(){
return {
mes:'',
brotherMes:'',
fatherMes:''
}
},
mounted(){
/* 绑定事件 */
EventBus.on('fatherSay',this.fatherSay)
},
methods:{
/* 向父组件传递信息 */
send(){
EventBus.emit('sonSay',this.mes)
},
/* 向兄弟组件传递信息 */
sendBrother(){
EventBus.emit('brotherSay',this.brotherMes)
},
/* 父组件对我说 */
fatherSay(value){
this.fatherMes = value
}
},
}
注意:兄弟组件处理逻辑和父子之间没什么区别
优点:
简单灵活,父子兄弟通信不受限制。
通信方式不受框架影响。
通过eventBus
可以绕过渲染层,直接有逻辑层讲数据进行推送,节约了性能的开销。(小程序)
缺点:
维护困难,容易引起连锁问题
需要谨小慎微的命令规范
不利于组件化开发--------一个页面上有多个公共组件,我们只要向其中的一个传递数据,但是每个公共组件都绑定了数据接受的方法。我们怎么样做到把数据传递给需要的组件呢?
六 、事件总线二 new Vue
new Vue
这种通信方式和eventBus
大致差不多,
不同点是,以vue
实例作为eventBus
中心,除了我们可以用$on
,$emit
之外,我们还可以用vue下的data
,watch
等方法,而且我们建立多个多个vue
,作为不同模块的数据通信桥梁,相比上边那个EventBus
方法,new Vue
这种方法更高效,更适合vue
项目场景
vueBus
import Vue from 'vue'
export default new Vue()
用法:
import VueBus from './vueBus'
export default {
/* 父组件 */
name:'father',
components:{
son ,/* 子组件 */
},
data(){
return {
mes:'',
sonMes:'' /* 发送给子组件的信息 */
}
},
created(){
/* 绑定属性 */
VueBus._data.mes = 'hello,world'
},
mounted(){
/* 绑定事件 */
VueBus.$on('sonSay',this.sonSay)
},
methods:{
/* 传递给子组件 */
send(){
VueBus.$emit('fatherSay',this.mes)
},
/* 接受子组件信息 */
sonSay(value){
this.sonMes = value
},
},
}
优点:
简单灵活,任意组件之间通信。
除了通信还可以使用watch
, computed
等方法
缺点:
基本上EventBus
的缺点,都在vue
这种通信方式中都有存在
应用场景:
在项目中不考虑用vuex
的中小型项目中,可以考虑采用vue
事件总线这种通信方式,在使用的这种方式的时候,我们一定要注意命名空间,不要重复绑定事件名称。分清楚业务模块,避免后续维护困难。
最后
vue
项目中,具体要用什么通信方式,还要看具体的业务场景,项目大小等因素综合评估。
如果大家觉得有点帮助的话,就 点赞 或 关注 一波