微信小程序不支持自定義組件笼痹,只提供了一個(gè)非常受限制的模板功能,尤其缺乏了開發(fā)產(chǎn)品中最重要的幾個(gè)功能:
- 模板內(nèi)的數(shù)據(jù)只能由當(dāng)前頁面?zhèn)鬟f识补,無法預(yù)先設(shè)置一些初始化數(shù)據(jù)以達(dá)到復(fù)用的目的冈钦;
- 模板內(nèi)的數(shù)據(jù)變化無法通知到當(dāng)前頁面,也就是說模板不知道誰在使用它李请,只能在當(dāng)前頁面定義方法綁定在模板內(nèi)部的組件事件上來處理交互邏輯
- 模板內(nèi)部無法預(yù)先設(shè)置一些內(nèi)部邏輯代碼。
這些局限性分分鐘叫人抓狂厉熟,這個(gè)模板功能只是實(shí)現(xiàn)了UI層復(fù)用的功能导盅,所有的邏輯代碼都需要在頁面上重新寫一遍。開發(fā)和維護(hù)的效率一下子回到了解放前揍瑟,再也沒法愉快地思考業(yè)務(wù)需求了白翻,先把這個(gè)坑補(bǔ)補(bǔ)。 大路貌似走不通,只能另辟彎道滤馍,曲線救國了岛琼。 解決思路:所有的頁面都通過全局Page函數(shù)來定義,它提供了一個(gè)onLaunch的觸發(fā)事件巢株,看來能在這里想想辦法槐瑞。我們大致需要這樣的自定義組件或者說是模板:
/*這是一個(gè)組件基類*/
module.exports = class BaseComp {
constructor({ key, page, data = {} }) {
this.key = key
this.page = page
this.data = data ... }
/*當(dāng)組件內(nèi)部數(shù)據(jù)變化后,能在這里更新頁面*/
update(){}
/*當(dāng)頁面觸發(fā)事件時(shí)阁苞,組件也應(yīng)該能捕捉到*/
onLoad() {} onReady() {} onShow() {} onHide() {} onUnload() {}}
/*這是一個(gè)文本框組件*/
module.exports = class FormInput extends BaseComp {
constructor( { key, page, data }) {
/*這里可以定義一些默認(rèn)數(shù)據(jù)*/
data = Object.assign({
type: 'text',
label: '',
placeholder: '',
value:'',
...
focus: false
}, data)
super( { key, page, data })
}
onfocus(e){...}
onblur(e){...}
onchange(e){...}
}
小程序的視圖(wxml)和js無法像webpack那樣打包在一起困檩,所以只能利用現(xiàn)有的模板功能,把小程序原生組件組合成一個(gè)自定義組件那槽。
<template name="formInput">
<view>
<label>{{label}}</label>
<input type="{{type}}" class="{{focus?' active':''}}"
placeholder-class="{{placeholderClass}}"
placeholder="{{placeholder}}"
value="{{value}}"
data-key="{{key}}"
data-index=""
bindfocus="_EventProxy"
bindblur="_EventProxy" />
<text>{{message}}</text>
</view>
</template>
1. 寫個(gè)Page函數(shù)悼沿,在App初始化之前中引入,把小程序的全局Page替換掉骚灸。在這個(gè)Page函數(shù)內(nèi)根據(jù)配置參數(shù)實(shí)例化自定義組件糟趾,把當(dāng)前頁面賦給組件的page屬性,并向組件傳入當(dāng)前頁面的'onLoad','onReady'等事件甚牲;
const Page = this.Page
this.Page = function( config ) {
if( config.components ) {
let onLoad = config.onLoad
config.onLoad = function() {
for( let key in config.components ) {
let opts = config.components[ key ]
opts.key = key
opts.page = this
config.components[ key ] = new comp[ opts.is ]( opts )
config.components[ key ].onLoad( arguments )
}
if( onLoad ) {
onLoad.call( this, arguments )
}
}
......
2. Page函數(shù)在當(dāng)前頁面上添加一個(gè)統(tǒng)一的事件代理(Proxy)义郑,自定義組件內(nèi)部的小程序原生組件上的事件全部指向這個(gè)代理方法,由這個(gè)事件代理根據(jù)事件對象判斷是由哪個(gè)自定義組件鳖藕、自定義組件內(nèi)的哪個(gè)原生組件觸發(fā)的魔慷,觸發(fā)的是哪個(gè)事件,然后再調(diào)用自定義組件的處理方法去執(zhí)行內(nèi)部邏輯著恩。
config._EventProxy = function( e ) {
let dset = e.currentTarget.dataset
let comp = config.components[ dset.key ]
/* 組件上定義的事件名必須為 on + event.type
data-index用來區(qū)分多個(gè)同名事件 */
let event = 'on' + e.type + dset.index
if( comp && comp[ event ] ) {
comp[ event ].call( comp, e )
}
}
最后再執(zhí)行小程序的Page函數(shù)
Page( config )
3. 自定義組件基類中實(shí)現(xiàn)了更新頁面數(shù)據(jù)的公共方法
update() {
let origin = this.page.data[this.key] || {}
let data = Object.assign({}, origin, this.data)
this.page.setData({
[this.key]: data
})
}
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者