打包通识
基本打包逻辑
基本分两组,JavaScript 库与工程化项目。
JavaScript库
单包目的比较明确,基本是:
- 自定义处理文件:比如给每个文件加头注
- babel
- 配置打包工具打包文件
- Loader
- Plugins
- Treeshaking
- 输出不同格式
如果是多包,即开发目录下包含多个目录,每个目录都想输出为一个依赖包。还需:
- 自定义生成多入口 entry
- external 对 peer 开发包的依赖
打包成 xx 格式:ship a variety of different module formats/ ship your library as..
打包工具
webpack: 顾名思义,由于 web,所以它的打包多了很多对 web 环境下的支持。比如 splitChunks, asset management, dev-server, hmr
Rollup: 转换到某种 standard format + treeShaking
TreeShaking
prerequisites:
- 导出和导入依赖包的形式最好是 es/esm(目前,webpack v5 也在一定程度上支持 cjs 的 treeshaking.)
- 依赖包 pkg.json 指定 module 字段作为 esm 入口
why: es/esm are static in their structure(但是像 class 为一个整体,并不能 shake 掉内部未用的方法)
WhenInUse:
- 避免导入依赖包的 esm 被 babel 重构,比如通过
["@babel/env", { "modules": false }]
来保有原格式
sideEffects
-
情况一:
export const bar = mayHaveSideEffect("Hello");
,webpack 可以使用 /#PURE/ comment -
情况二:
Import "./button.css"
Pkg.json: "sideEffects" flag
babel 到什么版本?
可参考 Deploying ES2015+ Code in Production Today — Philip Walton 和 ECMAScript modules in browsers - JakeArchibald.com
极端想优化的情况下,已经可以在大部分现代浏览器中使用 es2015+ 和 es module 了
legacy 的 tagret: ES5 的配置
{
resolve: {
target: ["web", "es5"]
},
module: {
rules: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/, // 视情况
use: {
loader: 'babel-loader',
options: {
cacheDirectory: false,
presets: [
'@babel/preset-env', // 不指定 target 或 browserlist 的话,默认到 es5
'@babel/preset-react'
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-async-to-generator',
'@babel/plugin-transform-runtime', // 或自导入 corejs
],
},
},
}],
},
}
总而言之,产物 = 打包工具的 runtime + 被转化的 code + 被转化 code 的 runtime。这三者都要 target ES5 才行。
关于 runtime:
For instance,
@babel/plugin-transform-runtime
relies on the type of the current document to decide whether to insert animport
declaration, or arequire()
call.@babel/preset-env
also does the same for its"useBuiltIns"
option.
兼容多种模块规范
;(function(name, definition){
//检测环境是否为AMD/CMD
var hasDefine = typeof define === 'function',
hasExports = typeof module !== 'undefined' && module.exports;
if(hasDefine){ // AMD
define(definition);
} else if (hasExports){ // commonjs
moudle.exports = definition();
} else {
this[name] = definition(); // UMD
}
})('hello', function(){
var hello = function(){};
return hello;
})