為您解碼網(wǎng)站建設(shè)的點點滴滴
發(fā)表日期:2019-11 文章編輯:小燈 瀏覽次數(shù):4866
戰(zhàn)爭,信念,意志和情感,這些散發(fā)著光芒和硝煙的詞匯,象一枚枚炮彈轟入我們現(xiàn)在的生活。歷史的記憶不會被抹滅。
當(dāng)我們在各自項目里幸福的拷貝著官方代碼 demo,在 componnets
文件夾里使用 Component
方法書寫一個個組件時,不要忘記,在 2018 年上半年以前,小程序是沒有提供組件化方案的。
當(dāng)時,主要有兩種解決方法,一種是 WePY 拷貝法,另一種則是摩拜 template 法。
比如有個最簡單的按鈕組件:
<!-- components/button.wpy -->
<template>
<view class="button">
<button @tap="onTap">點這里</button>
</view>
</template>
<!-- pages/index.wpy -->
<template>
<view class="container">
<wpy-button /> // button 組件1
<wpy-button2 /> // button 組件2
</view>
</template>
經(jīng)過編譯后結(jié)果如下:
<view class="container">
<view class="button">
<button bindtap="$wpyButton$onTap">點這里</button>
</view>
<view class="button">
<button bindtap="$wpyButton2$onTap">點這里</button>
</view>
</view>
為了方便變量隔離,所以引入到頁面中的組件得單獨命名:
import wepy from 'wepy'
import Button from '@/components/button'
export default class Index extends wepy.page {
components = {
'wpy-button': Button,
'wpy-button2': Button
}
...
}
有一些不便的地方,但也很好的解決了組件化缺失的問題。
有心的同學(xué)可能記得當(dāng)初我們發(fā)了這篇文章:微信小程序組件化解決方案wx-component,當(dāng)時主要講了如何使用,這次講講技術(shù)的細節(jié)。
主要利用小程序當(dāng)時提供的 template
模板方法,使用方式如下:
<!-- pages/template/login.wxml -->
<template name="login">
<view class="login">這是登錄組件</view>
</template>
<!-- pages/login/index.wxml -->
<import src='../../components/login/index.wxml'/>
<view class="login-box">
<template is="login" data="{{...}}"></template>
</view>
由于知道這只是臨時的解決方法,最終還會遷移到微信官方組件化方案。了解到微信團隊正在開發(fā),就死皮賴臉找了微信研發(fā)同學(xué)要下技術(shù)方案,以便后期遷移成本做到最低。最后微信同學(xué)不耐煩的扔給我們?nèi)缦麓a,并特別囑咐不要泄露出去:
Component({
// 組件名
name: '',
// 為其他組件指定別名
using: {},
// 類似mixins,組件間代碼復(fù)用
behaviors: [],
// 組件私有數(shù)據(jù)
data: {
},
// 外部傳入的組件屬性
propties: {
},
// 當(dāng)組件被加載
attached () {
},
// 當(dāng)組件被卸載
detached () {
},
// 組件私有方法
methods: {
}
})
一目了然,依照此文檔實現(xiàn)一個簡單的組件化方案也有了思路。
由于沒有辦法在小程序全局注入 Component
方法,可以將組件代碼以模塊方式導(dǎo)出,在頁面的 Page
方法里引入:
// components/login/index.wxml
<template name="login">
<form bindsubmit="onLoginSubmit">
...
<button type="primary" formType="submit">{{btnText}}</button>
</form>
</template>
// components/login/index.js
module.exports = {
name: 'login',
data: {
btnText: ''
}
....
}
// pages/index/index.js
Page({
data: {
...
},
components: {
login: {
btnText: '開始',
onLoginCallback() { ... }
}
}
})
<!-- pages/index/index.wxml -->
<import src='../../components/login/index.wxml'/>
<view class="login-box">
<template is="login" data="{{...login}}"></template>
</view>
在 Page
的傳參里多了 components
屬性,傳入了組件名login
,以及組件對應(yīng)的屬性值和方法。為了使這些新增傳參生效,那勢必需要對 Page
進行改造。
如何用一行代碼毀掉你的小程序,在小程序根目錄的 app.js
里加入這段代碼即可:
Page = funtion() {}
這樣核心的 Page
的方法就被覆蓋掉了,所以利用這個“特性”,可以改造 Page
方法:
// utils/wx.js
var page = function() {
// 改造代碼
...
}
module.exports = {
page
}
// app.js
Page = require('./utils/wx').page
這就完成了獨一無二的自定義的小程序 Page
的方法。
精簡了核心的代碼如下:
function noop() {}
class Component {
constructor (config) {
// 兼容 onLoad onUnload 的寫法
config.onLoad = config.onLoad || config.attached || noop
config.onUnload = config.onUnload || config.detached || noop
this.data = config.data || {}
this.config = config
this.methods = config.methods || {}
for (let name in this.methods) {
// 為了使組件事件綁定生效,直接掛在到 this 下
this[name] = methods[name]
}
}
setData (data, deepExtend) {
let name = this.name
let parent = this.parent
let mergeData = extend(deepExtend !== false, parent.data[name], data)
let newData = {}
newData[name] = mergeData
this.data = mergeData
// 更新頁面的 data
parent.setData(newData)
}
setName (name) {
this.name = name
}
setParent (parent) {
this.parent = parent
}
}
主要完成了三件事:
attached
和 detached
template
的 bindtap
等代碼生效setData
功能有個細節(jié),為了讓大家容易理解(不泄露微信的方法名),分享到外部的文章用 onLoad
、onUnload
代替了 attached
、detached
,但內(nèi)部早就開始用微信命名的這兩個屬性名,才有了代碼中的兼容寫法。
整理了大致的核心代碼如下:
// 緩存下微信的 Page
const originalPage = Page
// 組件生命周期
const LIFETIME_EVENT = [
'onLoad',
'onUnload'
]
class MyPage {
constructor (origin) {
this.origin = origin
this.config = {}
this.children = {}
this.childrenEvents = {}
// 是否需要`components`
let components = this.components = origin.components
if (components) {
this.config.data = {}
for (let item in components) {
let props = components[item] || {}
let component = new Component(require(`../components/${item}/index`))
this.children[name] = component
// 合并組件的 data
extend(component.data, component.props)
// ...
// 合并組件的 method
for (let fnName in component.methods) {
this.config[fnName] = component.methods[fnName].bind(component)
}
// ...
let childrenEvents = this.childrenEvents[item] = {}
LIFETIME_EVENT.forEach((prop) => {
childrenEvents[item][prop] = component.config[prop]
})
}
// 合并所有依賴組件的生命周期函數(shù)
LIFETIME_EVENT.forEach((prop) => {
this.config[prop] = () => {
for (let item in this.components) {
this.childrenEvents[item][prop].apply(this.component, arguments)
}
this.origin[prop] && this.origin[prop].apply(this, arguments)
}
})
// 把新生成的 config 傳給原始的微信的 Page 方法
originalPage(this.config)
} else {
// 沒有依賴組件,直接透傳給微信的 Page 方法
originalPage(origin)
}
}
}
可能有點亂,其實就是不斷 merge data
和 method
的過程。最終所有組件自定的數(shù)據(jù)和方法都被掛在到了 Page
的傳參里。
最后,導(dǎo)出自定義的 page
:
// utils/wx.js
const page = function (config) {
return new MyPage(config)
}
module.exports = {
page
}
在 app.js
中覆蓋掉原有的 Page
方法:
// app.js
Page = require('./utils/wx').page
雖然滿足業(yè)務(wù)了,但也是有些問題的,例如上面 MyPage
方法里的這段:
for (let fnName in component.methods) {
this.config[fnName] = component.methods[fnName].bind(component)
}
可以看出,直接把組件內(nèi)部定義的方法,掛在到 config
中去了,這就要求頁面的方法和組件的方法不能重名,這是為了方便 template
可以直接綁定組件定義的事件,只能通過把組件事件轉(zhuǎn)移到頁面的事件方法里。
也有很多不完善的地方,但通過內(nèi)部約束代碼規(guī)范也基本可以解決。
這種近乎 Hack 的方式支撐了摩拜單車小程序業(yè)務(wù)大半年的時間,期間產(chǎn)出了大大小小十多個組件。而由于組件內(nèi)部基本是按照微信官方組件化 api 書寫,等待官方推出組件化方案后,全部遷移過去的成本也大大減小。
日期:2019-11 瀏覽次數(shù):5524
日期:2019-11 瀏覽次數(shù):11982
日期:2019-11 瀏覽次數(shù):4351
日期:2019-11 瀏覽次數(shù):5385
日期:2019-11 瀏覽次數(shù):5260
日期:2019-11 瀏覽次數(shù):7180
日期:2019-11 瀏覽次數(shù):5165
日期:2019-11 瀏覽次數(shù):15768
日期:2019-11 瀏覽次數(shù):4718
日期:2019-11 瀏覽次數(shù):6517
日期:2019-11 瀏覽次數(shù):5371
日期:2019-11 瀏覽次數(shù):4564
日期:2019-11 瀏覽次數(shù):10763
日期:2019-11 瀏覽次數(shù):8320
日期:2019-11 瀏覽次數(shù):5079
日期:2019-11 瀏覽次數(shù):4311
日期:2019-11 瀏覽次數(shù):8952
日期:2019-11 瀏覽次數(shù):4648
日期:2019-11 瀏覽次數(shù):4845
日期:2019-11 瀏覽次數(shù):4865
日期:2019-11 瀏覽次數(shù):4477
日期:2019-11 瀏覽次數(shù):5026
日期:2019-11 瀏覽次數(shù):10282
日期:2019-11 瀏覽次數(shù):5459
日期:2019-11 瀏覽次數(shù):5437
日期:2019-11 瀏覽次數(shù):4884
日期:2019-11 瀏覽次數(shù):12330
日期:2019-11 瀏覽次數(shù):7354
日期:2019-11 瀏覽次數(shù):7904
日期:2019-11 瀏覽次數(shù):4857
Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.