组件传值
父子组件
props
/$emit
ref
this.$refs.xxx
通过子组件的实例来获取子组件数据或方法调用$parent
this.$parent.xxx
获取父组件数据或方法调用
兄弟组件
$parent
$root
eventbus
vuex
跨层级关系
$attrs
/listeners
vuex
provide
/inject
$root
1. $attrs
与 $listeners
v2.4版本新增
解决跨组件通信问题,其它方式都有缺陷:
props
需要一层层传递,冗余难维护vuex
适合大些的场景provide / inject
自身有缺陷eventBus
可读性低,可维护下也不太好
目前用到的场景是最三方组件的再次封装,能直接继承三方组件的所有属性与方法
2. $attrs
包含了父作用域中不作为 prop
被识别 (且获取) 的 attribute
绑定 (class 和 style 除外)。当一个组件没有声明任何 prop
时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
它有个布尔值的 inheritAttrs
属性,用于控制是否启用 attribute
,默认为开启。
HTML
<!-- 简单来说,在父->子->孙结构的组件中,子组件的这样写法 -->
<!-- 父->孙组件就能直接使用 props 与 $emit 传递数据与接受事件了 -->
<dom-child v-bind="$attrs" v-on="$listeners" />
<!-- 简单来说,在父->子->孙结构的组件中,子组件的这样写法 -->
<!-- 父->孙组件就能直接使用 props 与 $emit 传递数据与接受事件了 -->
<dom-child v-bind="$attrs" v-on="$listeners" />
3. $listeners
包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
示例1:
HTML
<!-- 父组件 -->
<demo-parent name="zhangsan" />
<!-- 子组件 this.$attrs.name -->
<div>{{ $attrs.name }}</div>
<!-- 父组件 -->
<demo-parent name="zhangsan" />
<!-- 子组件 this.$attrs.name -->
<div>{{ $attrs.name }}</div>
4. provide
/ inject
可以给组件所有后代传递数据,最大问题是绑定不是响应式的,同时多人维护时也不够透明,但是在实现通用组件时还是很好用的。
举个例子:element-ui
中,组件会有个 size
属性,比如表单 el-form
的 size
属性会影响其所有表单内的组件 size
,我们可以使用 provide
与 inject
实现 size
的传递,即使中级隔着非表单组件。
5. eventBus
兄弟组件间通信,目前使用来看,代码维护与可读性都不好
- 创建一个中央事件总线EventBus
- 兄弟组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
- 另一个兄弟组件通过$on监听自定义事件
JS
// 创建一个中央时间总线类
class Bus {
constructor() {
this.callbacks = {}; // 存放事件的名字
}
$on(name, fn) {
this.callbacks[name] = this.callbacks[name] || [];
this.callbacks[name].push(fn);
}
$emit(name, args) {
if (this.callbacks[name]) {
this.callbacks[name].forEach((cb) => cb(args));
}
}
}
// main.js
Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上
// 另一种方式
Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能
// 创建一个中央时间总线类
class Bus {
constructor() {
this.callbacks = {}; // 存放事件的名字
}
$on(name, fn) {
this.callbacks[name] = this.callbacks[name] || [];
this.callbacks[name].push(fn);
}
$emit(name, args) {
if (this.callbacks[name]) {
this.callbacks[name].forEach((cb) => cb(args));
}
}
}
// main.js
Vue.prototype.$bus = new Bus() // 将$bus挂载到vue实例的原型上
// 另一种方式
Vue.prototype.$bus = new Vue() // Vue已经实现了Bus的功能
child1.vue
JS
this.$bus.$emit('foo')
this.$bus.$emit('foo')
child2.vue
JS
this.$bus.$on('foo', this.handle)
this.$bus.$on('foo', this.handle)