• Vue.js 组件的三个 API:prop、event、slot


    组件的构成

    一个再复杂的组件,都是由三部分组成的:prop、event、slot,它们构成了 Vue.js 组件的 API。如果你开发的是一个通用组件,那一定要事先设计好这三部分,因为组件一旦发布,后面再修改 API 就很困难了,使用者都是希望不断新增功能,修复 bug,而不是经常变更接口。如果你阅读别人写的组件,也可以从这三个部分展开,它们可以帮助你快速了解一个组件的所有功能。

    属性 prop

    prop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。写通用组件时,props 最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,这点在组件开发中很重要,然而很多人却忽视,直接使用 props 的数组用法,这样的组件往往是不严谨的。比如我们封装一个按钮组件 <i-button>

     1 <template>
     2  <button :class="'i-button-size' + size" :disabled="disabled"></button>
     3 </template>
     4 <script>
     5  // 判断参数是否是其中之一
     6  function oneOf (value, validList) {
     7    for (let i = 0; i < validList.length; i++) {
     8      if (value === validList[i]) {
     9        return true;
    10      }
    11    }
    12    return false;
    13  }
    14 
    15  export default {
    16    props: {
    17      size: {
    18        validator (value) {
    19          return oneOf(value, ['small', 'large', 'default']);
    20        },
    21        default: 'default'
    22      },
    23      disabled: {
    24        type: Boolean,
    25        default: false
    26      }
    27    }
    28  }
    29 </script>

    使用组件:

    <i-button size="large"></i-button>
    <i-button disabled></i-button>

    组件中定义了两个属性:尺寸 size 和 是否禁用 disabled。其中 size 使用 validator 进行了值的自定义验证,也就是说,从父级传入的 size,它的值必须是指定的 small、large、default 中的一个,默认值是 default,如果传入这三个以外的值,都会抛出一条警告。

    要注意的是,组件里定义的 props,都是单向数据流,也就是只能通过父级修改,组件自己不能修改 props 的值,只能修改定义在 data 里的数据,非要修改,也是通过后面介绍的自定义事件通知父级,由父级来修改。

    在使用组件时,也可以传入一些标准的 html 特性,比如 idclass

    <i-button id="btn1" class="btn-submit"></i-button>

    这样的 html 特性,在组件内的 <button> 元素上会继承,并不需要在 props 里再定义一遍。这个特性是默认支持的,如果不期望开启,在组件选项里配置 inheritAttrs: false 就可以禁用了。

    插槽 slot

    如果要给上面的按钮组件 <i-button> 添加一些文字内容,就要用到组件的第二个 API:插槽 slot,它可以分发组件的内容,比如在上面的按钮组件中定义一个插槽:

    <template>
     <button :class="'i-button-size' + size" :disabled="disabled">
       <slot></slot>
     </button>
    </template>

    这里的 <slot> 节点就是指定的一个插槽的位置,这样在组件内部就可以扩展内容了:

    <i-button>按钮 1</i-button>
    <i-button>
     <strong>按钮 2</strong>
    </i-button>

    当需要多个插槽时,会用到具名 slot,比如上面的组件我们再增加一个 slot,用于设置另一个图标组件:

    <template>
     <button :class="'i-button-size' + size" :disabled="disabled">
       <slot name="icon"></slot>
       <slot></slot>
     </button>
    </template>
    <i-button>
     <i-icon slot="icon" type="checkmark"></i-icon>
     按钮 1
    </i-button>

    这样,父级内定义的内容,就会出现在组件对应的 slot 里,没有写名字的,就是默认的 slot。

    在组件的 <slot> 里也可以写一些默认的内容,这样在父级没有写任何 slot 时,它们就会出现,比如:

    <slot>提交</slot>

    自定义事件 event

    现在我们给组件 <i-button> 加一个点击事件,目前有两种写法,我们先看自定义事件 event(部分代码省略):

    <template>
     <button @click="handleClick">
       <slot></slot>
     </button>
    </template>
    <script>
     export default {
       methods: {
         handleClick (event) {
           this.$emit('on-click', event);
         }
       }
     }
    </script>

    通过 $emit,就可以触发自定义的事件 on-click ,在父级通过 @on-click 来监听:

    <i-button @on-click="handleClick"></i-button>

    上面的 click 事件,是在组件内部的 <button> 元素上声明的,这里还有另一种方法,直接在父级声明,但为了区分原生事件和自定义事件,要用到事件修饰符 .native,所以上面的示例也可以这样写:

    <i-button @click.native="handleClick"></i-button>

    如果不写 .native 修饰符,那上面的 @click 就是自定义事件 click,而非原生事件 click,但我们在组件内只触发了 on-click 事件,而不是 click,所以直接写 @click 会监听不到。

    组件的通信

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

    A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。组件间经常会通信,Vue.js 内置的通信手段一般有两种:

    • ref:给元素或组件注册引用信息;

    • $parent / $children:访问父 / 子实例。

    这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据,比如下面的示例中,用 ref 来访问组件(部分代码省略):

    // component-a
    export default {
     data () {
       return {
         title: 'Vue.js'
       }
     },
     methods: {
       sayHello () {
         window.alert('Hello');
       }
     }
    }
    <template>
     <component-a ref="comA"></component-a>
    </template>
    <script>
     export default {
       mounted () {
         const comA = this.$refs.comA;
         console.log(comA.title);  // Vue.js
         comA.sayHello();  // 弹窗
       }
     }
    </script>

    $parent$children 类似,也是基于当前上下文访问父组件或全部子组件的。

    这两种方法的弊端是,无法在跨级兄弟间通信,比如下面的结构:

    // parent.vue
    <component-a></component-a>
    <component-b></component-b>
    <component-b></component-b>

    我们想在 component-a 中,访问到引用它的页面中(这里就是 parent.vue)的两个 component-b 组件,那这种情况下,就得配置额外的插件或工具了,比如 Vuex 和 Bus 的解决方案

    基于 Vue.js 开发独立组件,并不是新奇的挑战,坦率地讲,它本质上还是 JavaScript。掌握了 Vue.js 组件的这三个 API 后,剩下的便是程序的设计。在组件开发中,最难的环节应当是解耦组件的交互逻辑,尽量把复杂的逻辑分发到不同的子组件中,然后彼此建立联系,在这其中,计算属性(computed)和混合(mixins)是两个重要的技术点,合理利用,就能发挥出 Vue.js 语言的最大特点:把状态(数据)的维护交给 Vue.js 处理,我们只专注在交互上。

    来源:https://mp.weixin.qq.com/s/BWdGWFNVpdgG1tVQ0Lm9XA

  • 相关阅读:
    iOS进阶二-KVC
    iOS进阶一OC对象的本质
    2019-01-19
    2019-01-12
    2019
    2018-12-23 随笔
    2018-12-18 随笔
    2018-12-10
    2018-12-01
    2018.11.23 随笔
  • 原文地址:https://www.cnblogs.com/leungUwah/p/10454154.html
Copyright © 2020-2023  润新知