背景

我们在使用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
图一

图二

至此,我们大致有了解决问题的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配置,做到所有子包的基础构建配置相同。

03-05 15:48