如果您已經(jīng)對 Vue.js 組件的基礎(chǔ)用法了如指掌,可以跳過本小節(jié),不過當(dāng)做復(fù)習(xí)稍讀一下也無妨。
組件的構(gòu)成
一個(gè)再復(fù)雜的組件,都是由三部分組成的:prop、event、slot,它們構(gòu)成了 Vue.js 組件的 API。如果你開發(fā)的是一個(gè)通用組件,那一定要事先設(shè)計(jì)好這三部分,因?yàn)榻M件一旦發(fā)布,后面再修改 API 就很困難了,使用者都是希望不斷新增功能,修復(fù) bug,而不是經(jīng)常變更接口。如果你閱讀別人寫的組件,也可以從這三個(gè)部分展開,它們可以幫助你快速了解一個(gè)組件的所有功能。
屬性 prop
prop 定義了這個(gè)組件有哪些可配置的屬性,組件的核心功能也都是它來確定的。寫通用組件時(shí),props 最好用對象的寫法,這樣可以針對每個(gè)屬性設(shè)置類型、默認(rèn)值或自定義校驗(yàn)屬性的值,這點(diǎn)在組件開發(fā)中很重要,然而很多人卻忽視,直接使用 props 的數(shù)組用法,這樣的組件往往是不嚴(yán)謹(jǐn)?shù)?。比如我們封裝一個(gè)按鈕組件 <i-button>
:
<template>
<button :class="'i-button-size' + size" :disabled="disabled"></button>
</template>
<script>
// 判斷參數(shù)是否是其中之一
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>
組件中定義了兩個(gè)屬性:尺寸 size 和 是否禁用 disabled。其中 size 使用 validator 進(jìn)行了值的自定義驗(yàn)證,也就是說,從父級傳入的 size,它的值必須是指定的 small、large、default 中的一個(gè),默認(rèn)值是 default,如果傳入這三個(gè)以外的值,都會(huì)拋出一條警告。
要注意的是,組件里定義的 props,都是單向數(shù)據(jù)流,也就是只能通過父級修改,組件自己不能修改 props 的值,只能修改定義在 data 里的數(shù)據(jù),非要修改,也是通過后面介紹的自定義事件通知父級,由父級來修改。
在使用組件時(shí),也可以傳入一些標(biāo)準(zhǔn)的 html 特性,比如 id、class:
<i-button id="btn1" class="btn-submit"></i-button>
這樣的 html 特性,在組件內(nèi)的 <button>
元素上會(huì)繼承,并不需要在 props 里再定義一遍。這個(gè)特性是默認(rèn)支持的,如果不期望開啟,在組件選項(xiàng)里配置 inheritAttrs: false 就可以禁用了。
插槽 slot
如果要給上面的按鈕組件 <i-button>
添加一些文字內(nèi)容,就要用到組件的第二個(gè) API:插槽 slot,它可以分發(fā)組件的內(nèi)容,比如在上面的按鈕組件中定義一個(gè)插槽:
<template>
<button :class="'i-button-size' + size" :disabled="disabled">
<slot></slot>
</button>
</template>
這里的 <slot>
節(jié)點(diǎn)就是指定的一個(gè)插槽的位置,這樣在組件內(nèi)部就可以擴(kuò)展內(nèi)容了:
<i-button>按鈕 1</i-button>
<i-button>
<strong>按鈕 2</strong>
</i-button>
當(dāng)需要多個(gè)插槽時(shí),會(huì)用到具名 slot,比如上面的組件我們再增加一個(gè) slot,用于設(shè)置另一個(gè)圖標(biāo)組件:
<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>
這樣,父級內(nèi)定義的內(nèi)容,就會(huì)出現(xiàn)在組件對應(yīng)的 slot 里,沒有寫名字的,就是默認(rèn)的 slot。
在組件的 <slot>
里也可以寫一些默認(rèn)的內(nèi)容,這樣在父級沒有寫任何 slot 時(shí),它們就會(huì)出現(xiàn),比如:
<slot>提交</slot>
自定義事件 event
現(xiàn)在我們給組件 <i-button>
加一個(gè)點(diǎn)擊事件,目前有兩種寫法,我們先看自定義事件 event(部分代碼省略):
<template>
<button @click="handleClick">
<slot></slot>
</button>
</template>
<script>
export default {
methods: {
handleClick (event) {
this.$emit('on-click', event);
}
}
}
</script>
通過 $emit,就可以觸發(fā)自定義的事件 on-click ,在父級通過 @on-click 來監(jiān)聽:
<i-button @on-click="handleClick"></i-button>
上面的 click 事件,是在組件內(nèi)部的 <button>
元素上聲明的,這里還有另一種方法,直接在父級聲明,但為了區(qū)分原生事件和自定義事件,要用到事件修飾符 .native,所以上面的示例也可以這樣寫:
<i-button @click.native="handleClick"></i-button>
如果不寫 .native 修飾符,那上面的 @click 就是自定義事件 click,而非原生事件 click,但我們在組件內(nèi)只觸發(fā)了 on-click 事件,而不是 click,所以直接寫 @click 會(huì)監(jiān)聽不到。
組件的通信
一般來說,組件可以有以下幾種關(guān)系:
A 和 B、B 和 C、B 和 D 都是父子關(guān)系,C 和 D 是兄弟關(guān)系,A 和 C 是隔代關(guān)系(可能隔多代)。組件間經(jīng)常會(huì)通信,Vue.js 內(nèi)置的通信手段一般有兩種:
- ref:給元素或組件注冊引用信息;
- $parent / $children:訪問父 / 子實(shí)例。
這兩種都是直接得到組件實(shí)例,使用后可以直接調(diào)用組件的方法或訪問數(shù)據(jù),比如下面的示例中,用 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 類似,也是基于當(dāng)前上下文訪問父組件或全部子組件的。
這兩種方法的弊端是,無法在跨級或兄弟間通信,比如下面的結(jié)構(gòu):
// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>
我們想在 component-a 中,訪問到引用它的頁面中(這里就是 parent.vue)的兩個(gè) component-b 組件,那這種情況下,就得配置額外的插件或工具了,比如 Vuex 和 Bus 的解決方案,本小冊不再做它們的介紹,讀者可以自行閱讀相關(guān)內(nèi)容。不過,它們都是依賴第三方插件的存在,這在開發(fā)獨(dú)立組件時(shí)是不可取的,而在小冊的后續(xù)章節(jié),會(huì)陸續(xù)介紹一些黑科技,它們完全不依賴任何三方插件,就可以輕松得到任意的組件實(shí)例,或在任意組件間進(jìn)行通信,且適用于任意場景。
結(jié)語
本小節(jié)帶您復(fù)習(xí)了 Vue.js 組件的核心知識點(diǎn),雖然這并沒有完全覆蓋 Vue.js 的 API,但對于組件開發(fā)來說已經(jīng)足夠了,后續(xù)章節(jié)也會(huì)陸續(xù)擴(kuò)展更多的用法。
基于 Vue.js 開發(fā)獨(dú)立組件,并不是新奇的挑戰(zhàn),坦率地講,它本質(zhì)上還是 JavaScript。掌握了 Vue.js 組件的這三個(gè) API 后,剩下的便是程序的設(shè)計(jì)。在組件開發(fā)中,最難的環(huán)節(jié)應(yīng)當(dāng)是解耦組件的交互邏輯,盡量把復(fù)雜的邏輯分發(fā)到不同的子組件中,然后彼此建立聯(lián)系,在這其中,計(jì)算屬性(computed)和混合(mixins)是兩個(gè)重要的技術(shù)點(diǎn),合理利用,就能發(fā)揮出 Vue.js 語言的最大特點(diǎn):把狀態(tài)(數(shù)據(jù))的維護(hù)交給 Vue.js 處理,我們只專注在交互上。文章來源:http://www.zghlxwxcb.cn/news/detail-852821.html
當(dāng)您最終讀完本小冊時(shí),應(yīng)該會(huì)總結(jié)出和筆者一樣的感悟:Vue.js 組件開發(fā),玩到最后還是在拼 JavaScript 功底。對于每一位使用 Vue.js 的開發(fā)者來說,閱讀完本小冊都可以嘗試開發(fā)和維護(hù)一套屬于自己的組件庫,并樂在其中,而且你會(huì)越發(fā)覺得,一個(gè)組件或一套組件庫,就是融合了前端精髓的產(chǎn)出。文章來源地址http://www.zghlxwxcb.cn/news/detail-852821.html
擴(kuò)展閱讀
- Vue 組件通信之 Bus
- Vuex通俗版教程
到了這里,關(guān)于Vue.js組件精講 第2章 基礎(chǔ):Vue.js組件的三個(gè)API:prop、event、slot的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!