vue通信方式有很多,项目中用的比较多的的有pros、vuex、$emit/$on
这3种,还有provide/inject
(适合高阶组件)、$attrs和$listeners
(适合高阶组件)以及$parent/$child/ref、eventBus
等这3种方式。
1、pros
父子组件通信
vue使用props有两种形式形式实现父子组件之间的数据传递。官方描述:props
// 第一种方式
// parent.vue
<template>
<div>
<child :message="childName" @change= "changeMessage" />
</div>
</template>
<script>
import childfrom './child';
export default {
components: {
child,
},
data() {
return {
message: '我是父组件传递的数据',
};
},
methods: {
changeMessage(val) {
this.message= val;
},
},
};
</script>
// child
<template>
<div>{{message}}</div>
<button @click="changeMessage">change</button>
</template>
<script>
export default {
props: ['message'],
methods: {
changeMessage() {
this.$emit('change', '我是传递给父组件的数据')
}
}
};
</script>
父组件通过:message向子组件传递数据,子组件通过$emit触发数据变化,父组件使用$on来监听子组件数据的变化。
// 第二种方式
// parent.vue
<template>
<div>
<child v-modle="message" />
</div>
</template>
<script>
import child from './child';
export default {
components: {
child,
},
data() {
return {
message: '我是父组件传递的数据',
};
}
};
</script>
// child
<template>
<input v-model="sync_value" />
</template>
<script>
import propsync from '@/mixins/propsync'
export default {
mixins: [propsync],
props: {
value: {
default: '',
isSync: true
}
}
};
</script>
第二种方式是自动化的props双向数据绑定,直接引入mixin,并在配置中声明mixin即可: mixins: [propsync],
propsync默认会将所有props创建双向绑定,可通过isSync:false
来声明此props不需要创建双向绑定。
2、provide/inject
一般适用于高阶组件,祖父组件想多级子组件传递数据,官方描述:provide / inject。以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
provide 选项是:一个对象或返回一个对象的函数
inject 选项是:一个字符串数组,或 一个对象,对象的 key 是本地的绑定名
// parent.vue
<script>
export default {
provide() {
message: '我是爷爷'
},
};
</script>
// child
<script>
export default {
inject: ['message'],
created () {
console.log(this.message) // => "我是爷爷"
}
};
</script>
3、$attrs和$listeners
$attr: 向 子组件 传递,当前组件 没有接收的,父组件传递下来的 prop 。
$listeners: 向父组件传递,当前组件没有接收的,子组件抛出的自定义事件。
// parent.vue
<template>
<input type="text" v-bind="$attrs" v-on="$listeners">
</template>
// child.vue
<template>
<input type="text" v-bind="$attrs" @input="observerInput">
</template>
<script>
export default {
methods: {
// 拦截内置事件
observerInput (el) {
this.$emit('input', el.target.value)
},
},
}
</script>
4、eventBus
跨组件通信
class EventBus{
constructor(){
this.event=Object.create(null);
};
//注册事件
on(name,fn){
if(!this.event[name]){
//一个事件可能有多个监听者
this.event[name]=[];
};
this.event[name].push(fn);
};
//触发事件
emit(name,...args){
//给回调函数传参
this.event[name]&&this.event[name].forEach(fn => {
fn(...args)
});
};
//只被触发一次的事件
once(name,fn){
//在这里同时完成了对该事件的注册、对该事件的触发,并在最后取消该事件。
const cb=(...args)=>{
//触发
fn(...args);
//取消
this.off(name,fn);
};
//监听
this.on(name,cb);
};
//取消事件
off(name,offcb){
if(this.event[name]){
let index=this.event[name].findIndex((fn)=>{
return offcb===fn;
})
this.event[name].splice(index,1);
if(!this.event[name].length){
delete this.event[name];
}
}
}
}
声明一个全局Vue实例变量 EventBus
, 把所有的通信数据,事件监听都存储到这个变量上。这样就达到在组件间数据共享了,有点类似于 vuex。但这种方式只适用于极小的项目,复杂项目还是推荐 vuex。