1. 程式人生 > >寫一個微信小程式自定義公共元件

寫一個微信小程式自定義公共元件

微信小程式已經推出一段時間了,官方也提供很多元件,但是有些業務場景,官方元件難免有些捉襟見肘,這時候,就需要自己開發一個自定義元件了。但是微信小程式其實是做了很多限制的,本文記錄了開發(踩坑)一個公共登入彈出框元件的過程,僅供參考。

1. 一個元件的組成部分

一個微信小程式有四中格式的檔案*.wxml*.wxss*.js*.json*.json其實是小程式和頁面的配置檔案,開發元件的時候,就不需要這個了。我這裡開發一個登陸彈出框元件,我把元件放在component資料夾下:

123456789101112131415 Wechat-APP/├─app.js├─app.json├─app.wxss
├─component/│ └─login-pannel/│ ├─login-pannel.js│ ├─login-pannel.wxml│ └─login-pannel.wxss├─image/├─pages/│ ├─index/│ ├─merchant-detail/│ └─merchant-list/└─utils/

可見,元件也是由三個部分組成。

1.1. wxml

首先新建元件的模版,/component/login-pannel/login-pannel.wxml,同時,給定義的模版一個唯一標識<template name="loginPannel"></template>

,程式碼如下:

123456789101112131415 <templatename="loginPannel"> <viewclass="login-pannel"hidden="{{ isHide }}" > <viewcatchtap="__lgpanel_close"class="close-btn"></view> <viewclass="form-area"> <viewclass="login-inline phone {{ phoneClass }}"> <input
placeholder="請填寫手機號碼"maxlength="11"type="number"bindinput="__lgpanel_phoneInput"bindfocus="__lgpanel_phoneFocus"bindblur="__lgpanel_phoneBlur" />
<textcatchtap="__lgpanel_sendCode"class="verify-btn">{{ verifyText }}</text> </view> <viewclass="login-inline vcode {{ vcodeClass }}"> <inputplaceholder="請填寫驗證碼"maxlength="4"type="number"bindinput="__lgpanel_vcodeInput"bindfocus="__lgpanel_vcodeFocus"bindblur="__lgpanel_vcodeBlur" /> </view> </view> <textcatchtap="__lgpanel_login"class="login-btn">註冊/登入</text> </view></template>

官方提供了模版的概念,同時又給出了兩種引用方式:import 和 include。include 方式是將你的模版原封不動的“貼上”到引用的地方,而我們這裡開發的是一個互動元件,勢必會碰到資料繫結和事件繫結,所以我們最終引用的時候,使用 import 的形式來使用元件。

1.2. wxss

定義元件的樣式,/component/login-pannel/login-pannel.wxss。樣式沒什麼好說的,這裡略過。

1.3. js

首先定義元件的建構函式,建構函式主要做兩件事:

  • 把元件的資料“注入”到頁面的data物件中;
  • 把元件的事件“合併”到頁面物件上。

小程式文件中,關於資料和事件,是這麼描述的:

這樣,想實現模版的資料繫結,只能想辦法把元件上的資料“追加”到頁面的 data 物件上去;同理,想實現元件的事件繫結,則需要把元件的事件也“傳給”頁面函式Page。同時,為了防止影響到其它元件,還要給資料和事件都限定了“名稱空間”:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 let _compData = { '__lgpanel__.isHide': true, '__lgpanel__.phoneNum': '', '__lgpanel__.verifyCode': '', '__lgpanel__.verifyText': '獲取驗證碼', '__lgpanel__.phoneClass': '', '__lgpanel__.vcodeClass': ''}let _compEvent = { __lgpanel_timer: null, __lgpanel_countDown: 60, __lgpanel_close: function () { clearInterval(this.__lgpanel_timer) this.__lgpanel_countDown = 60 this.setData(_compData) }, __lgpanel_sendCode: function () { if (this.__lgpanel_countDown < 60) { return } this.setData({ '__lgpanel__.verifyText': this.__lgpanel_countDown + 's' }) this.__lgpanel_timer = setInterval(() => { this.__lgpanel_countDown-- if (this.__lgpanel_countDown <= 0) { clearInterval(this.__lgpanel_timer) this.__lgpanel_countDown = 60 this.setData({ '__lgpanel__.verifyText': '重新獲取' }) return } this.setData({ '__lgpanel__.verifyText': this.__lgpanel_countDown + 's' }) }, 1000) typeof this.loginPannel._configs.sendCode == "function" && this.loginPannel._configs.sendCode() }}// 小程式最新版把原型鏈幹掉了。。。換種寫法let loginPannel = { show: function(data) { this.__page.setData({'__lgpanel__.isHide': false}) if (data) { Object.assign(this._configs, data) } }}function LoginPannel () { // 定義元件的一些回撥 this._configs = { sendCode: null, closeCode: null, login: null } // 拿到當前頁面物件 let pages = getCurrentPages() let curPage = pages[pages.length - 1] // 把元件的事件“合併到”頁面物件上 Object.assign(curPage, _compEvent) this.__page = curPage Object.assign(curPage, loginPannel) // 小程式最新版把原型鏈幹掉了。。。換種寫法 // 附加到page上,方便訪問 curPage.loginPannel = this // 把元件的資料“注入”到頁面的data物件中 curPage.setData(_compData) return this}

同時,在元件的建構函式的原型鏈上追加“啟動”函式,(微信小程式最新版把原型鏈幹掉了。。。)在建構函式中,把啟動方法合併到LoginPannel物件上去,同時把LoginPannel暴露出去:

1234567891011 /* LoginPannel.prototype.show = function(data) { this.__page.setData({'__lgpanel__.isHide': false}) if (data) { Object.assign(this._configs, data) }} */module.exports = { LoginPannel}

到這裡,一個元件就寫好了。

2. 使用自定義元件

由於微信小程式限制太多,使用元件的時候,需要引用三個檔案

2.1. 引用 wxml

在要使用元件的頁面的wxml檔案中引用元件,同時使用模版,傳入擔任“名稱空間”的物件:

12 <import src="../../component/login-pannel/login-pannel.wxml"/><template is="loginPannel" data="{{ ...__lgpanel__ }}"/>

2.2. 引用 wxss

如果你的元件需要在多個頁面中使用,那麼在app.wxss中引入一次元件樣式即可;如果只需要在摸個特定的頁面使用,只需在該頁面的樣式檔案中引入元件樣式。

1 @import "./component/login-pannel/login-pannel.wxss";

2.3. 引用 js

如果你的元件需要在多個頁面中使用,你需要在app.js中引用,同時傳給App()

1234567891011121314151617181920212223242526 import { LoginPannel } from './component/login-pannel/login-pannel'App({ LoginPannel, _getUserInfo: function (cb) { var that = this if (this.globalData.userInfo) { typeof cb == "function" && cb(this.globalData.userInfo) } else { //呼叫登入介面 wx.login({success: function () { wx.getUserInfo({success: function (res) { that.globalData.userInfo = res.userInfotypeof cb == "function" && cb(that.globalData.userInfo) } }) } }) } }})

然後,在使用的頁面的生命週期函式onLoad中“註冊”該元件:

1234567891011121314 onLoad: function () { // 呼叫應用例項的方法獲取全域性資料 let app = getApp() // “註冊”元件 new app.LoginPannel() app._getUserInfo((userInfo) => { //更新資料 this.setData({ userInfo:userInfo }) })}

然後,在這個頁面中就可以愉快的使用元件啦:

12345678 openLoginPannel: function() { this.loginPannel.show({ sendCode: function () { console.log('index - ', 'sendCode') }, login: this.onLogin })}

如果你只是要在特定的頁面使用元件,只要在當前頁面引用元件即可:

1234567891011121314151617181920212223 import { LoginPannel } from '../../component/login-pannel/login-pannel'let pageData = { LoginPannel, data: { motto: '開啟登入框', userInfo: {} }, openLoginPannel: function() { this.loginPannel.show({ sendCode: function () { console.log('index - ', 'sendCode') } }) }, onLoad: function () { new this.LoginPannel() }}Page(pageData);

到此,自定義的元件就寫好啦:

image

PS:由於用到了ES6特性,需要在小程式除錯工具中勾選“開啟ES6轉ES5”。

3. 寫在最後

小程式官方還是限制太多,我並不看好。但是對於初創公司還是有點意義的,這時候遇到自定義元件的需求,只好帶著鐐銬跳舞。本文只是給出一個寫一個微信小程式自定義元件的思路,所謂條條大路通羅馬,肯定還有更合適的方案,能力有限,僅供參考。

原文:https://segmentfault.com/p/1210000007842233