多重影分身:一套程式碼如何生成多個小程式?
前言
影分身術,看過火影的都知道,一個本體,多個分身。
大家肯定要問了,那小程式開發跟影分身術也能扯上關係?沒錯,那自然就是:一套程式碼,多個小程式啦。
各位先別翻白眼,且聽我細細說來。
如今小程式發展如日中天,再加上微信的力推,很多公司的業務也都慢慢的轉向小程式,這讓我這個安卓開發,也不得不開始了小程式開發之旅。
然而隨著公司的發展,客戶越來越多,核心功能相同的小程式,需要上架多個小程式分別給不同的客戶使用,每個小程式之間又存在這一小部分的定製化,比如介面展示的不同、小功能的差異等等。
這可讓我這個剛接觸小程式開發的前端菜鳥抓狂了,每個小程式複製一份程式碼出來,然後做定製化的修改?這豈不是如果哪天核心業務有改動,我得對每套程式碼分別改動一次?不行,即使是菜鳥,對這種弄出多套重複程式碼的行為也是無法容忍的!
於是,就有了針對這種場景下的一個解決方案:給小程式開發來個影分身術。
Github地址:https://github.com/BakerJQ/WeAppBunXin
該專案基於Taro框架,由凹凸實驗室開源,非常感謝他們的努力付出。
之所以選用Taro,主要是因為它採用React語法標準,而本人之前有過ReactNative開發經驗。
由於本人接觸前端開發時間不長,文中若出現了錯誤或者有更好的方案,歡迎各位包容和指正,萬分感謝。
影分身之基礎配置
影分身的能力,主要來源於Taro所提供的編譯能力,所以需要對Taro的編譯配置和編譯配置詳情有所瞭解。
我們先來看看配置的相關檔案目錄:
config目錄為Taro初始化後的預設配置目錄,圖中藍色框框內的三個檔案(dev、index、prod)為預設生成的配置檔案,剩下的檔案,則為分身所需的配置。圖中配置了三個分身,我們以channel1為例,config是該分身的一些配置,project.config.json就是該分身小程式的基本配置,如:
{ \u0026quot;miniprogramRoot\u0026quot;: \u0026quot;./\u0026quot;, \u0026quot;projectname\u0026quot;: \u0026quot;channel1\u0026quot;, \u0026quot;description\u0026quot;: \u0026quot;channel1\u0026quot;, \u0026quot;appid\u0026quot;: \u0026quot;wx8888888888888\u0026quot;, ...}
channel.js檔案,則是用來指定,當前需要編譯哪個小程式,如:
module.exports = { channel: 'channel1'}
在預設的編譯配置入口檔案index.js中,我們需要配置小程式的輸出目錄,配置如下:
const channelInfo = require('./channel')const config = { ... //輸入目錄為dist_channel1 outputRoot: 'dist_' + channelInfo.channel, ... //講config/channel1/project.config.json檔案拷貝到dist_channel1下 copy: { patterns: [ { from: 'config/' + channelInfo.channel + '/project.config.json', to: 'dist_' + channelInfo.channel + '/project.config.json' } ], ... } ...}
執行Taro的小程式編譯命令後,將會生成該分身對應的小程式程式碼資料夾dist_channel1,直接使用小程式開發者工具開啟該目錄,就可以進行channel1小程式的預覽了。
通過這些配置,我們就可以通過同一套程式碼,生成多個不同的小程式啦!當然,這些小程式的內容是完全一樣的,頂多就是project.config.json中配置的名字、appid有不同而已。
那麼下面,我們就開始看看如何實現生成多個有差異化的小程式。
在具體實現之前,我們需要知道Taro兩個重要的配置:
影分身之樣式分身
首先,我們來看看最常見的一種需求,那就是不同小程式之間,樣式上的差別。我們先來看兩張圖。
小程式A:
小程式B:
在樣式上,這兩個小程式目前的區別有:
主色調不同
對應圖片資源不同
排列樣式不同
建立分身目錄
第一步,在src下為每個分身小程式建立一個目錄,名字最好與channel.js中的配置一樣,如下圖:
放置樣式差異
以之前的“小程式A”來舉例:
其中assets資料夾就是該小程式的資原始檔,即各種藍色的圖示。
app.less為全域性的樣式檔案,內容如下:
@main_color: #1296db;.main_color_txt { color: @main_color}
ChannelStyle.ts檔案則為可能在程式碼中需要用到的樣式:
const ChannelStyle = { mainColor: '#1296db'}export default ChannelStyle
配置別名
在放置好各類樣式差異後,就可以進行全域性變數和別名的配置了,在專案的config下的index.js中做如下配置:
const config = { ... alias: { '@/channel': path.resolve(__dirname, '..', 'src/channel/' + channelInfo.channel), '@/assets': path.resolve(__dirname, '..', 'src/channel/' + channelInfo.channel + '/assets'), '@/app_style': path.resolve(__dirname, '..', 'src/channel/' + channelInfo.channel + '/app.less'), } ...}
這樣,在程式碼中就可以通過別名進行引用了:
//程式碼中需要用到ChannelStyle中的樣式import ChannelStyle from '@/channel/ChannelStyle'//app.tsx入口檔案引用全域性樣式import '@/app_style'//引用資源圖片\u0026lt;Image src={require('@/assets/icon.png')} /\u0026gt;
另外請注意,由於目前Taro還未在.less等樣式檔案中支援別名,所以無法通過類似@import ‘@/app_style’的方式進行引用,所以目前需要在每個分身包下放置全量的差異樣式。
配置全域性變數
由於對於TabBar的配置,是純字串的形式,無法通過別名配置,所以需要使用另一種配置方式,也就是全域性變數,在index.js的配置方式如下:
const config = { defineConstants: { ASSETS_PATH: 'channel/'+channelInfo.channel+'/assets' }}
但是主色調每個分身都不一樣,所以需要在分身的配置檔案中配置,就是基礎配置中,分身資料夾下的config.js,在其中加入全域性變數的配置:
module.exports = { ... defineConstants: { MAIN_COLOR: '#1296db' }, ...}
全域性變數在程式碼中可以直接使用,如app.tsx中TabBar的配置:
config: Config = { ... tabBar: { ... selectedColor: MAIN_COLOR, list: [ { pagePath: 'pages/index/index', text: '首頁', iconPath: ASSETS_PATH + '/home_u.png', selectedIconPath: ASSETS_PATH + '/home_s.png' }, ... ] } }
配置合併
在配置完成之後,在index.js檔案最後的合併程式碼中,加上我們定義的分身配置:
module.exports = function (merge) { ... //預設的原始程式碼為return merge({}, config, envConfig) return merge({}, config, envConfig, require('./' + channelInfo.channel + \u0026quot;/config\u0026quot;))}
樣式分身小結
如此,根據“小程式B”的資原始檔和主題色配置之後,通過修改channel.js中的編譯分身名,就可以生成這兩個小程式了。
我們可能還發現,“小程式A”和“小程式B”的樣式差異,除了資源圖片和主題色之外,“開發”頁面的佈局方式也有差異,這該怎麼處理呢?沒錯,還是通過別名指定less檔案的方式,為各頁面指定對應的樣式檔案。
如果說在實際業務中,不同的小程式存在明顯的主題樣式風格差異的話,建議可以建立主題包,然後為不同的小程式分身配置不同的主題包,如:
//分身配置module.exports = { ... alias: { '@/theme': path.resolve(__dirname, '..', '../src/theme/theme1'), ... } ...}//檔案引用import '@/theme/dev.less'
影分身之功能分身
除了樣式差異之外,有定製化屬性的小程式一定也會存在一定的功能性差異。
細心的小夥伴可能發現了,“小程式A”和“小程式B”開發頁面的條目數是不一樣的。
“小程式A”並沒有FireWall這一項,而且,這兩個小程式的前兩個條目Java和JSX的順序是不一樣的。不僅如此,如果執行小程式,點選各項的話你會發現,點選C++這一項,“小程式B”是跳轉到條目詳情頁面,而“小程式A”則是跳轉到“管理”Tab頁。
類似這種功能性的差異,我們該如何處理呢?
定義頁面配置
我所想到的思路是,給具有差異化的頁面,提供差異化的配置項,然後通過合併的方式,合併具有差異的分身配置。
我們先來看定義完成後的配置目錄,該目錄在src下:
以“開發“頁面為例,在DevConfig.ts中,我定義瞭如下的配置:
import Taro from \u0026quot;@tarojs/taro\u0026quot;;//頁面配置export default { dev: { items: {//條目 item1: {//條目1 img: require('@/assets/jsx.png'),//圖片 txt: 'JSX',//文字 onItemClick: () =\u0026gt; {//點選跳轉事件 toPage('JSX', require('@/assets/jsx.png')) } }, item2: {...}, ... } }}//頁面跳轉function toPage(title, img){ Taro.navigateTo({url: '/pages/dev/DevInfo?title='+title+'\u0026amp;img='+img})}
定義差異合併
同時,diff包下的ChannelConfigDiff.ts檔案,作為差異配置檔案,其內容如下:
export default (config, merge)=\u0026gt;{ return merge([{}, config])}
可以看出,這其實就是把傳入的config原封不動的返回了,因為對於專案主體來說,config是不需要改變的,具體的用途,會在下面說明。
而MultiChannelConfig.ts則為最終的各頁面配置,內容如下:
import merge from 'deepmerge'import ChannelConfigDiff from '@/diff/ChannelConfigDiff'//開發頁面配置import DevConfig from './pages/DevConfig'//合併基本頁面配置const baseConfig = Object.assign({}, DevConfig)//合併差異頁面配置const config = ChannelConfigDiff(baseConfig, merge.all)//開發頁面最終配置export const devConfig = config.dev
定義差異配置
在上面的定義中,我們發現ChannelConfigDiff是根據別名引用的,現在大家應該明白ChannelConfigDiff.ts檔案的作用了吧?沒錯,就是通過在各分身中加入這個檔案,並編寫配置。
以“小程式A”為例,diff目錄如下:
在channel2的ChannelConfigDiff.ts中,只需要配置具體的差異項即可,未配置的則採用預設的配置:
const dev = { dev: { items: { item1: {//定義第一個item為java內容 img: require('@/assets/java.png'), txt: 'Java', onItemClick: () =\u0026gt; { toPage('Java', require('@/assets/java.png')) } }, item2: {...},//第二個item為jsx內容 item5: null,//第五個item(FireWall)為空 item8: { onItemClick: () =\u0026gt; {//最後一個item(C++)點選後跳轉TAB Taro.switchTab({url: '/pages/index/Manage'}) } } } }}//將dev配置合併到原始整體配置export default (config, merge) =\u0026gt; { return merge([{}, config, dev])}
可以看到,該配置中,將item1(原jsx)和item2(原java)的內容對調,將item5(原FireWall)置空,將item8(原C++)點選事件改變。通過這些配置,以達到實現“小程式A”中的功能差異。
最後,別忘了別名的定義,在index.js中,別名配置為:
'@/diff': path.resolve(__dirname, '..', 'src/config/diff'),
在channel2的config.js中,別名配置為:
'@/diff': path.resolve(__dirname, '..', '../src/channel/channel2/diff'),
功能分身小結
如果有了其他的頁面差異的話,通過類似的增加配置,來進行差異化處理,檔案的目錄格式並無要求,只需要保證配置檔名一致、別名配置正確就可以了。
這時,編譯過後,生成的“小程式A”就擁有樣式和功能差異化的“開發”頁面了。
通過這種方式進行差異化配置,就要求對業務有較好的理解和對元件的合理拆分,並且定義出合理的配置項。
影分身之大差異分身
即便使用了樣式分身和功能分身,依然可能出現一些巨大差異的定製化需求,這些巨大的差異導致樣式分身和功能分身的配置成本過大,那這種情況下,該如何是好呢?
如果真的出現這種情況,那也只好斷臂求生了 —— 那就是整體頁面的替換。
我們來看看“小程式A”和“小程式B”的“管理頁面”:
小程式A:
小程式B:
編寫新頁面
我們假設“小程式B”的“管理”頁很難通過配置的方式去做差異化,那麼這時,我們只有專門寫一個新頁面,目錄如下:
其中pages下的就是專屬於channel3的頁面。
頁面替換
替換頁面的方式,其實也是通過全域性變數。
index.js:
defineConstants: { PAGE_MANAGE: 'pages/index/Manage',}
channel3的config.js:
defineConstants: { PAGE_MANAGE: 'channel/channel3/pages/index/Manage'},
app.tsx的頁面配置:
config: Config = { pages: [ ... PAGE_MANAGE ], ... tabBar: { ... list: [ ... { pagePath: PAGE_MANAGE, ... } ] } }
如此,編譯後,channel3生成“小程式B”的“管理”頁面,就是channel3獨有的頁面了。
總結
本文所提供的,只是我能夠想到的一種解決“多個核心功能類似的小程式需要維護多套程式碼”這種窘境的方法,如果有更好的方法,希望各位能夠告訴我,非常感謝。
由於本人只是一個剛接觸前端不久的安卓開發,還有許多需要學習的地方,如果文中有誤,歡迎指正批評。
具體的程式碼可以到Github查閱,也歡迎各位Star和提Issue。
最後,再次貼一下Github地址:https://github.com/BakerJQ/WeAppBunXin
更多內容,請關注前端之巔。
會議推薦
2019年6月,GMTC全球大前端技術大會2019即將到來。小程式、Flutter、移動AI、工程化、效能優化…大前端的下一站在哪裡?點選下圖瞭解更多詳情。
相關文章
- android 一套程式碼多用 以及 多套程式碼用於一個專案Android
- 如何使用 sketch 智慧化一鍵生成多埠程式碼
- 微信雲託管如何實現一套程式碼對應多個環境
- 小程式開發之影分身術
- GO程式碼生成程式碼小思小試Go
- 小程式引入多個e-charts圖表
- 小程式 setData 學問多
- uni-app 1.4 釋出,一套程式碼,發行小程式(微信/支付寶/百度)、H5、App多個平臺APPH5
- 多執行緒程式是如何執行程式碼的?執行緒行程
- 微信小程式實戰影片教程附原始碼課件與多個微信小程式原始碼 14課微信小程式原始碼
- 小程式創業:小程式到底有多少個流量入口,真的有40多個嗎?創業
- 微信小程式如何突破模板訊息限制,獲取多個formId?微信小程式ORM
- 微信小程式-測試遊戲生成六邊多邊形微信小程式遊戲
- Netty ServerBootstrap 繫結多個埠(程式碼示例)NettyServerboot
- python多程式簡介,和VNPY多程式引數優化程式碼分析Python優化
- 直播小程式原始碼,小程式生成二維碼 (相容H5、微信小程式)原始碼H5微信小程式
- Tp生成小程式二維碼
- uni-app高分開源電影專案原始碼案例分析,支援一套程式碼釋出小程式、APP平臺多個平臺(前端入門必看)APP原始碼前端
- .NET生成小程式碼,併合自定義背景圖生成推廣小程式二維碼
- 支援多種小程式!阿里雲ARMS推出小程式監控阿里
- 一套程式碼小程式&Web&Native執行的探索(2)Web
- 程式碼差別不大的多專案如何管理?
- 同一頁面生成多個驗證碼
- 教你如何無程式碼整合連線多個不同型別資料庫型別資料庫
- Python——程式、執行緒、協程、多程式、多執行緒(個人向)Python執行緒
- 基於多資料來源零程式碼同時生成多個資料庫CRUD增刪改查RESTful API介面資料庫RESTAPI
- 一套vue+webpack下,如何開發多個專案VueWeb
- 《程式碼大全》程式碼生成
- 多執行緒,多程式執行緒
- 不寫程式碼也可以處理一對多的關係,一鍵生成前後端程式碼後端
- .NET MAUI 6 正式 GA:一個程式碼庫,多個平臺UI
- 如何在多個應用程式中共享日誌配置
- 機器配音微信小程式原始碼 多種語音任微信小程式原始碼
- [玩具程式碼]swoole多程式匯出大量資料
- 如何減小微信小程式程式碼包大小微信小程式
- 小程式如何實現多程式?從隔離角度出發,看完你就會懂!
- 程式多開
- Python多程式Python