前言微信小程序是一個工程,就和蓋房子一樣,打好了地基,才能保證后續(xù)工程師建立在可靠牢固的基礎(chǔ)上。筆者需要經(jīng)常新建項目,每次都要重復“修改項目結(jié)構(gòu) - 從老項目中復制粘貼文件 - ...
微信小程序是一個工程,就和蓋房子一樣,打好了地基,才能保證后續(xù)工程師建立在可靠牢固的基礎(chǔ)上。
筆者需要經(jīng)常新建項目,每次都要重復“修改項目結(jié)構(gòu) -> 從老項目中復制粘貼文件 -> 刪除一些老項目中代碼”這樣的過程,實在是…費心費力。
另一個痛點是:每次新建小程序頁面要生成三個文件名相同的文件 ( .wxml、.wxss 和 .js ),命令行太長(據(jù)微信同事:也可以在 app.json 的 pages
字段下添加新頁面的路徑,保存后也會生成對應(yīng)的文件)。
因此,閱讀本文需要對小程序開發(fā)稍有了解( 簡易教程指路 )。
我們現(xiàn)在有兩個目標:
pages
字段創(chuàng)建頁面,保存后生成這三個文件。筆者沒有采用這個方法的緣由一個是開始時不知道有此功能,另一個是不合平時的操作習慣,再者想到 js 文件初始化后,需要引入常用庫,要插入代碼片段,所以保留了這個功能 。這兩個需求其實很簡單,不需要 GUI,所以我們可以做一個 npm 命令行工具。想象一下這個命令行用起來應(yīng)該是什么樣的呢:
~ npm install wxapp -g
~ wxapp -i myapp && cd myapp
~ wxapp -p list
用流程圖示意就是:
正式開始之前,請先確認本地的開發(fā)環(huán)境 ,筆者的本地環(huán)境是:
~ npm -v
3.10.10
~ node -v
v6.9.4
我們把問題分解為三步:
不用擔心,都很容易解決,我們一個個看。
命令行工具
package.json 中有一個字段是 bin
:
{
...
"bin": {
"mywxapp": "./index.js"
}
}
這個字段可以將開發(fā)者希望執(zhí)行的腳本注冊到環(huán)境變量 (PATH) 中,不同的 key 對應(yīng)執(zhí)行不同的腳本。也就是說現(xiàn)在,當我們直接在命令行中執(zhí)行:
~ mywxapp
等價于在 terminal 中執(zhí)行:
~ /path/to/index.js
第一個問題輕松解決,關(guān)于 bin
字段更多信息請參考 npm 文檔中 package.json 一節(jié) 。
命令行參數(shù)
執(zhí)行 index.js 時,可以通過 process.argv
獲取執(zhí)行時的參數(shù),但是要從參數(shù)數(shù)組中拆分出參數(shù)無疑很麻煩。不過,npm 發(fā)展至今,處理命令行參數(shù)的庫肯定存在, 就是 commander 。簡單好用易上手,那么第二個問題也解決啦。
考慮項目模板的存放位置,是集成到工具中,還是和工具分開呢?
筆者選擇分開管理。 在一個單獨的模板代碼倉庫中管理模板內(nèi)容,方便我們維護。目前的模板還比較簡單(詳見下文“模板詳解”),只有標準目錄結(jié)構(gòu),預期后面會加上自動化的部分(比如 less -> wxss),所以未來會改動比較頻繁。
download-git-repo 可以把給定地址的倉庫內(nèi)容拷貝到執(zhí)行目錄中 。API 簡單,所以就是它了。
問題都解決了,現(xiàn)在就讓我們看看偽代碼 (注意:偽碼中沒有考慮出錯情況) :
const mkdirp = require('mkdirp');
const download = require('download-git-repo');
// 創(chuàng)建項目
function initProj(projName){
if(currentDir.exsits(projName))
console.warn(projName + '項目已經(jīng)存在于當前目錄中,請使用別的名字');
else
download('path/to/tmpl', currentDir+'/'+projName);
}
// 注冊頁面
function registerPage(pagesName) {
// 讀配置文件
readFile(configFile, function(data){
// 將新建的所有頁面都寫入配置文件中
for(name in pagesName){
data.pages.push('pages/' + name + '/' + name);
}
writeFile(configFile);
});
}
// 創(chuàng)建頁面
function createPage(pages) {
for(var index in pages) {
var page = pages[index];
mkdirp(pagePath, function(){
createFile(page+'.wxml');
createFile(page+'.wxss');
createFile(page+'.js');
})
}
// 將頁面注冊到 app.json 中
registerPage(pages);
}
在編寫好了這個工具之后,只需要在 本地全局使用 的話:
npm install -g
在本地開發(fā)過程中,如果更新了開發(fā)版本的代碼,需要更新同步到全局,這時候需要執(zhí)行:
npm link
就會看到安裝到環(huán)境變量中的工具目錄地址已經(jīng)和開發(fā)目錄關(guān)聯(lián)起來了:
~/Documents/kmokidd/cli-build$ npm link
/usr/local/bin/wxapp -> /usr/local/lib/node_modules/@kmokidd/wxapp-generator/index.js
/usr/local/bin/node_modules/@kmokidd/wxapp-generator -> /Users/kmokidd/Documents/kmokidd/cli-build/index.js
使用起來是這樣的:
模板和插件地址將附在參考資料一節(jié)中
如果和筆者一樣,希望在多個機器上使用這個工具,可以選擇發(fā)布到 npm 官網(wǎng)上。發(fā)布步驟非常簡單,基本上就是:
npm login
npm publish
不過筆者考慮到,項目模板畢竟是因人而異的東西,所以選擇了發(fā)布 scope package,也就是在插件的 package.json 中的 name
字段使用 @scopeName/wxapp-generator
這樣的值。
如果你也有類似的想法,并且也是個 npm 免費用戶,那么發(fā)布的時候要執(zhí)行:
npm publish --access public
scope 對使用沒有任何影響,但是安裝的時候要記得帶上 scope name 執(zhí)行:
npm install @scopeName/wxapp-generator -g
一千個人中有一千種項目模板。根據(jù)業(yè)務(wù)/個人愛好不同,大家的項目模板可能也相去甚遠。筆者自覺目前的模板用起來還不錯,將在這一節(jié)介紹一下。以下是項目的文件結(jié)構(gòu):
wxapp
├── app.js
├── app.json
├── app.wxss
├── base-styles/
├── images/
├── pages/
│ ├── tmpl/
├── utils /
│ ├── view.js
│ ├── util.js
│ ├── polyfiil.js
└── └── Deferred.js
之所以采用這樣的結(jié)構(gòu),是希望盡可能解耦 UI 邏輯與業(yè)務(wù)邏輯。但是由于完全解耦是不可能的,基本思路是單純的“變量分離”。通常 UI 的改變是通過 class 的切換或者內(nèi)聯(lián)樣式的調(diào)整,所以筆者的思路,是將“要切換的 class”或者“要調(diào)整的內(nèi)聯(lián)樣式”作為變量,由于大部分情況下業(yè)務(wù)邏輯和 UI 變化是聯(lián)動的,通過抽離出來的變量,實現(xiàn)在業(yè)務(wù)邏輯中簡單直白地改變 UI。
可能看到這里,讀者會有些困惑,那讓我們直接以「企鵝聽書」為例,具象地看看筆者是怎么做的吧。聽書的界面會出現(xiàn)變化的時以下兩種場景:
class
來控制 。
上文的文件結(jié)構(gòu)中的 view.js 就是 UI 邏輯的代碼。pages/ 目錄中的 js 文件將通過 import
引用 view.js,view.js 中的接口分為“通用”和“頁面使用”這兩個類型:
module.exports = {
// 通用
general: {
hide: 'hide', // 變量分離在此
show: 'show'
},
// 播放器頁面
playerView : {
class: {
listItemPlaying: 'playing'
}
}
// 其他頁面如果也有需要,以頁面為單位添加...
}
如果未來出現(xiàn)更多 UI 變化的場景,可以再通過變量添加上去,比如 pageView.id
。
舉個超級簡單的例子(如下),模擬工作流程:
class
class
寫到 view.js 中,并暴露接口event handler
event handler
的具體內(nèi)容,也就是切換 class
的觸發(fā)條件
老司機一看就知道是 MVVC 模式,這樣分離也就是為了 UI 有獨立的控制器,不至于和業(yè)務(wù)邏輯耦合嚴重,在頁面開發(fā)的階段就可以完成 UI 上的變化。從這個角度上看,小程序反而能給 UI 工程師更多控制 UI 邏輯的能力,確定好代碼規(guī)范和接口。
初始化一個項目是開始編碼的第一步,值得多花一些時間找到合適團隊合適自己的項目模板。
結(jié)束之前,先允許筆者打一個廣告,企鵝 FM 有兩個小程序:致敬傳統(tǒng)電臺,聽廣播節(jié)目的「小電臺」;聽有聲小說專用的「企鵝聽書」。以及輕量版的「微云」。大家可以掃碼體驗。性能的優(yōu)化和功能的完善也在一步步迭代中,希望大家多多使用多多反饋意見~ 比心
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)