寫一個微信小程式自定義公共元件
微信小程式已經推出一段時間了,官方也提供很多元件,但是有些業務場景,官方元件難免有些捉襟見肘,這時候,就需要自己開發一個自定義元件了。但是微信小程式其實是做了很多限制的,本文記錄了開發(踩坑)一個公共登入彈出框元件的過程,僅供參考。
1. 一個元件的組成部分
一個微信小程式有四中格式的檔案*.wxml
、*.wxss
、*.js
和*.json
,*.json
其實是小程式和頁面的配置檔案,開發元件的時候,就不需要這個了。我這裡開發一個登陸彈出框元件,我把元件放在component
資料夾下:
123456789101112131415 |
Wechat-APP/├─app.js├─app.json├─app.wxss |
可見,元件也是由三個部分組成。
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 |
官方提供了模版的概念,同時又給出了兩種引用方式: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); |
到此,自定義的元件就寫好啦:
PS:由於用到了ES6特性,需要在小程式除錯工具中勾選“開啟ES6轉ES5”。
3. 寫在最後
小程式官方還是限制太多,我並不看好。但是對於初創公司還是有點意義的,這時候遇到自定義元件的需求,只好帶著鐐銬跳舞。本文只是給出一個寫一個微信小程式自定義元件的思路,所謂條條大路通羅馬,肯定還有更合適的方案,能力有限,僅供參考。
原文:https://segmentfault.com/p/1210000007842233