話不多說先上圖,簡要說明一下干了些什么事。圖可能太模糊,可以點(diǎn) svg 看看
最近公司開展了小程序的業(yè)務(wù),派我去負(fù)責(zé)這一塊的業(yè)務(wù),其中需要處理的一個(gè)問題是接入我們web開發(fā)的傳統(tǒng)架構(gòu)-- 模塊化開發(fā) 。
我們來詳細(xì)說一下模塊化開發(fā)具體是怎么樣的。
目錄結(jié)構(gòu)大體如圖所示,一個(gè)模塊包含了他自己的pages / components / assets / model / mixins / apis / routes / scss等等。
這種開發(fā)模式的好處不言而喻,每個(gè)人都可以并行開發(fā),大大提升開發(fā)速度。這次就是要移植這種開發(fā)模式到小程序中。
背景說完了,那么來明確一下我們的目標(biāo)。
對(duì)應(yīng)到我們的目錄結(jié)構(gòu)中,每個(gè)模塊實(shí)際上就是一系列的page組件。要組合這一系列的模塊,那么很簡單,我們要做的就是把這一系列page的路由掃描成一個(gè)路由表,然后 插入到小程序的入口--app.json中 。對(duì)應(yīng)wepy框架那即是app.wpy中的pages字段。
export default class extends wepy.app { config = { pages: 'modules/home/pages/index',//here!!!! window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: '大家好我是渣渣輝', navigationBarTextStyle: 'black' } } //... }
第一步!先得到所有pages的路由并綜合成一個(gè) 路由表 !
我的方案是,在每個(gè)模塊中新建一份routes文件,相當(dāng)于注冊每個(gè)需要插入到入口的page的路由,不需要接入業(yè)務(wù)的page就不用注冊啦。是不是很熟悉呢,對(duì)的,就是參考vue-router的注冊語法。
//routes.js module.exports = [ { name: 'home-detail',//TODO: name先占位,后續(xù)再嘗試通過讀name跳轉(zhuǎn)某頁 page: 'detail',//需要接入入口的page的文件名。例如這里是index.wpy。相對(duì)于src/的路徑就是`modules/${moduleName}/pages/index`。 }, { name: 'home-index', page: 'index', meta: { weight: 100//這里加了一個(gè)小功能,因?yàn)樾〕绦蛑付╬ages數(shù)組的第一項(xiàng)為首頁,后續(xù)我會(huì)通過這個(gè)權(quán)重字段來給pages路由排序。權(quán)重越高位置越前。 } } ]
而掃描各個(gè)模塊并合并路由表的腳本非常簡單,讀寫文件就ok了。
const fs = require('fs') const path = require('path') const routeDest = path.join(__dirname, '../src/config/routes.js') const modulesPath = path.join(__dirname, '../src/modules') let routes = [] fs.readdirSync(modulesPath).forEach(module => { if(module.indexOf('.DS_Store') > -1) return const route = require(`${modulesPath}/${module}/route`) route.forEach(item => { item.page = `modules/${module}/pages/${item.page.match(/\/?(.*)/)[1]}` }) routes = routes.concat(route) }) fs.writeFileSync(routeDest,`module.exports = ${JSON.stringify(routes)}`, e => { console.log(e) })
路由排序策略
const strategies = { sortByWeight(routes) { routes.sort((a, b) => { a.meta = a.meta || {} b.meta = b.meta || {} const weightA = a.meta.weight || 0 const weightB = b.meta.weight || 0 return weightB - weightA }) return routes } }
最后得出路由表
const Strategies = require('../src/lib/routes-model') const routes = Strategies.sortByWeight(require('../src/config/routes')) const pages = routes.map(item => item.page) console.log(pages)//['modules/home/pages/index', 'modules/home/pages/detail']
So far so good...問題來了,如何替換入口文件中的路由數(shù)組。我如下做了幾步嘗試。
我第一感覺就是,這不很簡單嗎?在wepy編譯之前,先跑腳本得出路由表,再import這份路由表就得了。
import routes from './routes' export default class extends wepy.app { config = { pages: routes,//['modules/home/pages/index'] window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: '大家好我是渣渣輝', navigationBarTextStyle: 'black' } } //... }
然而這樣小程序肯定會(huì)炸啦,pages字段的值必須是靜態(tài)的,在小程序運(yùn)行之前就配置好,動(dòng)態(tài)引入是不行的!不信的話諸君可以試試。那么就是說,劃重點(diǎn)--- 我們必須在wepy編譯之前再預(yù)編譯一次 ---事先替換掉pages字段的值!
既然要事先替換,那就是要精準(zhǔn)定位pages字段的值,然后再替換掉。難點(diǎn)在于如果精準(zhǔn)定位pages字段的值呢?
最撈然而最快的方法:正則匹配。
事先定好編碼規(guī)范,在pages字段的值的前后添加 /* __ROUTES__ */ 的注釋
腳本如下:
const fs = require('fs') const path = require('path') import routes from './routes' function replace(source, arr) { const matchResult = source.match(/\/\* __ROUTE__ \*\/([\s\S]*)\/\* __ROUTE__ \*\//) if(!matchResult) { throw new Error('必須包含/* __ROUTE__ */標(biāo)記注釋') } const str = arr.reduce((pre, next, index, curArr) => { return pre += `'${curArr[index]}', ` }, '') return source.replace(matchResult[1], str) } const entryFile = path.join(__dirname, '../src/app.wpy') let entry = fs.readFileSync(entryFile, {encoding: 'UTF-8'}) entry = replace(entry, routes) fs.writeFileSync(entryFile, entry)
app.wpy的變化如下:
//before export default class extends wepy.app { config = { pages: [ /* __ROUTE__ */ /* __ROUTE__ */ ], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: '大家好我是渣渣輝', navigationBarTextStyle: 'black' } } //... } //after export default class extends wepy.app { config = { pages: [ /* __ROUTE__ */'modules/home/pages/index', /* __ROUTE__ */ ], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#fff', navigationBarTitleText: '大家好我是渣渣輝', navigationBarTextStyle: 'black' } } //... }
行吧,也總算跑通了。因?yàn)轫?xiàng)目很趕,所以先用這個(gè)方案開發(fā)了一個(gè)半星期。開發(fā)完之后總覺得這種方案太難受,于是密謀著換另一種各精準(zhǔn)的自動(dòng)的方案。。。
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)