1. 内容整体设计与思路拆解
1.1 为什么这个阶段还要聊webpack5
先说个现象。我这几年面试前端,简历上十有八九都写着“熟悉webpack配置”,可真让对方讲讲loader和plugin的区别、说说tree shaking的原理,能讲清楚的不超过两成。大部分人是被Vite惯坏了——Dev server一敲,页面出来了,至于打包环节发生了什么,基本是一本糊涂账。
但现实是,生产环境里存量项目绝大多数还是webpack的天下。特别是那些需要深度定制、多页面、复杂CDN部署、微前端拆分的场景,Vite的上限就在那儿摆着,最终大家都得回到webpack的语境里解决问题。所以说,webpack5不是过时,而是被低估了。
另外webpack5本身解决了webpack4时代几个非常痛点的问题:持久化缓存让二次构建速度提升了一个量级、module federation让微前端有了全新的落地思路、内置的静态资源模块让file-loader和url-loader直接退出了历史舞台。这些东西如果你不去亲手搭一遍工程,光看文档是体会不到它的好的。
这篇博文就是用webpack5完整走一遍前端工程化搭建的流程,包含开发环境、生产构建、性能优化、多环境区分、代码规范集成这几个核心模块。我不会只贴配置就完事,每个关键配置都会拆开讲清楚为什么这么写、底层在做什么、坑在哪里。目标读者是那种已经写过一些业务代码、想真正搞懂前端构建链路的人,不管是准备跳槽还是准备接手公司老项目,这篇都能让你少走弯路。
1.2 搭建方案的选型考量:为什么不用脚手架
很多人会问,现在create-react-app、vue-cli、甚至Vite一步到位,为什么还要手动搭一遍webpack?
我的看法是:脚手架帮你做了80%的事情,但也藏了80%的细节。CRA你只能改react-scripts暴露出来的那点配置,哪天你想加个Sass全局变量、做多页面入口、自定义CDN路径,就卡住了。而手动搭建webpack,本质上是在搭建你自己的构建平台,一切都在掌控之中。
这次我选型的原则有三条:
- 不追求最新,追求最稳:webpack就用5.x长期维护版本,loader和plugin都选社区活跃度高、维护频率正常的。
- 不堆砌配置:能用webpack5内置能力解决的,绝不多装一个包。比如静态资源处理用内置的asset module,开发服务器用webpack-dev-server,不再额外接一堆第三方中间件。
- 开发体验和生产构建分开:开发环境核心是速度和热更新,生产环境核心是体积和性能。两套配置要按场景去优化,不能一套配置走天下。
用一句话概括这次的搭建思路:用webpack5把开发构建、资源处理、代码质量、性能优化全部整合成一条标准化流水线,搭建出来的工程可以直接用于商业项目,也可以作为团队内部统一的前端构建基线。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础骨架搭建
2.1 初始化项目和安装核心依赖
先准备一个项目目录,我用的是frontend-webpack-boilerplate,你也可以随意命名。
bash复制mkdir frontend-webpack-boilerplate
cd frontend-webpack-boilerplate
npm init -y
然后安装webpack核心依赖。这里有一个容易踩的坑:webpack4和webpack5的版本差异很大,webpack4时代安装webpack-cli是为了命令行工具,webpack5同样需要,但两者的兼容性更好。安装命令:
bash复制npm install webpack webpack-cli --save-dev
装完之后建议用npx webpack --version验证一下版本。如果输出是5.x.x,那就没问题。有个概念要先搞清楚:webpack是核心打包库,webpack-cli是负责在命令行里调用webpack的工具,两者缺一不可。
接下来安装开发服务器和插件:
bash复制npm install webpack-dev-server html-webpack-plugin --save-dev
html-webpack-plugin是工程化搭建里几乎绕不开的插件。它的作用是根据模板生成最终要用的HTML文件,并且自动把打包出来的js、css文件通过script和link标签注入到HTML里。如果没有这个插件,你每次构建完都要手动去改HTML里的资源引用路径,这在多入口工程里基本就是噩梦。
2.2 基础目录结构和三个关键文件
一个合理的目录结构是工程化的地基。我建议按源码和构建逻辑分开组织:
code复制frontend-webpack-boilerplate/
├── src/
│ ├── assets/
│ │ ├── images/ # 图片资源
│ │ └── styles/ # 全局样式
│ ├── js/
│ │ └── index.js # 入口文件
│ ├── views/
│ │ └── index.html # HTML模板
├── config/
│ ├── webpack.common.js # 公共配置
│ ├── webpack.dev.js # 开发环境配置
│ └── webpack.prod.js # 生产环境配置
├── dist/ # 构建输出目录
├── package.json
这里我把webpack配置拆分成了三个文件。webpack.common.js写开发和构建都要用的公共配置,webpack.dev.js和webpack.prod.js分别写各自环境独有的配置,再通过webpack-merge把公共配置和环境配置合并起来。
bash复制npm install webpack-merge --save-dev
这种做法的好处是显而易见的:公共逻辑只写一遍,避免开发和生产配置里重复维护入口、loader这些内容。如果你只在webpack.config.js里通过环境变量判断,配置一长就变得不可维护,特别是在loader和plugin多起来之后。
2.3 入口与出口的配置逻辑
先看webpack.common.js里的基础配置:
javascript复制const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/js/index.js',
},
output: {
filename: 'js/[name].[contenthash:8].js',
path: path.resolve(__dirname, '../dist'),
clean: true,
publicPath: '/',
},
plugins: [
new HtmlWebpackPlugin({
templ
