解決taro小程式中引入axios包過大的問題

十一點我就睡發表於2021-12-13

背景

我們在使用taro 和 @freud/http(公司內部專案,基於axios做的二次開發) 的時候,發現構建產物中多了很多沒有用的包,導致產物變大了150kb左右。
經過一番搜尋,發現是因為taro小程式不能解析package.json中的browser module等欄位,而@frued/http 因為要同時支援web和小程式環境,而axios中就有browser屬性:

  "browser": {
    "./lib/adapters/http.js": "./lib/adapters/xhr.js"
  },

所以在taro中引入axios的時候,會將 lib/adapters/http.js也打包進來,http.js中會有很多依賴包如zlib等等,就會導致上述包過大的問題

解決思路

首先我們可以定位到axios原始碼相關的位置:

  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }

正常情況下,在web環境中,./lib/adapters/http.js會被 ./lib/adapters/xhr.js替換
所以以上程式碼在打包之後就會變成。

  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/xhr');
  }

我們在知道了正常情況下,構建產物應該長什麼樣子之後,再來看我們目前的構建產物。
因為@frued/http 是基於rollup做的構建,所以產物如下圖:
index.js中保留對axios的引用 var axios = require('axios'),而axios的對應lib/default.js中,依然在引用 adapter/http
圖一
Jgh5vobkfj.png
圖二7239D0F0-9C7C-47BA-A688-A390371FEFB6.png

至此,我們大致有了解決問題的2個方向:

  1. 構建工具的調整優化
  2. 修改原始碼,避免多餘引用

具體方案

因此對應的解決方案有:

  1. 將axios上傳到私有庫中,刪除掉對http.js的相關引用
  2. 利用npm包的patch機制,在@freud/http 中對axios原始碼進行修改,刪除掉對http.js的相關引用
  3. @freud/http 拆分成2個npm包,分別對應web環境,小程式環境
  4. 改用webpack 對@freud/http進行打包
  5. 引入rollup外掛

1-3 很簡單,不用細講。先講一講第四點,為什麼使用webpack就可以解決這個問題。
rollup中為了更好地做tree shaking,因此只保留著對依賴的引用,而不是直接將依賴的程式碼打包到index.js中(見圖一)。所以@freud/http 引入axios時,axios的程式碼依然保留著對http.js的引用,只有到實際web專案執行時,browser欄位生效,則http.js被xhr.js替換。
但是webpack構建時,會自動解析模組內容,將所有模組打包後的內容和id以key value的形式存在於構建產物的一個陣列中,此時http.js就會被xhr.js替換。
故由於對依賴包的處理方式不同,webpack構建即可解決此問題。
第五點,尚在研究中,有基於rollup做二次封裝的構建工具bili 解決了這個問題。

結尾

因為 @freud/http 為monorepo專案,統一使用rollup構建。若所有包都改用webpack構建,則成本太大。基於此,我選擇了在subpage中新增webpack做二次構建的方式,即在rollup將@freud專案中的子包構建完之後,再執行@freud/http 中的webpack構建命令,將rollup的構建產物lib/index.js 用webpack再構建一次。這樣能夠避免在子專案中再寫一遍babel配置,做到所有子包的基礎構建配置相同。

相關文章