2019-07-02 自定義組件能夠幫我們更好的復(fù)用代碼和重構(gòu)簡(jiǎn)化代碼復(fù)雜度。一起來學(xué)習(xí)一下小程序自定義組件的內(nèi)容吧。 從小程序基礎(chǔ)庫(kù)版本 1.6.3 開始,小程序支持簡(jiǎn)潔的組件化編程。所有自定義組件相關(guān)特性都需要基礎(chǔ)庫(kù)版本 1.6.3 或更高。 開發(fā)者可以將頁(yè)面內(nèi)的功能模塊抽象成自定義組件,以便在不同的頁(yè)面中重復(fù)使用;也可以將復(fù)雜的頁(yè)面拆分成多個(gè)低耦合的模塊,有助于代碼維護(hù)。自定義組件在使用時(shí)與基礎(chǔ)組件非常相似 總覽 一、Component概念 Component像頁(yè)面一樣由wxml、wxss、js和json4個(gè)文件組成,且需要把這4個(gè)文件放在同一個(gè)目錄中。與頁(yè)面不一樣的是,Component中的構(gòu)造函數(shù)(也可以稱構(gòu)造器)是Component({}),而頁(yè)面中的構(gòu)造函數(shù)是Page({})。要編寫一個(gè)自定義組件,首先需要在 JSON 文件中進(jìn)行自定義組件聲明(將component字段設(shè)為true可這一組文件設(shè)為自定義組件): { "component": true } slot Component的slot(slot意思是插槽),主要是讓你在外部的wxml可以自由的在你的Component的wxml里插入模塊。默認(rèn)情況下,一個(gè)組件的wxml只可能有一個(gè)slot。需要使用多個(gè)時(shí),可以在組件js中聲明啟用。 Component({ options: { multipleSlots: true // 在組件定義時(shí)的選項(xiàng)中啟用多slot支持 }, properties: { /* ... */ }, methods: { /* ... */ } }) 此時(shí),可以在這個(gè)組件的wxml中使用多個(gè)slot,以不同的 name 來區(qū)分。 <!-- 組件模板 --> <view class="wrapper"> <slot name="before"></slot> <view>這里是組件的內(nèi)部細(xì)節(jié)</view> <slot name="after"></slot> </view> 使用時(shí),用 slot 屬性來將節(jié)點(diǎn)插入到不同的slot上。 <!-- 引用組件的頁(yè)面模板 --> <view> <component-tag-name> <!-- 這部分內(nèi)容將被放置在組件 <slot name="before"> 的位置上 --> <view slot="before">這里是插入到組件slot name="before"中的內(nèi)容</view> <!-- 這部分內(nèi)容將被放置在組件 <slot name="after"> 的位置上 --> <view slot="after">這里是插入到組件slot name="after"中的內(nèi)容</view> </component-tag-name> </view> 組件樣式編寫注意事項(xiàng) 組件和引用組件的頁(yè)面不能使用id選擇器(#a)、屬性選擇器([a])和標(biāo)簽名選擇器,請(qǐng)改用class選擇器。 組件和引用組件的頁(yè)面中使用后代選擇器(.a .b)在一些極端情況下會(huì)有非預(yù)期的表現(xiàn),如遇,請(qǐng)避免使用。 子元素選擇器(.a>.b)只能用于 view 組件與其子節(jié)點(diǎn)之間,用于其他組件可能導(dǎo)致非預(yù)期的情況。 繼承樣式,如 font 、 color ,會(huì)從組件外繼承到組件內(nèi)。 除繼承樣式外, app.wxss 中的樣式、組件所在頁(yè)面的的樣式對(duì)自定義組件無效。 #a { } /* 在組件中不能使用 */ [a] { } /* 在組件中不能使用 */ button { } /* 在組件中不能使用 */ .a > .b { } /* 除非 .a 是 view 組件節(jié)點(diǎn),否則不一定會(huì)生效 */ 外部樣式類 使用外部樣式類可以讓組件使用指定的組件外樣式類,如果希望組件外樣式類能夠完全影響組件內(nèi)部,可以將組件構(gòu)造器中的options.addGlobalClass字段置為true。 /* 組件 custom-component.js */ Component({ externalClasses: ['my-class'] }) <!-- 組件 custom-component.wxml --> <custom-component class="my-class">這段文本的顏色由組件外的 class 決定</custom-component> /* 組件外的樣式定義 */ .red-text { color: red; } 創(chuàng)建一個(gè)組件 一個(gè)組件需要包括json、wxml、wxss、js四個(gè)文件組成,下面我們先看看一個(gè)簡(jiǎn)單的入門: <!--components/component/component.wxml--> <view class="inner"> {{innerText}} </view> <slot></slot> 編寫JS文件,組件的屬性值和內(nèi)部數(shù)據(jù)將被用于組件 wxml 的渲染,其中,屬性值是可由組件外部傳入的 // components/component/component.js Component({ /** * 組件的屬性列表 */ properties: { innerText: { type: String, value: 'hello world' }, myProperties:String }, /** * 組件的初始數(shù)據(jù) */ data: { }, /** * 組件的方法列表 */ methods: { } }) 設(shè)置字體的顏色 /* components/component/component.wxss */ .inner{color: red;} 完成對(duì)組件的初始化,包括設(shè)置屬性列表,初始化數(shù)據(jù),以及設(shè)置相關(guān)的方法。 使用自定義組件 使用已注冊(cè)的自定義組件前,首先要在頁(yè)面的 json 文件中進(jìn)行引用聲明。此時(shí)需要提供每個(gè)自定義組件的標(biāo)簽名和對(duì)應(yīng)的自定義組件文件路徑: { "usingComponents": { "component": "/components/component/component" } } 在page頁(yè)面下添加聲明過的自定義組件: <component></component> <view> <component> <!-- 這部分內(nèi)容將被放置在組件 <slot> 的位置上 --> <view>這里是插入到組件slot中的內(nèi)容</view> </component> </view> 上方的是一個(gè)最簡(jiǎn)單的自定義組件。 注意事項(xiàng): 1.對(duì)于基礎(chǔ)庫(kù)的1.5.x版本, 1.5.7 也有部分自定義組件支持。 2.因?yàn)閃XML節(jié)點(diǎn)標(biāo)簽名只能是小寫字母、中劃線和下劃線的組合,所以自定義組件的標(biāo)簽名也只能包含這些字符。 3.自定義組件也是可以引用自定義組件的,引用方法類似于頁(yè)面引用自定義組件的方式(使用 usingComponents 字段)。 4.自定義組件和使用自定義組件的頁(yè)面所在項(xiàng)目根目錄名不能以“wx-”為前綴,否則會(huì)報(bào)錯(cuò)。 5.舊版本的基礎(chǔ)庫(kù)不支持自定義組件,此時(shí),引用自定義組件的節(jié)點(diǎn)會(huì)變?yōu)槟J(rèn)的空節(jié)點(diǎn)。 Component構(gòu)造器 使用component構(gòu)造器,進(jìn)行構(gòu)造。 該構(gòu)造函數(shù)用于定義組件。調(diào)用Component函數(shù)能指定組件的數(shù)據(jù),屬性和方法。來看看這個(gè)完整的列表代碼含義: Component({ behaviors: [], properties: { myProperty: { // 屬性名 type: String, // 類型(必填),目前接受的類型包括:String, Number, Boolean, Object, Array, null(表示任意類型) value: '', // 屬性初始值(可選),如果未指定則會(huì)根據(jù)類型選擇一個(gè) observer: function (newVal, oldVal) { this._propertyChange(newVal, oldVal); } // 屬性被改變時(shí)執(zhí)行的函數(shù)(可選),也可以寫成在methods段中定義的方法名字符串, 如:'_propertyChange' }, myProperty2: String // 簡(jiǎn)化的定義方式 }, data: { A: [{ B: 'init data.A[0].B' }] }, // 私有數(shù)據(jù),可用于模版渲染 lifetimes: { // 生命周期函數(shù),可以為函數(shù),或一個(gè)在methods段中定義的方法名 attached: function () { }, moved: function () { }, detached: function () { }, }, // 生命周期函數(shù),可以為函數(shù),或一個(gè)在methods段中定義的方法名 attached: function () { }, // 此處attached的聲明會(huì)被lifetimes字段中的聲明覆蓋 ready: function() { }, pageLifetimes: { // 組件所在頁(yè)面的生命周期函數(shù) show: function () { }, }, methods: { onMyButtonTap: function () { this.setData({ // 更新屬性和數(shù)據(jù)的方法與更新頁(yè)面數(shù)據(jù)的方法類似 myProperty: 'Test' }) }, _myPrivateMethod: function () { // 內(nèi)部方法建議以下劃線開頭 this.replaceDataOnPath(['A', 0, 'B'], 'myPrivateData') // 這里將 data.A[0].B 設(shè)為 'myPrivateData' this.applyDataUpdates() }, _propertyChange: function (newVal, oldVal) { console.log(newVal); console.log(oldVal); } } }) 組件與數(shù)據(jù)通信 組件間的通信方法有以下幾種: WXML 數(shù)據(jù)綁定:用于父組件向子組件的指定屬性設(shè)置數(shù)據(jù),僅能設(shè)置 JSON 兼容數(shù)據(jù)(自基礎(chǔ)庫(kù)版本 2.0.9 開始,還可以在數(shù)據(jù)中包含函數(shù))。具體在 組件模板和樣式 章節(jié)中介紹。 事件:用于子組件向父組件傳遞數(shù)據(jù),可以傳遞任意數(shù)據(jù)。 如果以上兩種方式不足以滿足需要,父組件還可以通過 this.selectComponent 方法獲取子組件實(shí)例對(duì)象,這樣就可以直接訪問組件的任意數(shù)據(jù)和方法。 通過在頁(yè)面中給組件加了一個(gè)id值,這樣子我們就能查到組件的方法了。 <compontent id="modal"></compontent> /*js*/ var modal = this.setlectComponet('#modal'); 這樣子就能在外面調(diào)用組件里面的任意數(shù)據(jù)和方法了。 properties:主頁(yè)面?zhèn)魅霐?shù)據(jù)到組件,相當(dāng)于Vue的props,是傳入外部數(shù)據(jù)的入口。 data:則用于組件的內(nèi)部數(shù)據(jù)變化,外部數(shù)據(jù)沒法初始化 在 properties 定義段中,屬性名采用駝峰寫法(propertyName);在 wxml 中,指定屬性值時(shí)則對(duì)應(yīng)使用連字符寫法(component-tag-name property-name="attr value"),應(yīng)用于數(shù)據(jù)綁定時(shí)采用駝峰寫法(attr="{{propertyName}}") 傳入的數(shù)據(jù),不管是簡(jiǎn)單數(shù)據(jù)類型,還是引用類型,都如同值復(fù)制一樣 方法函數(shù)調(diào)用 methods:需要在組件中調(diào)用的方法,都寫在這個(gè)對(duì)象里面。跟Page中的對(duì)象里面的方法同級(jí)。 生命周期:可單獨(dú)某個(gè)生命周期放在Components下(舊式的定義方式,可以保持對(duì) <2.2.3 版本基礎(chǔ)庫(kù)的兼容),也可以放在lifetimes,如果兩個(gè)地方有同名生命周期,則lifetimes里面的方法會(huì)覆蓋前者。 Component({ lifetimes: { attached: function() { // 在組件實(shí)例進(jìn)入頁(yè)面節(jié)點(diǎn)樹時(shí)執(zhí)行 }, detached: function() { // 在組件實(shí)例被從頁(yè)面節(jié)點(diǎn)樹移除時(shí)執(zhí)行 }, }, // 以下是舊式的定義方式,可以保持對(duì) <2.2.3 版本基礎(chǔ)庫(kù)的兼容 attached: function() { // 在組件實(shí)例進(jìn)入頁(yè)面節(jié)點(diǎn)樹時(shí)執(zhí)行 }, detached: function() { // 在組件實(shí)例被從頁(yè)面節(jié)點(diǎn)樹移除時(shí)執(zhí)行 }, // ... }) 組件所在的生命周期 還有一些特殊的生命周期,它們并非與組件有很強(qiáng)的關(guān)聯(lián),但有時(shí)組件需要獲知,以便組件內(nèi)部處理。這樣的生命周期稱為“組件所在頁(yè)面的生命周期”,在 pageLifetimes 定義段中定義。其中可用的生命周期包括: 生成的組件實(shí)例可以在組件的方法、生命周期函數(shù)和屬性 observer 中通過 this 訪問。組件包含一些通用屬性和方法 image.png 組件傳出數(shù)據(jù)到主頁(yè)面 組件間交互的主要形式是自定義事件。 組件通過 this.triggerEvent() 觸發(fā)自定義事件,主頁(yè)面在組件上 bind:myevent="onMyEvent" 來接收自定義事件。 其中,this.triggerEvent() 方法接收自定義事件名稱外,還接收兩個(gè)對(duì)象,eventDetail 和 eventOptions。 <!-- 在自定義組件中 --> <button bindtap="onTap">點(diǎn)擊這個(gè)按鈕將觸發(fā)“myevent”事件</button> Component({ properties: {} methods: { // 子組件觸發(fā)自定義事件 ontap () { // 所有要帶到主頁(yè)面的數(shù)據(jù),都裝在eventDetail里面 var eventDetail = { name:'sssssssss', test:[1,2,3] } // 觸發(fā)事件的選項(xiàng) bubbles是否冒泡,composed是否可穿越組件邊界,capturePhase 是否有捕獲階段 var eventOption = { composed: true } this.triggerEvent('myevent', eventDetail, eventOption) } } }) 觸發(fā)的事件包括: 監(jiān)聽事件 自定義組件可以觸發(fā)任意的事件,引用組件的頁(yè)面可以監(jiān)聽這些事件。監(jiān)聽自定義組件事件的方法與監(jiān)聽基礎(chǔ)組件事件的方法完全一致: 在Page事件中監(jiān)聽組件中傳遞過來的值。 Page({ onMyEvent: function(e){ e.detail // 自定義組件觸發(fā)事件時(shí)提供的detail對(duì)象 } }) behaviors behaviors 是用于組件間代碼共享的特性,類似于一些編程語(yǔ)言中的“mixins”或“traits”。 每個(gè) behavior 可以包含一組屬性、數(shù)據(jù)、生命周期函數(shù)和方法,組件引用它時(shí),它的屬性、數(shù)據(jù)和方法會(huì)被合并到組件中,生命周期函數(shù)也會(huì)在對(duì)應(yīng)時(shí)機(jī)被調(diào)用。每個(gè)組件可以引用多個(gè) behavior 。 behavior 也可以引用其他 behavior 。 // my-behavior.js module.exports = Behavior({ behaviors: [], properties: { myBehaviorProperty: { type: String } }, data: { myBehaviorData: {} }, attached: function(){}, methods: { myBehaviorMethod: function(){} } }) 組件引用時(shí),在 behaviors 定義段中將它們逐個(gè)列出即可。 // my-component.js var myBehavior = require('my-behavior') Component({ behaviors: [myBehavior], properties: { myProperty: { type: String } }, data: { myData: {} }, attached: function(){}, methods: { myMethod: function(){} } }) 字段的覆蓋和組合規(guī)則 組件和它引用的 behavior 中可以包含同名的字段,對(duì)這些字段的處理方法如下: 如果有同名的屬性或方法,組件本身的屬性或方法會(huì)覆蓋 behavior 中的屬性或方法,如果引用了多個(gè) behavior ,在定義段中靠后 behavior 中的屬性或方法會(huì)覆蓋靠前的屬性或方法; 如果有同名的數(shù)據(jù)字段,如果數(shù)據(jù)是對(duì)象類型,會(huì)進(jìn)行對(duì)象合并,如果是非對(duì)象類型則會(huì)進(jìn)行相互覆蓋; 生命周期函數(shù)不會(huì)相互覆蓋,而是在對(duì)應(yīng)觸發(fā)時(shí)機(jī)被逐個(gè)調(diào)用。如果同一個(gè) behavior 被一個(gè)組件多次引用,它定義的生命周期函數(shù)只會(huì)被執(zhí)行一次。 內(nèi)置behavior 組件間關(guān)系 <custom-ul> <custom-li> item 1 </custom-li> <custom-li> item 2 </custom-li> </custom-ul> 這個(gè)例子中, custom-ul 和 custom-li 都是自定義組件,它們有相互間的關(guān)系,相互間的通信往往比較復(fù)雜。此時(shí)在組件定義時(shí)加入 relations 定義段,可以解決這樣的問題。示例: // path/to/custom-ul.js Component({ relations: { './custom-li': { type: 'child', // 關(guān)聯(lián)的目標(biāo)節(jié)點(diǎn)應(yīng)為子節(jié)點(diǎn) linked: function(target) { // 每次有custom-li被插入時(shí)執(zhí)行,target是該節(jié)點(diǎn)實(shí)例對(duì)象,觸發(fā)在該節(jié)點(diǎn)attached生命周期之后 }, linkChanged: function(target) { // 每次有custom-li被移動(dòng)后執(zhí)行,target是該節(jié)點(diǎn)實(shí)例對(duì)象,觸發(fā)在該節(jié)點(diǎn)moved生命周期之后 }, unlinked: function(target) { // 每次有custom-li被移除時(shí)執(zhí)行,target是該節(jié)點(diǎn)實(shí)例對(duì)象,觸發(fā)在該節(jié)點(diǎn)detached生命周期之后 } } }, methods: { _getAllLi: function(){ // 使用getRelationNodes可以獲得nodes數(shù)組,包含所有已關(guān)聯(lián)的custom-li,且是有序的 var nodes = this.getRelationNodes('path/to/custom-li') } }, ready: function(){ this._getAllLi() } }) // path/to/custom-li.js Component({ relations: { './custom-ul': { type: 'parent', // 關(guān)聯(lián)的目標(biāo)節(jié)點(diǎn)應(yīng)為父節(jié)點(diǎn) linked: function(target) { // 每次被插入到custom-ul時(shí)執(zhí)行,target是custom-ul節(jié)點(diǎn)實(shí)例對(duì)象,觸發(fā)在attached生命周期之后 }, linkChanged: function(target) { // 每次被移動(dòng)后執(zhí)行,target是custom-ul節(jié)點(diǎn)實(shí)例對(duì)象,觸發(fā)在moved生命周期之后 }, unlinked: function(target) { // 每次被移除時(shí)執(zhí)行,target是custom-ul節(jié)點(diǎn)實(shí)例對(duì)象,觸發(fā)在detached生命周期之后 } } } }) 更多: 關(guān)聯(lián)一類組件 抽象節(jié)點(diǎn) 自定義組件擴(kuò)展 開發(fā)第三方自定義組件 制作一個(gè)彈窗組件 |
|