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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM