Skip to content

打包通识

基本打包逻辑

基本分两组,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 WaltonECMAScript 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 an import declaration, or a require() 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;
})