构建工具基础:Webpack核心概念(Entry、Output、Loader、Plugin)
说实话,很多同学一上来就问我:「Webpack到底是个啥?」
我通常会打个比方:它就像一条流水线。你把原材料(源码)丢进去,它帮你加工、打包,最后输出成品(构建后的文件)。
嗯,今天我们就来拆解这条流水线的四个核心部件。我个人习惯把它们分成两组:入口与出口、加载器与插件。
Entry:一切从这里开始
Entry,就是打包的起点。Webpack会从这个文件出发,顺着依赖关系一路找到所有模块。
单入口 vs 多入口
单页面应用,通常一个入口就够了。比如:
module.exports = {
entry: './src/index.js'
};
多页面应用,或者你想拆分包,可以用对象语法:
module.exports = {
entry: {
home: './src/home.js',
about: './src/about.js'
}
};
我在项目中遇到过一种情况:团队把第三方库和业务代码混在一个入口里。结果每次改一行业务代码,vendor缓存就失效了。后来我建议拆成多入口,配合SplitChunks,缓存命中率直接翻倍。
小技巧:如果你用单入口,但想提取公共依赖,记得配optimization.splitChunks。别一股脑全塞进一个bundle里。
Output:打包后的归宿
Output告诉Webpack:打包好的文件放哪,叫什么名字。
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js'
}
};
这里有个坑,我曾经踩过——contenthash。它根据文件内容生成哈希值。内容变了,哈希才变。这样浏览器就能精准缓存未改动的文件。
你想想看,如果不用哈希,每次部署用户都得重新下载所有JS。那体验能好吗?
注意:多入口时,filename里必须用[name]占位符,否则会覆盖。我见过有人忘了加,结果最后一个入口覆盖了前面的,排查了半天。
Loader:让Webpack理解非JS文件
Webpack原生只懂JavaScript和JSON。那CSS、图片、TypeScript怎么办?
Loader就是翻译官。它把各种文件转成Webpack能处理的模块。
举个例子,处理CSS:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
};
这里要注意顺序:从右到左,从下到上。css-loader先解析CSS里的import和url,然后style-loader把样式注入到DOM里。
我记得有一次,团队新同学把顺序写反了,结果样式死活不生效。他折腾了一下午,最后发现是loader顺序的问题。嗯,这种错误我也犯过,所以印象特别深。
常用Loader一览
| Loader | 用途 |
|---|---|
| babel-loader | 把ES6+转成ES5 |
| ts-loader | 处理TypeScript |
| file-loader | 处理图片、字体等文件 |
| url-loader | 小文件转base64,减少请求 |
| sass-loader | 把SCSS/SASS转成CSS |
避坑指南:我曾经用url-loader处理大图片,结果打包后的JS文件暴涨到5MB。后来加了limit限制,超过8KB的文件还是走file-loader。这样既减少了小请求,又避免了大文件内联。
Plugin:干Loader干不了的事
Loader只能处理模块转换。但打包优化、资源管理、环境注入这些事,得靠Plugin。
Plugin的威力在于:它能触及Webpack编译的整个生命周期。从开始编译到输出文件,每一步都能插手。
举个最常见的例子——HtmlWebpackPlugin:
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
};
它会自动生成HTML,并把打包后的JS/CSS注入进去。你不用手动改script标签了。
还有几个我常用的:
- MiniCssExtractPlugin:把CSS从JS里抽出来,生成单独文件。对缓存和加载性能都有好处。
- DefinePlugin:注入全局变量。比如区分开发环境和生产环境。
- BundleAnalyzerPlugin:可视化分析打包体积。我每次做性能优化,第一步就是跑它看看哪个模块最大。
注意:Plugin和Loader的区别,说白了就是:Loader管「怎么转」,Plugin管「怎么做更多事」。别搞混了。
Entry + Output + Loader + Plugin 的协作流程
我习惯用一句话总结:
Entry告诉Webpack从哪开始,Loader处理沿途的各类文件,Plugin在关键时刻做额外操作,最后Output把结果写到指定位置。
举个例子,一个完整的配置大概长这样:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[contenthash].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
};
你看,每个部分各司其职。入口、出口、加载器、插件,缺一不可。
核心要点回顾
- Entry:打包的起点,单页用字符串,多页用对象
- Output:输出路径和文件名,善用contenthash做缓存
- Loader:处理非JS文件,注意执行顺序从右到左
- Plugin:扩展Webpack能力,覆盖编译全生命周期
说实话,这四个概念搞明白了,Webpack就算入门了。后面那些高级配置,都是在这四个基础上做文章。
下一章,我会带你实战配置一个完整的Webpack项目。到时候咱们再细聊优化的事。