最近又读了一次Vue的官方文档,每次读感触都不一样,在看到禁用Attribute继承时有些不太理解,继而读了几篇帖子,突然意识到这不是平时面试时背的8种组件间的通信方式之一吗,但是项目中基本没用过;这里想归纳一下Vue组件间的几种通信方式,针对于一些难于理解的方式,之后会出具体的文章。
Vue是数据驱动视图更新的框架, 所以对于Vue来说组件间的数据传递通信非常重要,那么组件之间如何进行数据通信的呢?在回答这问题之前我们要先了解组件之间都存在什么关系?
在组件树中可以看到:
所以针对存在的3中组件关系我们也总结出了4类通信的方法:
props / $emit
、$parent / $children
、 provide / inject
、ref
、$attrs / $listeners
;eventBus
、 Vuex
eventBus
、Vuex
、provide / inject
、$attrs / $listeners
$root
prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。
// section父组件
// 子组件 article.vue
{
{item}}
总结:
prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单
向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。
注意:
prop的类型校验
prop的命名
Vue 实例提供了一个自定义事件的系统。父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件; 子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件,同时也可以传递第二个参数来给父组件传递数据。
// 父组件中
{
{currentIndex}}
// 在子组件中
{
{item}}
$parent property 可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。
// 父组件中
{
{msg}}
// 子组件中
{
{messageA}}
获取父组件的值为: {
{parentVal}}
注意:
$children
是一个数组,是直接儿子的集合,关于具体是第几个儿子,那么儿子里面有个 _uid 属性,可以知道他是第几个元素,是元素的唯一标识符,根据这个属性,我们可以进行其他的操作- 当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。
- 当前实例的直接子组件。需要注意 children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
provide/ inject
是vue2.2.0新增的api, 简单来说就是父组件中通过provide
来提供变量, 然后再子组件中通过inject
来注入变量。
由于$parent property
无法很好的扩展到更深层级的嵌套组件上。使得provide
和 inject
得到了用武之地。
假设有三个组件: A.vue、B.vue、C.vue 其中 C是B的子组件,B是A的子组件
{
{ isShowRouter }}
// B.vue
{
{ demo }}
// C.vue
{
{ demo }}
provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。有关provide详情可以看这里
访问子组件实例或子元素
// 子组件 A.vue
export default {
data() {
return {
name: "Vue.js"
};
},
methods: {
sayHello() {
console.log("hello");
}
}
};
// 父组件 app.vue
$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
eventBus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
缺点就是当项目较大,就容易造成难以维护的灾难。
使用方式:
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
// addtionNum.vue 中发送事件
// showNum.vue 中接收事件
计算和: {
{count}}
import { eventBus } from'event-bus.js'
EventBus.$off('addition', {})
如果使用不善,EventBus会是一种灾难,到底是什么样的“灾难”了?大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus会被移除,这样就导致业务走不下去。还要就是如果业务有反复操作的页面,EventBus在监听的时候就会触发很多次,也是一个非常大的隐患。这时候我们就需要好好处理EventBus在项目中的关系。通常会用到,在vue页面销毁时,同时移除EventBus事件监听。
有关Bus事件详细信息可以看这里
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上。
这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。
通过window.localStorage.getItem(key)
获取数据
通过window.localStorage.setItem(key,value)
存储数据
注意用JSON.parse() / JSON.stringify()
做数据格式转换
localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.
$listeners可以让你在孙子组件改变父组件的值:
隔代传递数据的方法:
在vue2.4中,引入了attrs与listeners, 新增了inheritAttrs 选项。 在版本2.4以前,默认情况下,父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外),将会“回退”且作为普通的HTML特性应用在子组件的根元素上。接下来看一个跨级通信的例子:
// app.vue
// index.vue
// childCom1.vue
name: {
{ name}}
childCom1的$attrs: {
{ $attrs }}
// childCom2.vue
age: {
{ age}}
childCom2: {
{ $attrs }}
关于attrs和listeners 更多详细信息请参考这里
在每个 new Vue 实例的子组件中,其根实例可以通过 $root property 进行访问。例如,在这个根实例中:
// Vue 根实例
new Vue({
data: {
foo: 1
},
computed: {
bar: function () { /* ... */ }
},
methods: {
baz: function () { /* ... */ }
}
})
所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。
// 获取根组件的数据
this.$root.foo
// 写入根组件的数据
this.$root.foo = 2
// 访问根组件的计算属性
this.$root.bar
// 调用根组件的方法
this.$root.baz()
本文参考:vue的8种通信方式