Webpack
一站式解决了所有资源的依赖问题
why
传统 script 引入缺点
- 全局污染
- 顺序难保证
文件过大:在大型 SPA 里面,如果全部打包成一个文件会导致文件过大,首屏加载速度受影响(code splitting)
原理
90行代码实现模块打包器 - SegmentFault 思否
BetaSu/minipack: 📦 现代JS模块打包器的简易实现、详细教学
- 输入 entry.js 输出自执行函数
IIFE(modules)
- 使用 babel 解析 AST 并记录
import
依赖,有序队列(如何收集依赖?) - 使用 babel 转换成 cjs
- 写入 html
文件,文件依赖,生成依赖图 DAG,打包 bundle
工具要解决的三个问题
1. 处理静态资源如 CSS/images
2. 打包分拆
3. 异步加载
流程
流程 code input -> loaders -> plugins -> output
loaders 相当于其它工具中的 tasks
entry 从哪开始打包
output 输出到哪
loader 如何转换资源
plugin 如何打包
resolve 如何解析包
hash 策略
webpack-文件指纹策略:chunkhash、contenthash 和 hash | 个人博客
HMR
原子模块替换,避免整页刷新
Webpack HMR runtime负责监视文件变化、与Webpack建立连接、接收更新的模块信息,并通过热替换机制将这些更新应用到运行中的应用程序中,从而实现在开发过程中的即时反馈和模块热更新。
HMR 原理 https://stackoverflow.com/a/24587740/5657916
loader
- file-loader 将资源复制过去,然后返回资源路径,一般是图片文件,那别的格式如 mp3 文件怎么办?加到正则即可
- url-loader 小于多少字节时,转换为 base64 内联,支持回退到 file-loader,场景如 icon
plugin
cache-loader 读写磁盘开销、副作用,仓库已废弃、建议升 v5,默认开启
尽量用 include 显示指定,按需添加
webpack5新特性一览 · Issue #48 · HolyZheng/holyZheng-blog
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}
use 下面的先执行,使用了 compose
add filelist plugin · cyio/learn-webpack@4bb4ba2
loader 与 plugin 区别
工作阶段不一样,先后关系 loader 是文件级处理,打包前期 plugin 是 bundle/chunk 级处理,打包末尾
职责 loader 转换器,预处理源码 plugin 处理 loader 处理不了的任何事
打包
- 自带分析
webpack --profile --json > stats.json
vendor 第三方库 common 共用
With this bundle configuration, you would load your third party libraries, then your common application code, then your page-specific application code.
How to Reduce Your Vue.JS Bundle Size With Webpack
思路:应用打成一个 JS 文件,包体太大,所以需要拆分,拆分原则,第三方、共用、页面
分包 - 提取公共模块
why:
- 分离业务代码和第三方库( vendor )
- 按需加载(利用 import() 语法)
4.x 内置 SplitChunks
chunks 类别?async
默认规则
chunks: all, async, and initial 指定哪类包可以优化,initial 同步,all 可用于同步和异步
观察下 vendors hash 是否变化,应该保持不变
样式如何拆包 scss,官方插件仅把 css 从 js 中提取,多页存在冗余
- 按上面链接处理不行,打成了 scoped 样式,文件更大了
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: { // 拆取规则
vendors: {
test: /[\\/]node_modules[\\/]/, // node_modules 下引入的包归类为 vendors
priority: -10 // 优先级
},
default: {
minChunks: 2, // 默认被引用 2 次及以上时拆包
priority: -20,
reuseExistingChunk: true
}
}
}
构建性能
官方文档针对生产和开发环境有分别叙述
开发环境
- 是否是增量编译
- 是否利用了内存
渐近升级到 rust 实现的工具:
- babel + Terser -> swc
- js -> esbuild
- webpack -> rollup
https://nextjs.org/blog/next-11-1#adopting-rust-based-swc
方法论:
- 多线程 thread-loader 请仅在耗时的 loader 上使用
- 缓存 构建
- 移除无用代码 tree-shaking 移除未引用代码,依赖 ES6 模块语法
- 并行压缩
loader、plugin 耗时分析 SpeedMeasurePlugin 探索webpack构建速度提升方法和优化策略
noParse
- 含有 process.env.NODE_ENV 的文件也不要让 webpack noParse。
webpack-cdn-plugin
异步 import 默认会单独打包
chunks 含义是什么
SplitChunksPlugin | webpackvue.js - VueJs + Webpack lazyload modules from ElementUI - Stack Overflow
runtime
The Single Runtime Chunk > Webpack Encore: Frontend like a Pro! | SymfonyCasts
按需加载
import { xx } from yy
这么写,理论上会全局引入 yy
tree-shaking 依赖 ES6 导入导出以精确分析,但有些老库是 ES5 写的
用 babel 转换插件,写法不变,插件进行替换
lodash 支持按方法手动按需引入,但导入比较多时,要写很多行,繁琐
antd 默认对 JS 部分,用 babel plugin 转换成按需引入写法。如果全局引入,会在控制台给出警告。
打包分析
chainWebpack: config => {
if (process.env.npm_config_report) {
config
.plugin('webpack-bundle-analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin);
}
"analyz": "npm_config_report=true npm run build",
Compiler 与 compilation
引擎与一次编译
The Compiler module of webpack is the main engine that creates a compilation instance with all the options passed through webpack CLI or webpack api or webpack configuration file.
参考
petehunt/webpack-howtoWebpack slides详解前端模块化工具-Webpack | 淡忘~浅思Webpack configuration for Multi Page Application. All you need to start your project | Sergey Kryvets Blog
- publicPath 用于发布时文件路径的设置,是绝对路径 本地测试,要开服务,然后把服务地址加上
webpack main.js bundle.js
// webpack.config.js
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
}
}
调用:
webpack
开发环境下编译webpack -p
产品编译及压缩webpack --watch
开发环境下持续的监听文件变动来进行编译(非常快!)webpack -d
引入 source maps
resolve: {
// 现在你require文件的时候可以直接使用require('file'),不用使用require('file.coffee')
extensions: ['', '.js', '.json', '.coffee']
}
https://www.bilibili.com/video/BV1aE411i7gY?p=7&share_source=copy_pc