一、前言
京東到家小程式最初只有微信小程式,隨著業務的發展,同樣的功能需要支持容器越來越多,包括支付寶小程式、京東小程式、到家APP、京東APP等,然而每個端分開實作要面臨研發成本高、不一致等問題,
為了提高研發效率,經過技術選型采用了taro3+原生混合開發模式,本文主要講解我們是如何基于taro框架,進行多端能力的探索和性能優化,
二、多端能力的探索
1.到家小程式基于taro3的架構流程圖
框架分層解釋
1.配置層:主要包含編譯配置、路由配置、分包加載、拓展口子,
2.視圖層:主要完成App生命周期初始化、頁面初始化、注入宿主事件、決議配置為頁面和組件系結事件和屬性,
3.組件庫:是一個單獨維護的專案,多端組件庫包括業務組件和原子組件,可由視圖層根據配置動態加載組件,
//渲染主入口
render() {
let { configData, isDefault, isLoading } = this.state;
const pageInfo = { ...this.pageInfoValue, ...this._pageInfo }
return (
<MyContext.Provider value=https://www.cnblogs.com/Jcloud/p/{pageInfo}>
{//動態渲染模板組件
configData &&
configData.map((item, key) => {
return this.renderComponent(item, key);
})
}
{isLoading && }
);
}
//渲染組件 注入下發配置事件和屬性
renderComponent(item, key) {
const AsyncComponent = BussinesComponent[item.templateName];
if (AsyncComponent) {
return (
);
} else {
return null;
}
}
4.邏輯層:包括業務處理邏輯,請求、例外、狀態、性能、公共工具類,以及與基礎庫對接的適配能力,
5.基礎庫: 提供基本能力,定位、登錄、請求、埋點等基礎功能,主要是抹平各端基礎功能的差異,
2、基礎庫
1.統一介面,分端實作,差異內部抹平
關于基礎庫我們采用分端實作的方式,即統一介面的多端檔案,
基礎庫如何對接在專案里,修改config/index.js,結合taro提供的MultiPlatformPlugin插件編譯,
const baseLib = '@dj-lib/login'
//增加別名,便于后續基礎庫調整切換
alias: {
'@djmp': path.resolve(__dirname, "..", `./node_modules/${baseLib}/build`),
},
//修改webpack配置,h5和mini都要修改
webpackChain(chain, webpack) {
chain.resolve.plugin('MultiPlatformPlugin')
.tap(args => {
args[2]["include"] = [`${baseLib}`]
return args
})
}
業務里使用方式
import { goToLogin } from '@djmp/login/index';
goToLogin()
2.高復用
基礎庫不應該耦合框架,那么基礎庫應該如何設計,使其既能滿足taro專案又能滿足原生專案使用呢?
npm基礎庫 在taro經過編譯后生成為 vendors檔案
npm基礎庫 在小程式原生專案npm構建后 生成miniprogram_npm
一樣的基礎庫經過編譯后會存在2種形態,多占了一份空間呢,
我們對小程式包體積大小是比較敏感的,為了節約空間,那么如何讓taro使用小程式的miniprogram_npm呢?
先簡單說一下思路,更改 webpack 的配置項,通過 externals 配置處理公共方法和公共模塊的引入,保留這些引入的陳述句,并將引入方式設定成 commonjs 相對路徑的方式,詳細代碼如下所示,
const config = {
// ...
mini: {
// ...
webpackChain (chain) {
chain.merge({
externals: [
(context, request, callback) => {
const externalDirs = ['@djmp/login']
const externalDir = externalDirs.find(dir => request.startsWith(dir))
if (process.env.NODE_ENV === 'production' && externalDir) {
const res = request.replace(externalDir, `../../../../${externalDir.substr(1)}`)
return callback(null, `commonjs ${res}`)
}
callback()
},
],
})
}
// ...
}
// ...
}
3、組件庫
想要實作跨端組件,難點有三個
第一:如何在多個技術堆疊中找到最恰當的磨平方案,不同的方案會導致 開發適配的成本不同,而人效提升才是我們最終想要實作的目的;
第二:如何在一碼多端實作組件之后,確保沒有對各個組件的性能產生影響
第三:如何在各專案中進行跨端組件的使用
基于以上,在我們已經確定的以Taro為基礎開發框架的前提下,我們進行了整體跨端組件方案實作的規劃設計:
在組件層面,劃分為三層:UI基礎組件和業務組件 為最底層;容器組件是中間層,最上層是業務模板組件;我們首先從UI基礎組件與業務組件入手,進行方案的最終確認;
調研程序中,UI組件和業務組件主要從API、樣式、邏輯三個方面去調研跨端的復用率:
經過以上調研得出結論:API層面仍需要使用各自技術堆疊進行實踐,通過屬性一致的方式進行API層面的磨平;樣式上,基礎都使用Sass語法,通過babel工具在轉化程序中生成各端可識別的樣式形式;邏輯上基本是平移,不需要做改動;所以當我們想做跨端組件時,我們最大作業量在于:API的磨平和樣式的跨端寫法的探索;
例:圖片組件的磨平:
基于以上,跨端組件的復用方案經過調研是可行的,但是接下來,我們該如何保證轉化后的組件能夠和原生組件的性能媲美呢?我們的跨端組件又該如何在各個專案中使用呢?
在這個程序中,我們主要調研對比兩種方案:
第一:直接利用Taro提供的跨端編輯功能進行轉換,轉換編譯成RN . 微信小程式 以及H5;
第二:通過babel進行編譯,直接轉換成RN原生代碼,微信小程式原生代碼,以及H5原生代碼
對比方向 | 原碼大小 | 編譯成本 | 生成的組件性能 |
---|---|---|---|
Taro直接編譯 | 大(攜帶了Taro環境) | 中(Taro直接提供,但需要各端除錯) | 與原生相同 |
通過babel轉義 | 小(只有當前組件的原始碼代碼) | 中(需要開發Babel轉義組件) | 與原生相同 |
經過以上幾組對比,我們最終選用了babel轉義的方式,在專案中使用時,發布到Npm服務器上,供各個專案進行使用,
方案落地與未來規劃:
在確認整體的方案方向之后,我們進行了專案的落地,首先搭建了跨端組件庫的運行專案:能夠支持預覽京東小程式、微信小程式以及H5的組件生成的頁面;以下是整個組件從生成到發布到對應專案的全部流程,
目前已經完成了個5種UI組件的實作,4種業務組件;其中優惠券模塊已經落地在到家小程式專案中,并已經沉淀了跨端組件的設計規則和方案,未來一年中,會繼續跨端組件的實作與落地,從UI、業務層到復雜容器以及復雜頁面中,
4、工程化構建
1.構建微信小程式
因為存在多個taro專案由不同業務負責,需要將taro聚合編譯后的產物,和微信原生聚合在一起,才能構成完整的小程式專案,
下面是設計的構建流程,
為了使其自動化,減少人工操作,在迪迦發布后臺( 到家自研的小程式發布后臺 ) 創建依賴任務即可,完成整體構建并上傳,
其中執行【依賴任務】這個環節會進行,taro專案聚合編譯,并將產物合并到原生專案,
迪迦發布后臺
2.構建京東小程式
yarn deploy:jd 版本號 描述
//集成CI上傳工具 jd-miniprogram-ci
const { upload, preview } = require('jd-miniprogram-ci')
const path = require('path')
const privateKey = 'xxxxx'
//要上傳的目錄-正式
const projectPath = path.resolve(__dirname, '../../', `dist/jddist`)
//要上傳的目錄-本地除錯
const projectPathDev = path.resolve(__dirname, '../../', `dist/jddevdist`)
const version = process.argv[2]
const desc = process.argv[3]
//預覽版
preview({
privateKey: privateKey,
projectPath: projectPathDev,
base64: false,
})
//體驗版
upload({
privateKey: privateKey,
projectPath: projectPath,
uv: version,
desc: desc,
base64: false,
})
3.構建發布h5
yarn deploy:h5
h5的應用通常采用 cdn資源 +html入口 這種模式,先發布cdn資源進行預熱,在發布html入口進行上線,
主要進行3個操作
1.編譯出h5dist產物,即html+靜態資源
2.靜態資源,利用集成 @jd/upload-oss-tools 工具上傳到 cdn,
3.觸發【行云部署編排】發布html檔案入口
關于cdn: 我們集成了cdn上傳工具,輔助快速上線,
//集成 @jd/upload-oss-tools上傳工具
const UploadOssPlugin = require("@jd/upload-oss-tools");
const accessKey = new Buffer.from('xxx', 'base64').toString()
const secretKey = new Buffer.from('xxx', 'base64').toString()
module.exports = function (localFullPath, folder) {
return new Promise((resolve) => {
console.log('localFullPath', localFullPath)
console.log('folder', folder)
// 初始化上傳應用
let _ploadOssPlugin = new UploadOssPlugin({
localFullPath: localFullPath, // 被上傳的本地絕對路徑,自行配置
access: accessKey, // http://oss.jd.com/user/glist 生成的 access key
secret: secretKey, // http://oss.jd.com/user/glist 生成的 secret key
site: "storage.jd.local",
cover: true, // 是否覆寫遠程空間檔案 默認true
printCdnFile: true, // 是否手動重繪cdn檔案 默認false
bucket: "wxconfig", // 空間名字 僅能由小寫字母、數字、點號(.)、中劃線(-)組成
folder: folder, // 空間檔案夾名稱 非必填(1、默認創建當前檔案所在的檔案夾,2、屏蔽欄位或傳undefined則按照localFullPath的路徑一層層創建檔案夾)
ignoreRegexp: "", // 排除的檔案規則,直接寫正則不加雙引號,無規則時空字串,正則字串,匹配到的檔案和檔案夾都會忽略
timeout: "", // 上傳請求超時的毫秒數 單位毫秒,默認30秒
uploadStart: function (files) { }, // 檔案開始上傳回呼函式,回傳檔案串列引數
uploadProgress: function (progress) { }, // 檔案上傳程序回呼函式,回傳檔案上傳進度
uploadEnd: (res) =>{
console.log('上傳完成')
resolve()
},
// 檔案上傳完畢回呼函式,回傳 {上傳檔案陣列、上傳檔案的總數,成功數量,失敗數量,未上傳數量
});
_ploadOssPlugin.upload();
})
}
三、性能優化
性能優化是一個亙古不變的話題,總結來說優化方向:包下載階段、js注入階段、請求階段、渲染階段,
以下主要介紹在下載階段如何優化包體積,請求階段如何提高請求效率,
(一)體積優化
相信使用過taro3的同學,都有個同樣的體會,就是編譯出來的產物過大,主包可能超2M!
1.主包是否開啟
優化主包的體積大小 :optimizeMainPackage,
像下面這樣簡單配置之后,可以避免主包沒有引入的 module 不被提取到commonChunks
中,該功能會在打包時分析 module 和 chunk 的依賴關系,篩選出主包沒有參考到的 module 把它提取到分包內,
module.exports = {
// ...
mini: {
// ...
optimizeMainPackage: {
enable: true,
},
},
}
2.使用壓縮插件 terser-webpack-plugin
//使用壓縮插件
webpackChain(chain, webpack) {
chain.merge({
plugin: {
install: {
plugin: require('terser-webpack-plugin'),
args: [{
terserOptions: {
compress: true, // 默認使用terser壓縮
keep_classnames: true, // 不改變class名稱
keep_fnames: true // 不改變函式名稱
}
}]
}
}
})
}
3.把公共檔案提取到分包,
mini.addChunkPages?:
為某些頁面單獨指定需要參考的公共檔案,
例如在使用小程式分包的時候,為了減少主包大小,分包的頁面希望引入自己的公共檔案,而不希望直接放在主包內,那么我們首先可以通過 webpackChain 配置 來單獨抽離分包的公共檔案,然后通過 mini.addChunkPages
為分包頁面配置引入分包的公共檔案,其使用方式如下:
mini.addChunkPages
配置為一個函式,接受兩個引數
?pages
引數為 Map 型別,用于為頁面添加公共檔案
?pagesNames
引數為當前應用的所有頁面標識串列,可以通過列印的方式進行查看頁面的標識
例如,為 pages/index/index
頁面添加 eating
和 morning
兩個抽離的公共檔案:
mini: {
// ...
addChunkPages(pages: Map<string, string[]>, pagesNames: string[]) {
pages.set('pages/index/index', ['eating', 'morning'])
},
},
4.代碼分析
如果以上方式,還達不到我們想要的效果,那么我們只能靜下心來分析下taro的打包邏輯,
可以執行 npm run dev 模式查看產物里的 ``xxx
.LICENSE.txt檔案, 里面羅列打包了哪些檔案,需要自行分析去除冗余,
以下以vendors.LICENSE.txt 為例
?runtime.js
: webpack 運行時入口 ,只有2k,沒有優化空間,
?taro.js
: node_modules 中 Taro 相關依賴,112k,可以魔改原始碼,否則沒有優化空間,
?vendors.js
: node_modules 除 Taro 外的公共依賴,查看vendors.js.LICENSE.txt檔案分析包括哪些檔案
?common.js
: 專案中業務代碼公共邏輯,查看common
.js.LICENSE.txt檔案分析包括哪些檔案
?app.js app生命周期中依賴的檔案,查看app .js.LICENSE.txt檔案分析包括哪些檔案
?app.wxss 公共樣式檔案 ,看業務需求優化,去除非必要的全域樣式,
?base.wxml 取決于使用組件的方式,可優化空間較小,
(二)網路請求優化:
相信大家的業務里有多種型別的請求,業務類、埋點類、行為分析、監控、其他sdk封裝的請求,然而在不同的宿主環境有不同的并發限制,比如,微信小程式請求并發限制 10個,京東等小程式限制為5個,
如下圖,以微信小程式為例,在請求過多時,業務與埋點類的請求爭搶請求資源,造成業務請求排隊,導致頁面展示滯后,弱網情況甚至造成卡頓,
那么基于以上問題,如何平衡業務請求和非業務請求呢?
這里我們有2個方案:
1.動態調度方案 https://www.cnblogs.com/rsapaper/p/15047813.html
思路就行將請求分為高優和低優請求,當發生阻塞時,將高優請求放入請求佇列,低優進入等待佇列,
請求分發器 QueueRequest:對新的請求進行分發,
?加入等待佇列:正在進行的請求數超過設定的 threshold,且請求為低優先級時;
?加入請求池:請求為高優先級,或并發數未達到 threshold,
等待佇列 WaitingQueue:維護需要延時發送的請求等待佇列,在請求池空閑或請求超過最長等待時間時,補發等待請求,
請求池 RequestPool:發送請求并維護所有正在進行的請求的狀態,對外暴露正在進行的請求數量,并在有請求完成時通知等待佇列嘗試補發,
2.虛擬請求池方案
該思路是將微信的10個請求資源,分成3個請求池,業務請求:埋點類:其他請求的比例為6:2:2,比例可以自行調整,
這樣各型別請求都在自己的請求池,不存在爭搶其他請求池資源,保障了業務不被其他請求阻塞,
實作方式
方案對比
優缺點 | 動態調度(方案一) | 虛擬請求池(方案二) |
---|---|---|
拓展性 | 低 | 高 |
成本(開發、測驗、維護) | 高 | 低 |
請求效率 | 低 | 高 |
2個方案都可以完成請求資源的分配,但結合業務實際采用的是虛擬請求方案,經測驗在弱網情況下,請求效率可以提升15%.
四、總結和展望
未來一定是一碼多端的方向,所以我們未來在基礎建設上會投入更多的精力,包括框架層升級優化、基礎庫建設、組件庫建設、工程化建設快速部署多端,
在性能優化上我們還可以探索的方向有京東小程式分包預加載、分包異步化、京東容器flutter渲染、騰訊skyLine渲染引擎等,
在團隊溝通協作上會與Taro團隊、京東小程式容器團隊、nut-ui、拼拼等團隊進行學習溝通, 也希望能與大家合作共建,
五、結束語
京東小程式開放平臺是京東自研平臺,提供豐富的開放能力和底層的引擎支持,目前有開發者工具、轉化工具、可視化拖拽等多種開發工具可供內部研發同事使用,提升開發質量同時快速實作業務功能的上線,內部已有京東支付、京東讀書、京東居家等業務使用京東小程式作為技術框架開展其業務,
如您想深入了解和體驗京東小程式,可前往京東小程式官網(https://mp.jd.com/?entrance=shendeng)查看更多資訊,
參考:
https://www.cnblogs.com/rsapaper/p/15047813.html
https://taro-docs.jd.com/docs/next/config-detail#minioptimizemainpackage
https://taro-docs.jd.com/docs/next/dynamic-import
https://zhuanlan.zhihu.com/p/396763942
作者:京東零售 鄧樹海、姜微
來源:京東云開發者社區
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556342.html
標籤:Html/Css
上一篇:初入前端-HTML
下一篇:返回列表