组件通信常⽤⽅式:

  • props
  • eventBus
  • vuex

⾃定义事件:

  • 边界情况
    • $parent
    • $children
    • $root
    • $refs
    • provide/inject
  • ⾮prop特性
    • $attrs
    • $listeners

      一 组件通信

      1. props

      父给子传值 ```vue // child props: { msg: String }

// parent

  1. <a name="vjtBr"></a>
  2. ### props的形式
  3. ```javascript
  4. props: {
  5. // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
  6. propA: Number,
  7. // 多个可能的类型
  8. propB: [String, Number],
  9. // 必填的字符串
  10. propC: {
  11. type: String,
  12. required: true
  13. },
  14. // 带有默认值的数字
  15. propD: {
  16. type: Number,
  17. default: 100
  18. },
  19. // 带有默认值的对象
  20. propE: {
  21. type: Object,
  22. // 对象或数组默认值必须从一个工厂函数获取
  23. default: function () {
  24. return { message: 'hello' }
  25. }
  26. },
  27. // 自定义验证函数
  28. propF: {
  29. validator: function (value) {
  30. // 这个值必须匹配下列字符串中的一个
  31. return ['success', 'warning', 'danger'].indexOf(value) !== -1
  32. }
  33. }
  34. }

type 可以是下列原生构造函数中的一个:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

可以通过父给子传值方法,使子组件可以访问父组件的方法和作用域

2. 自定义事件

子给父传值

  1. // child
  2. this.$emit('add', good)
  3. // parent
  4. <child @add="handleAdd"></child>

3. $parent/$root/$children

$parent

兄弟组件之间通信可通过共同祖辈搭桥,

  1. // brother1
  2. this.$parent.$on('foo', handle)
  3. // brother2
  4. this.$parent.$emit('foo')

$root

访问vue根实例

$children

父组件可以通过$children访问子组件实现父子通信。

  1. // parent
  2. this.$children[0].xx = 'xxx'

注意:$children不能保证⼦元素顺序 和$refs有什么区别? refs可以引用组件也可以引用原生

4. $attrs/$listeners

包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外),并且可以通过v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用

  1. // child:并未在props中声明foo
  2. <p>{{$attrs.foo}}</p>
  3. // parent
  4. <HelloWorld foo="foo"/>
  • $attrs:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。当一个组件没有声明任何 props 时,它包含所有父作用域的绑定 (class 和 style 除外)。

    $listeners:包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。

https://segmentfault.com/a/1190000022708579

5. refs

访问子组件实例或子元素,也可以访问html元素

  1. // parent
  2. <HelloWorld ref="hw"/>
  3. mounted() {
  4. this.$refs.hw.xx = 'xxx'
  5. }

6. provide/inject

能够实现祖先和后代之间传值

  1. // 父级组件提供 'foo'
  2. var Provider = {
  3. provide: {
  4. foo: 'bar'
  5. },
  6. // ...
  7. }
  8. // 子组件注入 'foo'
  9. var Child = {
  10. inject: ['foo'],
  11. created () {
  12. console.log(this.foo) // => "bar"
  13. }
  14. // ...
  15. }

provide和 inject主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个响应式的对象(data里的),那么其对象的 property 还是可响应的

https://cn.vuejs.org/v2/api/#provide-inject

7. 事件总线

任意两个组件之间传值常用事件总线

  1. // Bus:事件派发、监听和回调管理
  2. class Bus {
  3. constructor(){
  4. this.callbacks = {}
  5. }
  6. // 监听
  7. $on(name, fn){
  8. this.callbacks[name] = this.callbacks[name] || [] this.callbacks[name].push(fn)
  9. }
  10. // 派发
  11. $emit(name, args){
  12. if(this.callbacks[name]){
  13. this.callbacks[name].forEach(cb => cb(args))
  14. }
  15. }
  16. }
  17. // main.js
  18. Vue.prototype.$bus = new Bus()
  19. // child1
  20. this.$bus.$on('foo', handle)
  21. // child2
  22. this.$bus.$emit('foo')

实践中通常用Vue代替Bus,因为Vue已经实现了相应接口


两种实现方式

  1. // 1.eventBus.js
  2. import Vue from 'vue'
  3. export const EventBus = new Vue()
  4. // 2.main.js
  5. Vue.prototype.$EventBus = new Vue()

二 插槽

插槽语法是Vue 实现的内容分发 API,用于复合组件开发。该技术在通用组件库开发中有大量应用。

1. 匿名插槽

  1. // comp1
  2. <div>
  3. <slot></slot>
  4. </div>
  5. // parent
  6. <comp>hello</comp>

2. 具名插槽

将内容分发到子组件指定位置

  1. // comp2
  2. <div>
  3. <slot></slot>
  4. <slot name="content"></slot>
  5. </div>
  6. // parent
  7. <Comp2>
  8. <!-- 默认插槽用default做参数 -->
  9. <template v-slot:default>具名插槽</template>
  10. <!-- 具名插槽用插槽名做参数 -->
  11. <template v-slot:content>内容...</template>
  12. </Comp2>

3. 作用域插槽

分发内容要用到子组件中的数据

  1. // comp3
  2. <div>
  3. <slot :foo="foo"></slot>
  4. </div>
  5. // parent
  6. <Comp3>
  7. <!-- v-slot的值指定为作用域上下文对象 -->
  8. <template v-slot:default="slotProps"> 来自子组件数据:{{slotProps.foo}} </template>
  9. </Comp3>
  10. // default可以省略,具名插槽时为具名插槽的名字