小程式踩坑日誌(三)----Labrador
小程式模組化開發框架—–Labrador
之前一直用的微信開發者工具去直接修改程式碼,倒不是說這樣不行,就是不太友好,目錄結構混亂,邏輯不清晰,身為開發者,我有時候都會混淆一些東西,更何況團隊開發,
所以不建議使用微信開發工具去直接修改程式碼,今天在網上看見一個Labrador框架,所以準備研究一下,正式學習之前需要具備一些知識 redux,
Labrador的優勢
- 支援微信開發工具支援海量npm包
- 支援es6語法,使用async/await有效避免回撥地獄
- 對小程式框架二次封裝,實現元件的重用和巢狀
- 可繼承redux使用redux資料流控制,讓專案邏輯清晰可維護
- 自動化測試,非常容易編寫單元測試指令碼,不經任何額外配置即可自動化測試
- Flow.js強型別檢查,編寫更加安全穩定的程式碼
- 使用Editor Config及ESLint標準化程式碼風格,方便團隊協作
- 強力壓縮程式碼,儘可能減小程式體積,讓你在1M的限制內做更多的事
環境搭建
首先系統中需要nodejs和npm,然後全域性安裝Labrador-cli
npm install -g labrador-cli
初始化專案
labrador create demo //新建一個專案
cd demo //進入demo
開發流程
在IDE中編輯src目錄下的原始碼,然後再專案根目錄中執行labrador build命令構建專案,然後在微信開發者工具中除錯介面中點選左側選單的 重啟 按鈕 ,即可檢視效果
常用命令
labrador create <name> //建立專案
labrador build 【opation】 //構建當前專案
labrador watch 【opation】 //編輯當前專案並檢測檔案改動
labrador generate 【opation】 <type> <name>
//建立新組建,頁面,redux saga
+ labrador generate page home/home //建立資料夾和新的頁面
+ labrador generate component home //建立home元件
+ labrador generate redux home //建立redux檔案
+ labrador generate saga home //建立saga檔案
wxml中支援import和include,但僅時檢視模板中可用,並非元件可重用,
wxml其實基於可重用元件,但不允許自定義元件
重要的是,只有真正用到的js檔案才被labrador命令加入到專案目錄中。這樣一個小小的改進象徵著我們的小程式可以便捷呼叫NPM倉庫中海量的擴充套件庫!
原始碼中const _ = require('lodash'); 被編譯為 var _ = require('./npm/lodash/lodash.js');
commonents資料夾中存放公用的元件
在 src/pages/index/index.less 中加入程式碼 @import ‘list’ 即可呼叫list元件的樣式,如果在src/components/list中找不到list.less,那麼編譯命令將在NPM包中尋找 node_modules/list/index.less 。
在 src/pages/index/index.xml 中加入程式碼 即可呼叫list元件的模板檔案,component 是Labrador自定義的元件,編譯後對應生成 import 和 template。如果在src/components/list中找不到list.xml,那麼編譯命令將在NPM包中尋找 node_modules/list/index.xml
頁面(pages)也是元件
預設首頁的示例程式碼
index.js
import wx, { Component } 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 {
state = {
userInfo: {},
mottoTitle: 'Hello World',
count: 0
};
children() {
return {
list: {
component: List
},
motto: {
component: Title,
props: {
text: this.state.mottoTitle
}
},
counter: {
component: Counter,
props: {
count: this.state.count,
onChange: this.handleCountChange
}
}
};
}
handleCountChange(count) {
this.setState({ count });
}
//事件處理函式
handleViewTap() {
wx.navigateTo({
url: '../logs/logs'
});
}
async onLoad() {
try {
//呼叫應用例項的方法獲取全域性資料
let userInfo = await wx.app.getUserInfo();
//更新資料
this.setState({ userInfo });
this.update();
} catch (error) {
console.error(error.stack);
}
}
onReady() {
this.setState('mottoTitle', 'Labrador');
}
}
js中export default會匯出一個預設的類,不可手動呼叫Page(),因為在編譯之後pages目錄下所有的js全會自動呼叫Page()方法宣告頁面。
children()方法
children()方法返回該元件依賴,包含的其他自定義元件,
以上的程式碼中包含了三個自定義元件list ,title,counter
children()方法包含兩個屬性,component屬性定義了元件類,props屬性定義了父元件向子元件傳遞的props屬性物件,
頁面也是元件,所有的元件都擁有一樣的生命週期函式onLoad, onReady, onShow, onHide, onUnload,onUpdate 以及setState函式。
注意所有的元件生命週期都支援async,但預設的是普通函式,如果函式內沒有非同步操作,建議採用普通函式,async函式會有一定的效能開銷,可能無法順暢的執行
components和pages兩個目錄的區別在於,components中存放的元件能夠被智慧載入,重用,pages目錄中的元件在編譯時加上pages()呼叫,所以pages目錄中的元件不能被重複呼叫,也就是說不可被其他元件呼叫,否則會報錯,
佈局index.xml
<view class="container">
<view class="userinfo" catchtap="handleViewTap">
<image class="userinfo-avatar" src="{{ state.userInfo.avatarUrl }}" background-size="cover"/>
<text class="userinfo-nickname">{{ state.userInfo.nickName }}</text>
</view>
<view class="usermotto">
<component key="motto" name="title"/>
</view>
<component key="list"/>
<component key="counter"/>
</view>
以上這段程式碼中使用了labrador提供的 標籤,此標籤的作用是匯入一個自定義元件的佈局檔案。標籤有兩個屬性,分別是key(必須)和name(可選,預設是key的值)。key與js邏輯程式碼中的元件key對應,name是 元件的目錄名。
key用來繫結元件js邏輯物件的children中對應的資料,name用於在src/components和node_moudles目錄中尋找子元件模板。
labrador支援多層元件巢狀
自定義元件列表
邏輯 src/components/list/list.js
import wx, { Component } from 'labrador';
import Title from '../title/title';
import Item from '../item/item';
import { sleep } from '../../utils/util';
export default class List extends Component {
constructor(props){
super(props);
this.state = {
items: [
{ id:1, title: 'Labrador' },
{ id:2, title: 'Alaska' }
]
};
}
children (){
return {
title:{
component: Title,
props: { text: 'The List Title' }
},
listItems: this.state.items.map((item) => {
return {
component: Item,
key: item.id,
props: {
item: item,
title: item.title,
isNew: item.isNew,
onChange: (title) => { this.handleChange(item, title) }
}
};
})
};
}
async onLoad() {
await sleep(1000);
this.setState({
items: [{ id:3, title: 'Collie', isNew: true }].concat(this.data.items)
});
}
handleChange(item, title) {
let items = this.state.items.map((i) => {
if(item.id == i.id){
return Object.assign({},i,{ title });
}
return i;
});
this.setState({ items });
}
}
以上程式碼中children()返回的listItem子元件定義時,是一個元件陣列,陣列的每一項都是一個子元件的定義,並且需要每一項的key屬性,key屬性將用於模板渲染效能優化,建議將唯一且不宜變化的值設為子元件的key