首页 > 技术文章 > Vue 父子组件的访问

landuo629 2020-03-06 13:35 原文

父子组件的通信

  • 首先,我们指导正常情况下子组件是不能访问父组件或者Vue实例的数据的

  • 但是,在开发中,往往一些数据确实需要从上层传递到下层

    • 比如在一个页面中,我们从服务器请求到了很多的数据。
    • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
    • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)
  • 那如何进行父子组件间的通信呢?Vue官方提到

    • 通过props向子组件传递数据
    • 通过事件向父组件发送消息
  • 在下面的代码中,我直接将Vue实例当做父组件,并且其中包含子组件来简化代码。

  • 真实的开发中,Vue实例和子组件的通信父组件和子组件的通信过程是一样的。

props基本用法

  • 在组件中,使用选项props来声明需要从父级接收到的数据。

  • props的值有两种方式:

    • 方式一:字符串数组,数组中的字符串就是传递时的名称。
    • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
  • 我们先来看一个最简单的props传递:

<div id="app">
  <child-cpn :message="message"></child-cpn>
</div>

<template id="child-cpn">
  <div>显示的信息: {{message}}</div>
</template>

<script>
  let app  = new Vue({
    el: '#app',
    data: {
      message: 'Hello lazy'
    },
    components: {
      'child-cpn': {
        template: '#child-cpn',
        props: ['message']
      }
    }
  })
</script>
  • 步骤
    1. Vue实例中的data
    2. 子组件的props
    3. 通过:message='message'将data中的数据传给props
    4. 将props中的数据显示在子组件中

props数据验证

  • 在前面,我们的props选项是使用一个数组。
  • 我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。
  • 验证都支持哪些数据类型呢?
    • String
    • Number
    • Boolean
    • Array
    • Object
    • Date
    • Function
    • Symbol
Vue.components('my-components',{
  props: {
    // 基础的类型检查('null' 匹配任何类型)
    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
      }
    }
  }
})
  • 当我们有自定义构造函数时,验证也支持自定义的类型
function Person (firstName, LastName){
  this.firstName = firstName
  this.lastName = lastName
}

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

子级向父级传递

  • props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。

  • 我们应该如何处理呢?这个时候,我们需要使用自定义事件来完成。

  • 什么时候需要自定义事件呢?

    • 当子组件需要向父组件传递数据时,就要用到自定义事件了。
    • 我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
  • 自定义事件的流程:

    • 在子组件中,通过$emit()来触发事件。
    • 在父组件中,通过v-on来监听子组件事件。
<!--父组件模板-->
<div id="app">
  <!-- 监听子组件事件item-click -->
  <cpn @item-click="cpnClick"></cpn>
</div>

<!--子组件模板-->
<template id="cpn">
  <div>
    <button v-for="item in categories"
            @click="btnClick(item)">
      {{item.name}}
    </button>
  </div>
</template>

<script src="../js/vue.js"></script>
<script>

  // 1.子组件
  const cpn = {
    template: '#cpn',
    data() {
      return {
        categories: [
          {id: 'aaa', name: '热门推荐'},
          {id: 'bbb', name: '手机数码'},
          {id: 'ccc', name: '家用家电'},
          {id: 'ddd', name: '电脑办公'},
        ]
      }
    },
    methods: {
      btnClick(item) {
        // 发射事件: 自定义事件
        this.$emit('item-click', item)
      }
    }
  }

  // 2.父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn
    },
    methods: {
      <!-- 定义好一个函数 -->
      cpnClick(item) {
        console.log('cpnClick', item.id, item.name);
      }
    }
  })
</script>

  1. 子组件cpn发射自定义事件item-click,携带参数item
  2. 父组件app在methods中定义一个函数
  3. 父组件app用v-on监听子组件事件item-click,并将事件指向一个在methods中定义好的函数

父子组件的访问方式: $children

  • 有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。

    • 父组件访问子组件:使用$children或$refs
    • 子组件访问父组件:使用$parent
  • $children的缺陷:

    • 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
    • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
    • 有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

父子组件的访问方式: $refs

  • $refs的使用:
    • $refs和ref指令通常是一起使用的。
    • 首先,我们通过ref给某一个子组件绑定一个特定的ID。
    • 其次,通过this.$refs.ID就可以访问到该组件了。
<child-cpn1 ref="child1"></child-cpn1>
<child-cpn2 ref="child2"></child-cpn2>
<button @click="showRefsCpn"></button>

showRefscpn() {
      console.log(this.$refs.child1.message);
       console.log(this.$refs.child2.message);
    }

父子组件的访问方式: $parent

  • 如果我们想在子组件中直接访问父组件,可以通过$parent
  • 注意事项:
    • 尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
    • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
    • 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
    • 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护

非父子组件通信

  • 刚才我们讨论的都是父子组件间的通信,那如果是非父子关系呢?

    • 非父子组件关系包括多个层级的组件,也包括兄弟组件的关系。
  • 在Vue1.x的时候,可以通过$dispatch和$broadcast完成

    • $dispatch用于向上级派发事件
    • $broadcast用于向下级广播事件
    • 但是在Vue2.x都被取消了
  • 在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。

    • 但是这种方案和直接使用Vuex的状态管理方案还是逊色很多。

推荐阅读