Open creeperyang opened 1 year ago
output.library
及相关的 commonjs | commonjs2
等差别一个问题,commonjs2
和 commonjs
有什么差别?
commonjs
是最初版本CommonJS规范,只定义了 exports
,所以编译产出一般是 exports['name'] = xxx
;commonjs2
是目前广泛使用的模块定义系统,编译产出是 module.exports = xxx
(cjs2 支持 default export 和 named export)。有如下 webpack 配置来生成 umd 格式的代码:
module.exports = {
//...
output: {
library: {
name: 'MyLibrary',
type: 'umd',
},
},
};
编译产出:
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if (typeof define === 'function' && define.amd) define([], factory);
else if (typeof exports === 'object') exports['MyLibrary'] = factory();
else root['MyLibrary'] = factory();
})(global, function () {
return _entry_return_;
});
已收到,稍候会处理best wish~
从功能核心来说,webpack 是JS应用的打包工具(static module bundler)。webpack 会从入口(entry point)开始处理你的应用,构建依赖图,把多个模块(module)合并到一个或多个包(bundle)。
webpack 本身只能处理 JS/JSON 文件,它依赖各种 loader、plugin 来共同完成对复杂应用的支持。
(一)核心概念
loader vs plugin
loader 就是转换模块代码的工具函数,允许你预处理你要加载的文件。
plugin 是 webpack 的基石,而 webpack 也是基于同样的插件系统打造。plugin 本质是注册webpack的生命周期事件来做到 loader 做不到的事。
loader 顺序
loader 是洋葱型顺序,pitch 从左到右,loader 本身从右到左执行。
module vs chunk
模块化编程中,完整的程序可以被分成完成特定功能的模块。在 webpack 里,一个文件就是一个模块。
chunk 有两种形式:
SplitChunksPlugin
时产生。(二)优化构建效率
总的来说,想要提高打包/构建效率,要么是减少打包工作量,要么是提高打包速度。相关措施可以总结为:
持久化缓存,提高二次构建性能
如果不想变更一个模块导致打包过程(可能几百个模块)重复一遍,那么我们需要利用缓存持久化来避免不必要的工作。
配置缓存很简单,配置
cache
即可:cache.type
:默认为memory
,这在watch
模式下很有用,但是如果想持久化,方便中断后下次打包使用,可以设置为filesystem
;cache.cacheDirectory
:缓存文件存放的路径,默认为node_modules/.cache/webpack
;cache.maxAge
:缓存失效时间,默认为5184000000
;cache.buildDependencies
:额外的依赖文件,当这些文件内容变化时,缓存会完全失效而执行完整的编译构建,通常可设置为项目配置文件。配置完缓存后,测试的两次编译时间为
2047ms
和417 ms
,效果显著。关于 dll
dll 动态链接库本质也是缓存,即不经常改变的代码抽取成一个共享的库,然后直接使用。
通常:
[name].manifest.json
;由于从 webpack@4 开始,webpack 打包性能已经足够好,dll 模式被弃用。
减少编译查找路径、编译范围(减少查找时间,减少需要编译的文件)
除了缓存,还可以缩减编译查找步骤、范围来减小工作量。
1. Rule 的 exclude/include/issuer 等多种方式减少查找范围
2. noParse 跳过编译
使用 noParse 让 webpack 不要去解析特地文件,对忽略一些大型类库,可以节省很多时间。
3. 配置 resolve 减少查找范围
尽量减少webpack的查找范围。
提升编译性能(通过跳过不必要的编译步骤等)
1. 开发阶段禁止产物优化
usedExports: false
)除了压缩强烈建议开发阶段关闭,其它几项看个人需要。
2. 合适的 sourcemap 配置
eval/eval-source-map/(none)
;source-map
。3. 减少 watch 文件范围
4.
experiments.lazyCompilation
需要时再编译多线程/多进程处理
利用更高性能的 swc/esbuild 来压缩
两者通过 Rust/Go 提高了性能。
(三)优化
webpack caching (长期缓存)
理解 hash/chunkhash/contenthash
首先了解 webpack 中 hash 相关概念: https://webpack.js.org/configuration/output/#template-strings
hash/fullhash,Compilation-level,本次编译(compilation)的 hash。可以理解为项目级别的,任意改动基本都会导致 hash 变更。
chunkhash,Chunk-level,chunk 的 hash。不同 chunk 之间互不影响。
contenthash,Module-level,模块相关内容的 hash。
cache 第一步:Output FileNames
推荐使用
contenthash
来防止有改动但缓存未失效的问题(用户页面没访问最新内容)。cache 第二步:拆出模板代码(Extracting Boilerplate)
optimization.runtimeChunk
设置为"single"
会为所有生成的 chunk 创建一个共用的 runtime。如果设置为false
则会在每个 chunk 里面嵌入 runtime。以上两步让公共包和runtime可以不因为业务代码变更而缓存失效。
cache 第三步:Module Identifiers
假设这样一个情况:新增一个文件
'./src/print.js'
并被'./src/index.js'
(main)作为依赖引入使用。重新编译:module.id
was changed.我们发现 main/vendors/runtime 文件名(hash)都变了。理论上 vendors 应该不变。
引入
optimization.moduleIds="deterministic"
可以解决这类问题:natural
:基于使用顺序的数字 id。模块增减会导致id变更。deterministic
:模块名hash得出的数字id(默认3位)。跟顺序无关,解决了模块增减导致其它chunk的module的id(natural)也变化的问题。另外
optimization.chunkIds="deterministic"
也是 production 模式默认的。Concatenate Module(Scope Hoisting)
optimization.concatenateModules允许webpack去查找可以安全串联的模块来串联/合并到一个模块。
webpack之前会把每个module都放入单独的wrapper function,但这会拖慢执行速度。concatenateModules 可以像 RollupJS 之类尽可能把module安全合并,合并到同一个闭包下面。
对 Concatenate Module 而言: