摘要:自定義組件的自定義組件,是基于微信小程序框架的組件之上,進一步自定義組合,擁有邏輯處理和樣式。這樣做的目的請參見微信小程序開發三宗罪和解決方案項目中通用自定義組件存放在目錄,一個組件一般由三個文件組成,和分別對應微信小程序框架的和文件。
Labrador 是一個專為微信小程序開發的組件化開發框架。
特性使用Labrador框架可以使微信開發者工具支持加載海量NPM包
支持ES6/7標準代碼,使用async/await能夠有效避免回調地獄
組件重用,對微信小程序框架進行了二次封裝,實現了組件重用和嵌套
自動化測試,非常容易編寫單元測試腳本,不經任何額外配置即可自動化測試
使用Editor Config及ESLint標準化代碼風格,方便團隊協作
安裝首先您的系統中安裝Node.js和npm v3 下載Node.js,然后運行下面的命令將全局安裝Labrador命令行工具。
npm install -g labrador-cli初始化項目
mkdir demo # 新建目錄 cd demo # 跳轉目錄 npm init # 初始化npm包 labrador init # 初始化labrador項目項目目錄結構
demo # 項目根目錄 ├── .labrador # Labrador項目配置文件 ├── .babelrc # babel配置文件 ├── .editorconfig # Editor Config ├── .eslintignore # ESLint 忽略配置 ├── .eslintrc # ESLint 語法檢查配置 ├── package.json ├── dist/ # 目標目錄 ├── node_modules/ └── src/ # 源碼目錄 ├── app.js ├── app.json ├── app.less ├── components/ # 通用組件目錄 ├── pages/ # 頁面目錄 └── utils/
配置開發工具注意 dist目錄中的所有文件是由labrador命令編譯生成,請勿直接修改
項目初始化后使用WebStorm或Sublime等你習慣的IDE打開項目根目錄。然后打開 微信web開發者工具 新建項目,本地開發目錄選擇 dist 目標目錄。
開發流程在WebStorm或Sublime等IDE中編輯 src 目錄下的源碼,然后在項目根目錄中運行labrador build 命令構建項目,然后在 微信web開發者工具 的調試界面中點擊左側菜單的 重啟 按鈕即可查看效果。
我們在開發中, 微信web開發者工具 僅僅用來做調試和預覽,不要在 微信web開發者工具 的編輯界面修改代碼。
微信web開發者工具 會偶爾出錯,表現為點擊 重啟 按鈕沒有反應,調試控制臺輸出大量的無法require文件的錯誤,編輯 界面中代碼文件不顯示。這是因為 labrador build 命令會更新整個 dist 目錄,而 微信web開發者工具 在監測代碼改變時會出現異常,遇到這種情況只需要關掉 微信web開發者工具 再啟動即可。
我們還可以使用 labrador watch 命令來監控 src 目錄下的代碼,當發生改變后自動構建,不用每一次編輯代碼后手動運行 labrador build 。
所以最佳的姿勢是:
在項目中運行 labrador watch
在WebStorm中編碼,保存
切換到 微信web開發者工具 中調試、預覽
再回到WebStorm中編碼
...
labrador 命令 labrador init 初始化項目命令注意此命令會初始化當前的目錄為項目目錄。
labrador build 構建當前項目Usage: labrador build [options] Options: -h, --help output usage information -V, --version output the version number -c, --catch 在載入時自動catch所有JS腳本的錯誤 -t, --test 運行測試腳本 -d, --debug DEBUG模式 -m, --minify uglify壓縮代碼labrador watch 監測文件變化
Usage: labrador watch [options] Options: -h, --help output usage information -V, --version output the version number -c, --catch 在載入時自動catch所有JS腳本的錯誤 -t, --test 運行測試腳本 -d, --debug DEBUG模式 -m, --minify uglify壓縮代碼labrador 庫
labrador 庫對全局的 wx 變量進行了封裝,將大部分 wx 對象中的方法進行了Promise支持, 除了以 on* 開頭或以 *Sync 結尾的方法。在如下代碼中使用 labrador 庫。
import wx from "labrador"; console.log(wx.version); wx.app; // 和全局的 getApp() 函數效果一樣,代碼風格不建議粗暴地訪問全局對象和方法 wx.Component; // Labrador 自定義組件基類 wx.Types; // Labrador 數據類型校驗器集合 wx.login; // 封裝后的微信登錄接口 wx.getStorage; // 封裝后的讀取緩存接口 //... 更多請參見 https://mp.weixin.qq.com/debug/wxadoc/dev/api/
我們建議不要再使用 wx.getStorageSync() 等同步阻塞方法,而在 async 函數中使用 await wx.getStorage() 異步非阻塞方法提高性能,除非遇到特殊情況。
app.jssrc/app.js 示例代碼如下:
import wx from "labrador"; import {sleep} from "./utils/util"; export default class { globalData = { userInfo: null }; async onLaunch() { //調用API從本地緩存中獲取數據 let res = await wx.getStorage({ key: "logs" }); let logs = res.data || []; logs.unshift(Date.now()); await wx.setStorage({ key: "logs", data: logs }); this.timer(); } async timer() { while (true) { console.log("hello"); await sleep(10000); } } async getUserInfo() { if (this.globalData.userInfo) { return this.globalData.userInfo; } await wx.login(); let res = await wx.getUserInfo(); this.globalData.userInfo = res.userInfo; return res.userInfo; } }
代碼中全部使用ES6/7標準語法。代碼不必聲明 use strict ,因為在編譯時,所有代碼都會強制使用嚴格模式。
代碼中并未調用全局的 App() 方法,而是使用 export 語法默認導出了一個類,在編譯后,Labrador會自動增加 App() 方法調用,所有請勿手動調用 App() 方法。這樣做是因為代碼風格不建議粗暴地訪問全局對象和方法。
自定義組件Labrador的自定義組件,是基于微信小程序框架的組件之上,進一步自定義組合,擁有邏輯處理和樣式。這樣做的目的請參見 微信小程序開發三宗罪和解決方案
項目中通用自定義組件存放在 src/compontents 目錄,一個組件一般由三個文件組成,*.js 、 *.xml 和 *.less 分別對應微信小程序框架的 js 、 wxml 和 wxss 文件。在Labardor項目源碼中,我們特意采用了 xml 和 less 后綴以示區別。如果組件包含單元測試,那么在組件目錄下會存在一個 *.test.js 的測試腳本文件。
自定義組件示例下面是一個簡單的自定義組件代碼實例:
import wx from "labrador"; import randomColor from "../../utils/random-color"; const { string } = wx.Types; export default class Title extends wx.Component { propTypes = { text: string }; props = { text: "" }; data = { text: "", color: randomColor() }; onUpdate(props) { this.setData("text", props.text); } handleTap() { this.setData({ color: randomColor() }); } }
自定義組件的邏輯代碼和微信框架中的page很相似,最大的區別是在js邏輯代碼中,沒有調用全局的 Page() 函數聲明頁面,而是用 export 語法導出了一個默認的類,這個類必須繼承于 labrador.Component 組件基類。
相對于微信框架中的page,Labrador自定義組件擴展了 propTypes 、 props 、 children 選項及 onUpdate 生命周期函數。children 選項代表當前組件中的子組件集合,此選項將在下文中敘述。
Labrador的目標是構建一個可以重用、嵌套的自定義組件方案,在現實情況中,當多個組件互相嵌套組合,就一定會遇到父子組件件的數據和消息傳遞。因為所有的組件都實現了 setData 方法,所以我們可以使用 this.children.foobar.setData(data) 或 this.parent.setData(data) 這樣的代碼調用來解決父子組件間的數據傳遞問題,但是,如果項目中出現大量這樣的代碼,那么數據流將變得非常混亂。
我們借鑒了 React.js 的思想,為組件增加了 props 機制。子組件通過 this.props 得到父組件給自己傳達的參數數據。父組件怎樣將數據傳遞給子組件,我們下文中敘述。
onUpdate 生命周期函數是當組件的 props 發生變化后被調用,類似React.js中的 componentWillReceiveProps 所以我們可以在此函數體內監測 props 的變化。
組件定義時的 propTypes 選項是對當前組件的props參數數據類型的定義。 props 選項代表的是當前組件默認的各項參數值。propTypes 、 props 選項都可以省略,但是強烈建議定義 propTypes,因為這樣可以使得代碼更清晰易懂,另外還可以通過Labrador自動檢測props值類型,以減少BUG。為優化性能,只有在DEBUG模式下才會自動檢測props值類型。
編譯時加上 -d 參數時即可進入DEBUG模式,在代碼中任何地方都可以使用魔術變量 __DEBUG__ 來判斷是否是DEBUG模式。
另外,Labrador自定義組件的 setData 方法,支持兩種傳參方式,第一種像微信框架一樣接受一個 object 類型的對象參數,第二種方式接受作為KV對的兩個參數,setData 方法將自動將其轉為 object。
注意 組件中事件響應方法必須以 handle 開頭!例如上文中的 handleTap,否則子組件將無法與模板綁定。這樣做也是為了代碼風格統一,方便團隊協作。建議事件響應方法命名采用 handle + 組件名 + 事件名 例如:handleUsernameChange handleLoginButtonTap ,這樣我們很容易區分是模板上哪一個組件發生了什么事件,如果省略中間的名詞,如 handleTap ,則代表當前整個自定義組件發生了 tap 事件。
{{text}}
XML布局文件和微信WXML文件語法完全一致,只是擴充了一個自定義標簽
.title-text { font-weight: bold; font-size: 2em; }
雖然我們采用了LESS文件,但是由于微信小程序框架的限制,不能使用LESS的層級選擇及嵌套語法。但是我們可以使用LESS的變量、mixin、函數等功能方便開發。
頁面我們要求所有的頁面必須存放在 pages 目錄中,每個頁面的子目錄中的文件格式和自定義組件一致,只是可以多出一個 *.json 配置文件。
頁面示例下面是默認首頁的示例代碼:
import wx from "labrador"; import List from "../../components/list/list"; import Title from "../../components/title/title"; import Counter from "../../components/counter/counter"; export default class Index extends wx.Component { data = { userInfo: {}, mottoTitle: "Hello World", count: 0 }; get children() { return { list: new List(), motto: new Title({ text: "@mottoTitle" }), counter: new Counter({ count: "@count", onChange: this.handleCountChange }) }; } async onLoad() { try { //調用應用實例的方法獲取全局數據 let userInfo = await wx.app.getUserInfo(); //更新數據 this.setData({ userInfo }); this.update(); } catch (error) { console.error(error.stack); } } onReady() { this.setData("mottoTitle", "Labrador"); } handleCountChange(count) { this.setData({ count }); } //事件處理函數 handleViewTap() { wx.navigateTo({ url: "../logs/logs" }); } }
頁面代碼的格式和自定義組件的格式一模一樣,我們的思想是 頁面也是組件。
js邏輯代碼中同樣使用 export default 語句導出了一個默認類,也不能手動調用 Page() 方法,因為在編譯后,pages 目錄下的所有js文件全部會自動調用 Page() 方法聲明頁面。
我們看到組件類中,有一個對象屬性 children ,這個屬性定義了該組件依賴、包含的其他自定義組件,在上面的代碼中頁面包含了三個自定義組件 list 、 title 和 counter ,這個三個自定義組件的 key 分別為 list 、 motto 和 counter。
自定義組件類在實例化時接受一個類型為 object 的參數,這個參數就是父組件要傳給子組件的props數據。一般情況下,父組件傳遞給子組件的props屬性在其生命周期中是不變的,這是因為JS的語法和小程序框架的限制,沒有React.js的JSX靈活。但是我們可以傳遞一個以 @ 開頭的屬性值,這樣我們就可以把子組建的 props 屬性值綁定到父組件的 data 上來,當父組件的 data 發生變化后,Labrador將自動更新子組件的 props。例如上邊代碼中,將子組件 motto 的 text 屬性綁定到了 @mottoTitle。那么在 onReady 方法中,將父組件的 mottoTitle 設置為 Labrador,那么子組件 motto 的 text 屬性就會自動變為 Labrador。
頁面也是組件,所有的組件都擁有一樣的生命周期函數onLoad, onReady, onShow, onHide, onUnload,onUpdate 以及setData函數。
componets 和 pages 兩個目錄的區別在于,componets 中存放的組件能夠被智能加載,pages 目錄中的組件在編譯時自動加上 Page() 調用,所以,pages 目錄中的組件不能被其他組件調用,否則將出現多次調用Page()的錯誤。如果某個組件需要重用,請存放在 componets 目錄或打包成NPM包。
{{ userInfo.nickName }}
XML布局代碼中,使用了Labrador提供的
@import "list"; @import "title"; @import "counter"; .motto-title-text { font-size: 3em; padding-bottom: 1rem; } /* ... */
LESS樣式文件中,我們使用了 @import 語句加載所有子組件樣式,這里的 @import "list" 語句按照LESS的語法,會首先尋找當前目錄 src/pages/index/ 中的 list.less 文件,如果找不到就會按照Labrador的規則智能地嘗試尋找 src/componets 和 node_modules 目錄中的組件樣式。
接下來,我們定義了 .motto-title-text 樣式,這樣做是因為 motto key 代表的title組件的模板中(src/compontents/title/title.xml)有一個view 屬于 title-text 類,編譯時,Labrador將自動為其增加一個前綴 motto- ,所以編譯后這個view所屬的類為 title-text motto-title-text (可以查看 dist/pages/index/index.xml)。那么我們就可以在父組件的樣式代碼中使用 .motto-title-text 來重新定義子組件的樣式。
Labrador支持多層組件嵌套,在上述的實例中,index 包含子組件 list 和 title,list 包含子組件 title,所以在最終顯示時,index 頁面上回顯示兩個 title 組件。
詳細代碼請參閱 labrador init 命令生成的示例項目。
自動化測試我們規定項目中所有后綴為 *.test.js 的文件為測試腳本文件。每一個測試腳本文件對應一個待測試的JS模塊文件。例如 src/utils/util.js 和 src/utils/utils.test.js 。這樣,項目中所有模塊和其測試文件就全部存放在一起,方便查找和模塊劃分。這樣規劃主要是受到了GO語言的啟發,也符合微信小程序一貫的目錄結構風格。
在編譯時,加上 -t 參數即可自動調用測試腳本完成項目測試,如果不加 -t 參數,則所有測試腳本不會被編譯到 dist 目錄,所以不必擔心項目會肥胖。
普通JS模塊測試測試腳本中使用 export 語句導出多個名稱以 test* 開頭的函數,這些函數在運行后會被逐個調用完成測試。如果test測試函數在運行時拋出異常,則視為測試失敗,例如代碼:
// src/util.js // 普通項目模塊文件中的代碼片段,導出了一個通用的add函數 export function add(a, b) { return a + b; }
// src/util.test.js // 測試腳本文件代碼片段 import assert from "assert"; //測試 util.add() 函數 export function testAdd(exports) { assert(exports.add(1, 1) === 2); }
代碼中 testAdd 即為一個test測試函數,專門用來測試 add() 函數,在test函數執行時,會將目標模塊作為參數傳進來,即會將 util.js 中的 exports 傳進來。
自定義組件測試自定義組件的測試腳本中可以導出兩類測試函數。第三類和普通測試腳本一樣,也為 test* 函數,但是參數不是 exports 而是運行中的、實例化后的組件對象。那么我們就可以在test函數中調用組件的方法或則訪問組件的props 和 data 屬性,來測試行為。另外,普通模塊測試腳本是啟動后就開始逐個運行 test* 函數,而組件測試腳本是當組件 onReady 以后才會開始測試。
自定義組件的第二類測試函數是以 on* 開頭,和組件的生命周期函數名稱一模一樣,這一類測試函數不是等到組件 onReady 以后開始運行,而是當組件生命周期函數運行時被觸發。函數接收兩個參數,第一個為組件的對象引用,第二個為run 函數。比如某個組件有一個 onLoad 測試函數,那么當組件將要運行 onLoad 生命周期函數時,先觸發 onLoad 測試函數,在測試函數內部調用 run() 函數,繼續執行組件的生命周期函數,run() 函數返回的數據就是生命周期函數返回的數據,如果返回的是Promise,則代表生命周期函數是一個異步函數,測試函數也可以寫為async 異步函數,等待生命周期函數結束。這樣我們就可以獲取run()前后兩個狀態數據,最后對比,來測試生命周期函數的運行是否正確。
第三類測試函數與生命周期測試函數類似,是以 handle* 開頭,用以測試事件處理函數是否正確,是在對應事件發生時運行測試。例如:
// src/components/counter/counter.test.js export function handleTap(c, run) { let num = c.data.num; run(); let step = c.data.num - num; if (step !== 1) { throw new Error("計數器點擊一次應該自增1,但是自增了" + step); } }
生命周期測試函數和事件測試函數只會執行一次,自動化測試的結果將會輸出到Console控制臺。
項目配置文件labrador init 命令在初始化項目時,會在項目根目錄中創建一個 .labrador 項目配置文件,如果你的項目是使用 labrador-cli 0.3 版本創建的,可以手動增加此文件。
配置文件為JSON格式,默認配置為:
{ "npmMap":{ }, "uglify":{ "mangle": [], "compress": { "warnings": false } } }
npmMap 屬性為NPM包映射設置,例如 {"underscore":"lodash"} 配置,如果你的源碼中有require("underscore") 那么編譯后將成為 require("lodash")。這樣做是為了解決小程序的環境限制導致一些NPM包無法使用的問題。比如我們的代碼必須依賴于包A,A又依賴于B,如果B和小程序不兼容,將導致A也無法使用。在這總情況下,我們可以Fork一份B,起名為C,將C中與小程序不兼容的代碼調整下,最后在項目配置文件中將B映射為C,那么在編譯后就會繞過B而加載C,從而解決這個問題。
uglify 屬性為 UglifyJs2 的壓縮配置,在編譯時附加 -m 參數即可對項目中的所有文件進行壓縮處理。
ChangeLog 2016-10-09labrador 0.3.0
重構自定義組件支持綁定子組件數據和事件
2016-10-12labrador 0.4.0
增加自定義組件props機制
自動化測試
UglifyJS壓縮集成
NPM包映射
增加.labrador項目配置文件
貢獻者鄭州脈沖軟件科技有限公司
梁興臣
開源協議本項目依據MIT開源協議發布,允許任何組織和個人免費使用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87963.html
摘要:是一個專為微信小程序開發的模塊化的前端開發框架在微信小程序開發三宗罪和解決方案一文中我向大家闡述了微信小程序開發的三個弊端,并提供了框架來解決這些弊端。注意雖然我們采用了文件,但是由于微信小程序框架的限制,不能使用的層級選擇及嵌套語法。 Labrador 是一個專為微信小程序開發的模塊化的前端開發框架 在微信小程序開發三宗罪和解決方案一文中我向大家闡述了微信小程序開發的三個弊端,并提供...
閱讀 1917·2021-11-23 09:51
閱讀 1246·2019-08-30 15:55
閱讀 1613·2019-08-30 15:44
閱讀 759·2019-08-30 14:11
閱讀 1146·2019-08-30 14:10
閱讀 915·2019-08-30 13:52
閱讀 2630·2019-08-30 12:50
閱讀 615·2019-08-29 15:04