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 處理,咱們只專一在交互上。

相關文章
相關標籤/搜索