组件通信常⽤⽅式:
- props
- eventBus
- vuex
⾃定义事件:
- 边界情况
- $parent
- $children
- $root
- $refs
- provide/inject
- ⾮prop特性
// parent
<a name="vjtBr"></a>
### props的形式
```javascript
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
type 可以是下列原生构造函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
可以通过父给子传值方法,使子组件可以访问父组件的方法和作用域
2. 自定义事件
子给父传值
// child
this.$emit('add', good)
// parent
<child @add="handleAdd"></child>
3. $parent/$root/$children
$parent
兄弟组件之间通信可通过共同祖辈搭桥,
// brother1
this.$parent.$on('foo', handle)
// brother2
this.$parent.$emit('foo')
$root
访问vue根实例
$children
父组件可以通过$children
访问子组件实现父子通信。
// parent
this.$children[0].xx = 'xxx'
注意:$children不能保证⼦元素顺序 和$refs有什么区别? refs可以引用组件也可以引用原生
4. $attrs/$listeners
包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外),并且可以通过v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用
// child:并未在props中声明foo
<p>{{$attrs.foo}}</p>
// parent
<HelloWorld foo="foo"/>
- $attrs:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。当一个组件没有声明任何 props 时,它包含所有父作用域的绑定 (class 和 style 除外)。
$listeners:包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。
5. refs
访问子组件实例或子元素,也可以访问html元素
// parent
<HelloWorld ref="hw"/>
mounted() {
this.$refs.hw.xx = 'xxx'
}
6. provide/inject
能够实现祖先和后代之间传值
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
provide和 inject主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个响应式的对象(data里的),那么其对象的 property 还是可响应的
7. 事件总线
任意两个组件之间传值常用事件总线
// 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()
// child1
this.$bus.$on('foo', handle)
// child2
this.$bus.$emit('foo')
实践中通常用Vue代替Bus,因为Vue已经实现了相应接口
两种实现方式
// 1.eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 2.main.js
Vue.prototype.$EventBus = new Vue()
- 触发事件
$emit('event',args)
- 监听事件
$on('event',()=>{..})
- 移除事件
$off('event')
8. Vuex
创建唯一的全局数据管理者store,通过它管理数据并通知组件状态变更。
二 插槽
插槽语法是Vue 实现的内容分发 API,用于复合组件开发。该技术在通用组件库开发中有大量应用。
1. 匿名插槽
// comp1
<div>
<slot></slot>
</div>
// parent
<comp>hello</comp>
2. 具名插槽
将内容分发到子组件指定位置
// comp2
<div>
<slot></slot>
<slot name="content"></slot>
</div>
// parent
<Comp2>
<!-- 默认插槽用default做参数 -->
<template v-slot:default>具名插槽</template>
<!-- 具名插槽用插槽名做参数 -->
<template v-slot:content>内容...</template>
</Comp2>
3. 作用域插槽
分发内容要用到子组件中的数据
// comp3
<div>
<slot :foo="foo"></slot>
</div>
// parent
<Comp3>
<!-- 把v-slot的值指定为作用域上下文对象 -->
<template v-slot:default="slotProps"> 来自子组件数据:{{slotProps.foo}} </template>
</Comp3>
// default可以省略,具名插槽时为具名插槽的名字