文章目录
- 前言
- 一、核心概念
- 1.1 Entry(入口)
- 1.2 Output(输出)
- 1.3 完整的构建流程
- 二、Loader
- 2.1 定义
- 2.2 常用 Loader
- 2.3 Loader 执行顺序
- 三、Plugin
- 3.1 定义
- 3.2 常用 Plugin
- 3.3 Loader vs Plugin
- 四、devServer 与 HMR
- 4.1 devServer
- 4.2 HMR(Hot Module Replacement)
- 五、contenthash 缓存策略
- 5.1 三种 hash 类型
- 5.2 为什么用 contenthash
- 5.3 最佳配置
- 六、从入口到产出:完整链路
- 七、易混淆点
- 八、思考与练习
前言
Webpack 是前端工程化的核心工具,理解其工作原理对构建优化、性能调优至关重要。本篇会讲清楚:
- Entry / Output 配置
- Loader 与 Plugin 的区别与执行顺序
- devServer 与 HMR(热模块替换)
contenthash缓存策略
一、核心概念
1.1 Entry(入口)
// webpack.config.jsmodule.exports={// 单入口entry:'./src/index.js',// 多入口(多页应用)entry:{app:'./src/app.js',admin:'./src/admin.js'}}1.2 Output(输出)
module.exports={output:{filename:'[name].[contenthash].js',// 输出文件名path:path.resolve(__dirname,'dist'),// 输出目录clean:true// 构建前清空输出目录}}1.3 完整的构建流程
Entry → 依赖分析 → Loader 转换 → 模块图 → 生成 Chunk → Output二、Loader
2.1 定义
Loader 是模块转换器,将非 JS 文件(CSS、图片、TypeScript 等)转换为 Webpack 可处理的模块。
2.2 常用 Loader
module.exports={module:{rules:[// CSS 处理{test:/\.css$/,use:['style-loader','css-loader']// 从右到左执行},// TypeScript{test:/\.tsx?$/,use:'ts-loader'},// 图片{test:/\.(png|svg|jpg|gif)$/,type:'asset/resource'},// Babel 转译{test:/\.js$/,exclude:/node_modules/,use:{loader:'babel-loader',options:{presets:['@babel/preset-env']}}}]}}2.3 Loader 执行顺序
从右到左,从下到上。
use:['style-loader','css-loader']// 执行顺序:css-loader → style-loader// 1. css-loader:解析 CSS 文件,处理 @import 和 url()// 2. style-loader:将 CSS 注入到 DOM 的 <style> 标签三、Plugin
3.1 定义
Plugin 是构建流程的扩展,可以在构建的各个阶段执行自定义逻辑。
3.2 常用 Plugin
constHtmlWebpackPlugin=require('html-webpack-plugin')constMiniCssExtractPlugin=require('mini-css-extract-plugin')const{BundleAnalyzerPlugin}=require('webpack-bundle-analyzer')module.exports={plugins:[// 生成 HTML 文件newHtmlWebpackPlugin({template:'./src/index.html'}),// 提取 CSS 到单独文件newMiniCssExtractPlugin({filename:'[name].[contenthash].css'}),// 打包分析newBundleAnalyzerPlugin()]}3.3 Loader vs Plugin
| 对比项 | Loader | Plugin |
|---|---|---|
| 作用 | 模块转换 | 构建流程扩展 |
| 时机 | 模块加载时 | 构建生命周期各阶段 |
| 配置 | module.rules | plugins |
| 本质 | 函数 | 类(带 apply 方法) |
// Loader 本质:一个函数module.exports=function(source){returntransformedSource}// Plugin 本质:一个带 apply 方法的 classclassMyPlugin{apply(compiler){compiler.hooks.done.tap('MyPlugin',(stats)=>{console.log('构建完成!')})}}四、devServer 与 HMR
4.1 devServer
module.exports={devServer:{port:3000,hot:true,// 启用 HMRopen:true,// 自动打开浏览器proxy:{// 代理配置'/api':'http://localhost:8080'},historyApiFallback:true// SPA 路由支持}}4.2 HMR(Hot Module Replacement)
HMR 在不刷新整个页面的情况下,替换、添加或删除模块。
// 开启 HMR 后,Webpack 会:// 1. 建立 WebSocket 连接// 2. 文件变化时,通过 WebSocket 通知客户端// 3. 客户端请求更新的模块// 4. 替换旧模块,保持应用状态// 手动处理 HMR(如状态管理)if(module.hot){module.hot.accept('./module',()=>{// 模块更新后的处理逻辑})}五、contenthash 缓存策略
5.1 三种 hash 类型
// 1. hash:每次构建都变化,所有文件共用output:{filename:'[name].[hash].js'}// 2. chunkhash:同一 chunk 的文件相同output:{filename:'[name].[chunkhash].js'}// 3. contenthash:根据文件内容变化output:{filename:'[name].[contenthash].js'}5.2 为什么用 contenthash
// 假设有两个文件:app.js 和 vendor.js// 使用 chunkhash:vendor.js 内容没变,但 app.js 变了// 结果:vendor.js 的 hash 也变了(因为它们在同一 chunk)// 使用 contenthash:只有内容变化的文件 hash 才变// 结果:只有 app.js 的 hash 变化,vendor.js 保持不变// 浏览器可以利用缓存,避免重新下载未变化的文件5.3 最佳配置
module.exports={output:{filename:'[name].[contenthash:8].js',// 8 位 hashchunkFilename:'[name].[contenthash:8].chunk.js'},optimization:{splitChunks:{cacheGroups:{vendor:{test:/[\\/]node_modules[\\/]/,name:'vendors',chunks:'all'}}}}}六、从入口到产出:完整链路
1. 读取 Entry 2. 从 Entry 开始,递归分析依赖 3. 对每个模块应用对应的 Loader 转换 4. 生成模块图(Module Graph) 5. 将模块合并为 Chunk 6. 应用 Plugin(如 HtmlWebpackPlugin) 7. 输出到 dist 目录七、易混淆点
- Loader vs Plugin:Loader 是模块转换器(处理单个文件),Plugin 是构建流程扩展(干预整个构建过程)。
- 执行顺序:Loader 从右到左执行;Plugin 按数组顺序执行,但会根据 hooks 类型在不同阶段触发。
- HMR 原理:通过 WebSocket 实现,文件变化时通知客户端请求更新的模块,替换旧模块保持应用状态。
- contenthash:根据文件内容计算 hash,只有内容变化时 hash 才变化,利于浏览器缓存。
八、思考与练习
1.Loader 和 Plugin 的区别是什么?
解析:
- Loader:模块转换器,处理单个文件(如 CSS → JS),配置在
module.rules - Plugin:构建流程扩展,干预整个构建过程(如生成 HTML),配置在
plugins
2.Loader 的执行顺序是怎样的?
解析:从右到左,从下到上。例如use: ['style-loader', 'css-loader'],先执行css-loader,再执行style-loader。
3.HMR 是如何工作的?
解析:
- 建立 WebSocket 连接
- 文件变化时,Webpack 通过 WebSocket 通知客户端
- 客户端请求更新的模块
- 替换旧模块,保持应用状态
4.为什么推荐使用contenthash?
解析:contenthash根据文件内容计算 hash,只有内容变化时 hash 才变化。这使得未变化的文件可以利用浏览器缓存,避免重新下载。
5.说一下 Webpack 从入口到产出的完整流程。
解析:
- 读取 Entry
- 递归分析依赖
- 对每个模块应用 Loader 转换
- 生成模块图
- 合并为 Chunk
- 应用 Plugin
- 输出到 dist 目录