首都青年網 |
      • 手機客戶端
      • 微信
      您的位置:首頁 > 社會 > 正文
      京東到家小程序-在性能及多端能力的探索實踐|世界要聞
      來源:京東云開發者 2023-06-30 06:54:56

      一、前言

      京東到家小程序最初只有微信小程序,隨著業務的發展,同樣的功能需要支持容器越來越多,包括支付寶小程序、京東小程序、到家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={pageInfo}>                <View                    className={style.bg}                >                     {//動態渲染模板組件                        configData &&                        configData.map((item, key) => {                            return this.renderComponent(item, key);                        })                    }                </View>                {isLoading && <Loading></Loading>}            </MyContext.Provider>        );    }     //渲染組件 注入下發配置事件和屬性     renderComponent(item, key) {        const AsyncComponent = BussinesComponent[item.templateName];        if (AsyncComponent) {            return (                <AsyncComponent                    key={key}                    dataSource={item.data}                    {...item.config}                    pageEvent={pageEvent}                ></AsyncComponent>            );        } 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轉義

      ?。ㄖ挥挟斍敖M件的源碼代碼)

      中(需要開發Babel轉義組件)

      與原生相同

      經過以上幾組對比,我們最終選用了babel轉義的方式。在項目中使用時,發布到Npm服務器上,供各個項目進行使用。

      方案落地與未來規劃:

      在確認整體的方案方向之后,我們進行了項目的落地,首先搭建了跨端組件庫的運行項目:能夠支持預覽京東小程序、微信小程序以及H5的組件生成的頁面;以下是整個組件從生成到發布到對應項目的全部流程。

      ??目前已經完成了個5種UI組件的實現,4種業務組件;其中優惠券模塊已經落地在到家小程序項目中,并已經沉淀了跨端組件的設計規則和方案。未來一年中,會繼續跨端組件的實現與落地,從UI、業務層到復雜容器以及復雜頁面中。

      4、工程化構建

      1.構建微信小程序

      因為存在多個taro項目由不同業務負責,需要將taro聚合編譯后的產物,和微信原生聚合在一起,才能構成完整的小程序項目。

      下面是設計的構建流程。

      ??
      為了使其自動化,減少人工操作,在迪迦發布后臺(到家自研的小程序發布后臺創建依賴任務即可,完成整體構建并上傳。

      ??
      其中執行【依賴任務】這個環節會進行,taro項目聚合編譯,并將產物合并到原生項目。

      ??迪迦發布后臺


      2.構建京東小程序

      yarn deploy:jd 版本號 描述

      //集成CI上傳工具 jd-miniprogram-ciconst { upload, preview } = require("jd-miniprogram-ci")const path = require("path")const privateKey = "xxxxx"http://要上傳的目錄-正式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://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

      關鍵詞

      圖片新聞
      最近更新
      Copyright @ 2008-2023 www.npbk4zh.cn All Rights Reserved 首都青年網 版權所有
      文章采集互聯網,為了傳遞信息,如有出處與本站無關。 非本站原創,系由網友自助上傳或轉載、采編于其它媒體,不代表本站的觀點和和看法,一切責任由發布者承擔,與本站無關!
      版權文章處理
      聯系方式:QQ  39 60 29 14 2 @qq.com  備案號:皖ICP備2022009963號-20