用 RxJS、RxWX 編寫更優秀的(de)微信小程序代碼
發表時(shí)間:2021-4-12
發布人(rén):融晨科技
浏覽次數:55
好的(de)代碼通常符合一個(gè)特點:高内聚,低耦合。
通俗地(dì / de)說(shuō)就(jiù)是(shì)用更少地(dì / de)代碼完成更多地(dì / de)功能,比如web前端地(dì / de)MVVM框架就(jiù)是(shì)把對DOM的(de)操作和(hé / huò)事件監聽抽象出(chū)來(lái),通過數據綁定來(lái)更新數據和(hé / huò)視圖。
“組件化”的(de)思想也(yě)是(shì)如此,組件的(de)目的(de)不(bù)是(shì)簡單的(de)代碼分割,更重要(yào / yāo)的(de)是(shì)方便代碼的(de)複用。
這(zhè)些都是(shì)針對視圖層面的(de)優化措施,針對數據層,其實也(yě)有方法寫出(chū)更優秀的(de)代碼。
其中一個(gè)簡單有效的(de)方法就(jiù)是(shì) 盡量編寫純函數。
純函數
什麽是(shì)純函數?可以(yǐ)用一個(gè)表達式來(lái)描述
輸入參數x => 執行代碼 => 輸出(chū)結果y
這(zhè)看起來(lái)好像和(hé / huò)普通函數也(yě)沒啥區别,那麽它和(hé / huò)普通的(de)函數相比,“純”在(zài)哪裏?
從輸入來(lái)看,參數是(shì)必傳的(de),而(ér)且不(bù)能被修改。比如
Math.random
這(zhè)類沒有參數的(de)函數就(jiù)不(bù)是(shì)純函數。從執行來(lái)看,不(bù)能引用外部變量或函數。
從結果來(lái)看,執行必有結果,而(ér)且輸入相同的(de)參數,執行的(de)結果必須相同。
純函數是(shì)将邏輯分離到(dào)極緻:一段功能單一,邏輯封閉的(de)代碼。
思考:function(x) { return 1 } 是(shì)不(bù)是(shì)純函數?
如果你的(de)代碼中使用純函數,會帶來(lái)以(yǐ)下好處:
易測試。寫過單元測試的(de)前端開發都知道(dào),前端的(de)單元測試是(shì)很難寫的(de),其中很大(dà)一個(gè)原因就(jiù)是(shì)“不(bù)純”的(de)函數太多,一個(gè)函數可能既要(yào / yāo)操作DOM,又要(yào / yāo)發送ajax請求,還可能引用了(le/liǎo)不(bù)知名的(de)全局變量…AngularJS在(zài)官方文檔中就(jiù)直接指出(chū)有些耦合性高的(de)代碼是(shì)難以(yǐ)測試的(de),所以(yǐ)這(zhè)也(yě)是(shì)近些年MVVM框架流行的(de)原因,框架承擔了(le/liǎo)DOM操作,開發者隻負責寫邏輯,從而(ér)讓代碼邏輯更清晰。
可複用。 使用Node.js編寫服務端的(de)同學對一個(gè)詞肯定熟悉——“同構”,也(yě)就(jiù)是(shì)說(shuō),一份js代碼可以(yǐ)同時(shí)在(zài)服務端和(hé / huò)浏覽器端正常運行,而(ér)純函數是(shì)支持同構的(de)。
無副作用。比如多個(gè)不(bù)純的(de)函數同時(shí)修改一個(gè)變量(或操作一個(gè)DOM元素),再加上(shàng)異步等情況,這(zhè)樣就(jiù)很容易引起沖突。而(ér)純函數既不(bù)修改入參也(yě)不(bù)修改外部變量,所以(yǐ)完全不(bù)用擔心。
RxJS
符合純函數特點的(de)第三方開源庫有非常著名的(de)underscore和(hé / huò)lodash,以(yǐ)及更加強大(dà)的(de)RxJS。
RxJS是(shì)微軟推出(chū)的(de)ReactiveX系列(RxJava,Rx.NET,RxScala,RxSwift等)中的(de)一員,目前Github上(shàng)star數已經超過17k。
它可以(yǐ)用來(lái)優雅地(dì / de)處理異步和(hé / huò)事件。主要(yào / yāo)通過它的(de)核心類型 Observable,以(yǐ)及強大(dà)的(de)操作符 (map、filter、reduce、every等,其中大(dà)部分都是(shì)純函數)來(lái)實現。
官方給它最直白的(de)定義是(shì)
可以(yǐ)把 RxJS 當做是(shì)用來(lái)處理事件的(de) Lodash 。
來(lái)一段官方的(de)代碼體驗一下:
// 使用普通的(de) JavaScript 控制按鈕一秒鍾内隻允許點擊一次
var count = 0;
var rate = 1000;
var lastClick = Date.now() - rate;
var button = document.querySelector('button');
button.addEventListener('click', () => {
if (Date.now() - lastClick >= rate) {
console.log(`Clicked ${++count} times`);
lastClick = Date.now();
}
});
// 使用Rx.js實現
var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
.throttleTime(1000)
.scan(count => count + 1, 0)
.subscribe(count => console.log(`Clicked ${count} times`));
使用RxJS的(de)代碼消除了(le/liǎo)一些中間變量,使用操作符來(lái)分步執行邏輯,可讀性更強、耦合性更低,更方便測試和(hé / huò)修改。
關于(yú)RxJS更詳細介紹在(zài)以(yǐ)後的(de)文章中再叙,本文先談一談它的(de)應用。關于(yú)RxJS在(zài)web端和(hé / huò)node.js服務端的(de)應用都不(bù)乏文章,所以(yǐ)這(zhè)一次突破常規,來(lái)講一講在(zài)微信小程序(以(yǐ)下簡稱“小程序”)開發中的(de)使用。
小程序
直接在(zài)小程序中使用RxJS是(shì)會報錯的(de),所以(yǐ)我建立了(le/liǎo)一個(gè)開源項目來(lái)解決這(zhè)個(gè)問題:RxWX(項目地(dì / de)址:https://github.com/yalishizhude/RxJS )。
封裝了(le/liǎo)兩個(gè)js文件。
Rx.js。對Rx.js進行了(le/liǎo)一些修改使其能在(zài)小程序中運行。
RxWX.js。基于(yú)Rx.js對微信的(de)api進行了(le/liǎo)封裝,調用同名API不(bù)再使用回調,而(ér)是(shì)返回Observalbe對象。
安裝
提供兩種安裝途徑
Github
git clone https://github.com/yalishizhude/RxWX.git
可以(yǐ)直接下載項目,将根目錄的(de)Rx.js和(hé / huò)RxWX.js複制到(dào)小程序項目中,也(yě)可以(yǐ)訪問該網址複制粘貼這(zhè)兩個(gè)文件内容。
npm
npm i rxjs-wx
将node_modules/rxjs-wx目錄下的(de)Rx.js和(hé / huò)RxWX.js複制到(dào)小程序項目中。
使用
小程序的(de)API大(dà)多數都不(bù)是(shì)按照純函數的(de)思想設計的(de),把返回結果賦值給入參的(de)success、fail、complete屬性。
在(zài)邏輯簡單複雜的(de)情況下很容堕入“回調地(dì / de)獄”,而(ér)且同步和(hé / huò)異步的(de)接口調用方式也(yě)不(bù)一緻。而(ér)使用RxJS就(jiù)可以(yǐ)解決這(zhè)些問題,下面來(lái)看幾個(gè)例子(zǐ)。
處理回調
假設有這(zhè)樣一個(gè)需求,先通過 wx.getUserInfo
獲取用戶信息,然後傳給後端服務獲取該用戶其它信息,顯示在(zài)頁面上(shàng)。
// 普通代碼
let self = this
wx.getUserInfo({
success: (res) => {
wx.request({
method: 'GET',
url: 'xxx/user',
data: res.userInfo,
success(r) {
self.setData({userInfo:r})
},
fail(e) {
self.setData({userInfo:'not found'})
}
})
},
fail(e) {
console.error(e)
}
})
// 使用RxWX
import obs from './RxWX'
obs.getUserInfo()
.catch(e => console.error(e))
.switchMap(({ userInfo }) => obs.request({ method: 'GET', url: 'xxx/user', data: user }))
.subscribe(userInfo => self.setData({ userInfo: r }), e => self.setData({ userInfo: 'not found' }))
處理事件
曾經在(zài)開發小程序的(de)時(shí)候使用navigator組件碰到(dào)一個(gè)比較嚴重的(de)問題:快速多次點擊的(de)時(shí)候會發生多次頁面跳轉,跳轉完成後需要(yào / yāo)多次點擊“返回”才能退回到(dào)原頁面。
爲(wéi / wèi)了(le/liǎo)解決這(zhè)個(gè)問題,一般可以(yǐ)手動綁定事件,然後進行一個(gè)防抖操作。
// 普通代碼
let tapping = false
...
tap(e) {
if(!tapping) {
wx.navigateTo({ url: '../demo/demo' })
tapping = true
setTimeout(() => tapping=false, 1000)
}
}
// 使用RxWX
import obs from './RxWX'
tap(e) {
obs.navigateTo({ url: '../demo/demo' })
.debounce(1000)
.subscribe()
}
其它
RxWX同時(shí)還支持wx對象的(de)其它非函數屬性,比如:
import obs from './RxWX'
console.log(obs.version)
// {info:"", updateTime:"2017.7.10 19:35:05", version:"1.4.0"}
最後
RxJS和(hé / huò)RxWX是(shì)第三方庫,也(yě)是(shì)進入純函數世界的(de)大(dà)門,更是(shì)一種編寫更好代碼的(de)思維方式。
本文作者小程序聯盟社區博主 搜索關注個(gè)人(rén)公衆号“web學習社”~
本文可被轉發或分享,但必須保留完整圖文信息和(hé / huò)出(chū)處,作者保留追究一切法律責任的(de)權利和(hé / huò)手段~