Vue组件API:prop、event、slot

组件的构成

一个再复杂的组件,都是由三部分组成的:prop、event、slot,它们构成了 Vue.js 组件的 API。若是你开发的是一个通用组件,那必定要事先设计好这三部分,由于组件一旦发布,后面再修改 API 就很困难了,使用者都是但愿不断新增功能,修复 bug,而不是常常变动接口。若是你阅读别人写的组件,也能够从这三个部分展开,它们能够帮助你快速了解一个组件的全部功能。html

属性 prop

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

<template>
  <button :class="'i-button-size' + size" :disabled="disabled"></button>
</template>
<script>
  // 判断参数是不是其中之一
  function oneOf (value, validList) {
    for (let i = 0; i < validList.length; i++) {
      if (value === validList[i]) {
        return true;
      }
    }
    return false;
  }

  export default {
    props: {
      size: {
        validator (value) {
          return oneOf(value, ['small', 'large', 'default']);
        },
        default: 'default'
      },
      disabled: {
        type: Boolean,
        default: false
      }
    }
  }
</script>

复制代码

使用组件:数组

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

复制代码

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

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

在使用组件时,也能够传入一些标准的 html 特性,好比 idclassui

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

复制代码

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

插槽 slot

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

<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 的解决方案。不过,它们都是依赖第三方插件的存在,这在开发独立组件时是不可取的...

结语

在组件开发中,最难的环节应当是解耦组件的交互逻辑,尽可能把复杂的逻辑分发到不一样的子组件中,而后彼此创建联系,在这其中,计算属性(computed)和混合(mixins)是两个重要的技术点,合理利用,就能发挥出 Vue最大特色,把状态(数据)的维护交给 Vue.js 处理,咱们只专一在交互上。

相关文章
相关标签/搜索