vue组件通信方式
模块化,组件化在前端框架中已经是很常见的玩意了,所以了解组件、模块之间的通信那就很重要了。 组件通信方式的话,有父与子,兄弟之间,祖先与子等方式。这儿主要以父子关系取阐述这些通信机制。
一、props方式
父--->子
父组件传props给子组件
<son :what="name" :hello="world" :callback="thing"></son>
设置了传递的值的内容,name为一个对象,hello为字符串。
data() {
return {
name: {
a: 1,
b: 2
},
world: '123',
thing: function() {
console.log(arguments)
}
}
}
子组件中
props: {
what: {
type: Object,
default: {}
},
hello: {
type: String,
default: '456'
},
callback: {
type: Function,
default: function() {}
}
}
props类似于传参的方式传给子组件,子组件中就可直接使用this去访问和使用。
子--->父
直接修改引用类型的数据。
前面例子中,father组件传给了son组件一个对象,son组件内可以直接修改传入的对象,由于引用关系,father内的组件值也会改变,此时我们只需要在father设置一个监听就行了。
// 监听传给子组件的值
watch: {
name: {
handler(nV, oV) {
console.log(nV);
},
deep: true
}
}
father组件就能实时获取son组件对传入值操作的情况了
传入函数方式
前面的例子中,father组件向son组件传了thing这个方法,而在son组件中可以通过调用这个方法向father组件传递数据。
mounted() {
// 定时模拟用户操作了啥后
setTimeout(() => {
this.callback(1, 2, 3);
}, 2000);
}
而在father组件中定义callback的地方就可以拿到这几个参数啦。
注意:我们直接修改father传给son的非引用类型的值时,是不成功的,而且vue会提示错误。
二、$attrs与$listeners
假如有父、子、孙三层组件,父要向孙通过props的方式传递数据,按照第一种方式的话得父调子时写一层,子调孙时再写一层,很繁琐且读起来很不便捷。 所以vue在2.4版本中增加了$attrs和$listeners来处理这个问题。$attrs使得属性能够穿过中间组件,而$listeners是处理绑定事件的。 父组件中,在先前的例子上加了个函数,data的内容不去改变。
<son :what="name" :hello="world" :callback="thing" @show="showMe"></son>
methods: {
showMe(a) {
console.log('I am father', a);
}
}
在son组件中,只使用hello这个变量值,其他的数据都要给grandchild组件。
<grandchild v-bind="$attrs" v-on="$listeners"></grandchild>
export default {
components: {
grandchild
},
props: ['hello']
};
son组件中只接收了hello,剩余的属性将通过$attrs传递给了grandchild组件。而$listeners也将绑定在son上的事件传了下去。在grandchild中,我们打印通过props传过来的属性的内容,并emit祖父级组件上的事件,
export default {
created () {
console.log(this.$attrs);
this.$emit('show', 'yes');
}
};
此时控制台的打印:
注:inheritAttrs配合使用,表示组件是否把未被注册的props呈现为普通的HTML属性。
三、$on与$emit
该方式,严格说只有子组件向父组件通信。 父组件中:
<son @what="show"></son>
methods: {
show() {
console.log(arguments);
}
}
子组件中通过$emit去调用父组件中的方法,
mounted() {
// 定时模拟用户操作了啥后
setTimeout(() => {
this.$emit('what', 1, 2)
}, 1000);
}
这种方式是不是比props方式中直接传函数更“和谐”些呢?
四、Bus方式
在外部定义一个总的js文件用于处理组件之间的通信,该方法不止是父子组件之间的通信,只要引用该文件,任何两个组件之间都是可以进行消息传递的(父子,兄弟等等)。
该种通信下,其实没有父子关系,只有监听与触发的关系,为了方便理解,还是使用父与子。 新建一个bus.js文件,编辑该文件为:
import Vue from 'vue';
export default new Vue;
father和son组件中都增加对bus.js文件的引用
import Bus from './bus.js';
father组件在创建之后,使用$on增加事件的监听,
created() {
Bus.$on('nice', (a, b) => {
console.log(a, b);
});
}
son组件在mount后触发对应的nice事件:
mounted() {
Bus.$emit('nice', {
name: 111
}, 123);
}
此时父组件中就能获取到对应的参数了。当然反过来,子组件$on,父组件$emit,就成了父组件向子组件通信的方式了。
五、inject与provide
父组件中使用provide来提供变量,在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provide中的数据。该方式只要在父组件的生命周期内,子组件都可以注入。
父组件中
<son></son>
provide: {
name: 'demo'
}
子组件中对应的inject该变量就行了,当然为了安全起见,可以给他设定默认值。
inject: {
name: {
default: '1234567890'
}
}
// inject: ['name'] // 简写方式
在element-ui中的form等表单组件时,用的还是比较多的。为了简单,仍然用父子来代替多层级的关系。
六、$parent和$children
vue2中没有了broadcast和dispatch这种通信方式,但是通过对$parent和$children的处理,我们仍可以模拟这种方式,例如在element-ui库中,作为混入的emitter.js文件中,就用此实现了事件的触发机制。
详细的内容可以查看:element-ui emitter.js文件源码学习。
七、$ref
被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的$refs对象上。如果在普通的DOM元素上使用,那么指向的就是普通的DOM元素。
通过直接取到对应的组件来获取对应的值。 比如在子组件中简单的变量,
data() {
return {
name: 'aaa'
};
}
在父组件中,只要取到对应的ref就可以获得子组件下所有的属性,
<son ref="son"></son>
mounted() {
console.log(this.$refs.son.name);
}
控制台上就可以获取到对应的属性了。
八、vuex
vue的状态管理工具,不详细说了,网上有很多的教程,而且我研究的也不深入。
九、本地存储
cookie,storage,indexedDB等一些用于前端本地存储的也是可以用来给组件之间交互的。
十、总结
我们来总结下,非vue的方式有使用本都存储的方式,而以上所有的方式都可以用于父子组件之间的通信。父到孙组件的话,有$attrs和$listeners,bus方式,inject和provide,vuex。
兄弟直接可以使用bus方式和vuex或者先传给父组件,再广播(利用$parent和$children查找也可以)。
bus方式可以在任何关系的组件中使用,不适用vuex的简单项目中可以选取,而项目复杂的可以上vuex进行管理。