vuejs之父子组件的通信【props】和【$emit】

上一篇中谈到了子组件是不能引用父组件或Vue实例挂载的数据。这里Vue实例可看作根组件(root component)。

问题导入

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

如一个页面中,我们从服务器请求到了很多数据。但这些并非全部应用于一个组件来展示,其中一小部分需要包含的子组件来渲染,(这时我的评价是不如交给子组件去请求需要的数据,各组件请求自己的那份数据不是OK了吗),按照设计者的思路,此时并不会让子组件独自发送一个网络请求的,而是直接让其父组件(大组件)将数据传递给子组件(小组件)。嗯,回过头来想想雀食,一旦下面的子组件过多,所造成的冗余请求对服务器来说,简直是个灾难!

组件间的关系

一般来说,组件可以有以下几种关系:

如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。 

如何进行父子组件间的数据通信呢?Vue官方提到:

  • 通过props向子组件传递数据。
  • 通过$emit向父组件传递数据。

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

(注:下面代码示例中,将Vue实例看作父组件,其包含子组件以简化代码)

props基本用法

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

props值的两种写法

方式一:字符串数组,数组中的字符就是传递时的属性名称(attribute)。

方式二:对象,里面可以设置传递时的类型,也可以设置默认值等。

  <div id="app">

    <cpn :cMovies="movies" :cMessage="message"></cpn>

  </div>

  <template id="cpn">

    <div>
      <ul>
        <li v-for="(item, index) in cmovies" :key="index">{{item}}</li>
      </ul>
      <h2>{{cmessage}}</h2>

    </div>
  </template>
   <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
  
    // 父传子:props
    const cpn = {
      template: "#cpn",
      props: ['cmovies', 'cmessage'],// 数组写法,元素虽然是字符串,但可当做变量
      data() {
        return {
        }
      },
      methods: {
      },
    };
    const app = new Vue({
      el: "#app",
      data: {
        message: "你好",
        movies: ["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
      },
      components: {
        cpn
      }
    })
  </script>

使用props数组方式接收父组件data数据时,一定要记住v-bind(或:),否则无法解析,会当成字符串输出。

Props的类型

使用props对象方式的话,需要进行类型验证。

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

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

示例:

Vue.component('my-component', {
  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'].includes(value)
      }
    }
  }
})

prop 对象和数组的默认值为什么要为工厂函数的形式返回?原因在这里

额外的,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。例如,给定下列现成的构造函数:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

你可以使用:

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

来验证 author prop 的值是否是通过 new Person 创建的。

Props的驼峰标识

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

如果你使用字符串模板,那么这个限制就不存在了。

示例:

如果props定义的值只是首字母大写(如Cuser),则绑定数据时使用Cuser或C-user均可。

自定义事件 

子传父应用场景说明参考

通过自定义事件($emit)可以实现子组件传递数据或事件到父组件中。

主要流程:

  • 在子组件中,通过 $emit来触发事件。
  • 在父组件中,通过 v-on来监听子组件事件。

这部分直接看示例代码吧,用白话讲有点绕。  

 <!-- 父组件 -->
  <div id="app">
    <!-- 不写参数默认传递btnClick的item -->
    <cpn @itemclick="cpnClcik"></cpn>

  </div>

  <!-- 子组件 -->
  <template id="cpn">

    <div>
      <button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
    </div>
  </template>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>
    // 父传子:props
    const cpn = {
      template: "#cpn",
      data() {
        return {
          categoties: [{
              id: 'aaa',
              name: '热门推荐'
            },
            {
              id: 'bbb',
              name: '手机数码'
            },
            {
              id: 'ccc',
              name: '家用家电'
            },
            {
              id: 'ddd',
              name: '电脑办公'
            },
          ]
        }
      },
      methods: {
        btnClick(item) {
          this.$emit('itemclick', item)
        }
      },
    };
    const app = new Vue({
      el: "#app",
      data() {
        return {

        }
      },
      methods: {
        cpnClcik(item) {
          console.log('cpnClick'+item.name);
        }
      },
      components: {
        cpn
      },
    })
  </script>

参考:

https://v2.cn.vuejs.org/v2/guide/components-props.html#Prop-%E7%9A%84%E5%A4%A7%E5%B0%8F%E5%86%99-camelCase-vs-kebab-case

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值