webpack5实现前端工程化搭建
webpack5/ vite为什么会出现?
在早期的 Web 开发中,我们只需要编写 HTML、CSS 和 JavaScript,浏览器可以直接运行这些文件。
但随着前端工程规模不断扩大,单纯“写代码 → 浏览器运行”的模式逐渐暴露出问题。
- 浏览器“听不懂”开发语言:
- 问题:浏览器只能读懂标准的 JS、CSS。但为了开发效率,我们用了
.vue文件、JSX、TS、Less/Sass,浏览器直接运行这些会报错。 - Webpack / Vite 的作用:构建工具本质上充当的是一个 “翻译官”:JSX → 普通 JS、TypeScript → JavaScript、Less / Sass → CSS、新语法 → 浏览器可识别的语法,它们的目标不是让你写得更复杂,而是让浏览器最终只看到它能运行的代码。
- 问题:浏览器只能读懂标准的 JS、CSS。但为了开发效率,我们用了
- 网络请求的“拥堵”:
- 问题:如果你有 50 个 JS 文件(比如 utils.js, api.js, components…),浏览器加载页面时就要发 50 次 HTTP 请求。这会导致页面加载极慢(队头阻塞), 请求排队、页面加载缓慢、首屏渲染延迟等问题。
- Webpack 的作用:它把 50 个零散文件“缝合”成 1 个或几个 bundle 文件(模块合并),浏览器只需请求一次,从而减少请求数量,提高加载效率。
- 资源管理的“混乱”:
- 问题:图片、字体、CSS 在以前是分开管理的,很难知道哪张图被哪个组件用了。删代码时不敢删图,导致项目越来越臃肿。
- Webpack 的作用:“依赖图谱”。在 Webpack 眼里,一切皆模块。它从入口文件出发,顺藤摸瓜找到所有依赖(JS引用了图片,Vue引用了CSS)。只有被引用的资源才会被打包,没用的直接丢弃(Tree Shaking)。
因此基于上述种种问题,我们需要解析引擎,能够针对我们业务文件不同的代码,输出为浏览器能够使用的代码,并且能够对我们的文件进行管理输出,在我们的产物文件中能够发挥更好的效果
构建工具在整体架构中的位置
在传统服务端渲染(SSR 或模板渲染)场景中,整体流程通常是:
Server(Koa / Express)
↓
模板(.tpl / .html)
↓
浏览器渲染页面
而在现代前端工程中,构建工具被插入到了服务与浏览器之间:
业务源码(Vue / React / TS / CSS)
↓
构建工具(Webpack / Vite)
↓
产物文件(JS / CSS / 图片)
↓
Server(Koa / Nginx)
↓
浏览器
构建引擎内部做了什么?
因此,基于上述分析可以看出,Webpack / Vite 本质上就是前端工程中的构建引擎。整体来看,一个构建引擎的工作流程可以抽象为三个核心步骤:
输出界面
^
业务文件(vue...) -> 构建引擎(webpack/vite) -> 产物文件(public)
| ^
| Koa
|
(解析编译-> 模块分包 -> 压缩优化 )
1️⃣ 解析与编译
- 遇到
.vue→ Vue 解析器 - 遇到 JSX → React 解析器
- 遇到 CSS / Less → 对应的样式处理器
输出浏览器可以执行的资源如下:
xxx.js
xxx.css
xxx.png
2️⃣ 模块拆分与组织
- 将业务代码拆分为多个模块
- 根据策略生成一个或多个 chunk
- 输出最终入口文件(如
entry.js)
3️⃣ 压缩与优化(区分环境)
- 生产环境:
- 压缩 JS / CSS
- 移除无用代码
- 优化体积
- 开发环境:
- 启动本地服务
- 模块热更新(HMR)
- 快速反馈
通过这三个步骤,构建工具将复杂的前端工程,转化为高效、可部署、可运行的最终产物。
具体配置案例(以webpack5为例)
因此基于上面的案例,我们从核心原因探究了webpack/vite具体在工程中的作用,接下来我们用实际案例从上述介绍的三个流程完成webpack5的配置
首先我们先引入一下webpack,具体代码如下,我们使用pnpm run build将调用这个文件
const webpack = require('webpack');
const webpackConfig = require('./config/webpack.prod.js');
console.log('\nbuilding... \n');
webpack(webpackConfig, (err, stats) => {
if (err) throw err;
process.stdout.write(stats.toString({
colors: true, // 终端中输出带颜色
modules: false, // 不输出模块信息
children: false, // 不输出子级信息
chunks: false, // 不输出块信息
chunkModules: true // 输出块模块信息
}) + '\n\n');
});
接下来才是我们最核心的配置也就是webpackConfig,
entry 用来指定应用的入口文件
module 主要通过 rules 配置各种 loader,用来告诉 Webpack:
- 遇到
.js / .jsx用什么解析 - 遇到
.vue用什么解析 - 遇到
.css / .less用什么解析
output 用来配置最终构建产物的输出方式,包括:
- 输出路径
- 文件名规则
- 是否使用 hash
plugins 用来扩展 Webpack 的能力,覆盖更广泛的构建场景,例如:
- 生成 HTML 文件
- 注入环境变量
- 清理构建目录
- 提取 CSS
optimization 主要用于配置构建产物的优化策略,例如:
- 代码拆分
- 缓存策略
- 压缩配置
具体的流程可以具体看webpack 文档,以下配置仅供参考
/**
* webpack 基础配置
*/
module.exports = {
// 入口配置 多页面配置
entry: pathEntries,
// 模块解析配置(决定了要加载解释哪些模块以及用什么方式去解释)
module: {
rules: [
{
test: /\.vue$/, // 匹配所有以.js结尾的文件
use: { loader: "vue-loader" }, // 使用vue-loader进行转译
},
{
test: /\.js$/, // 匹配所有以.js结尾的文件
// 只对业务代码进行打包
include: [path.resolve(process.cwd(), "app/pages")],
use: { loader: "babel-loader" }, // 使用babel-loader进行转译
},
{
test: /\.(png|jpg?g|gif)(\?.+)?$/,
use: {
loader: "url-loader",
options: { limit: 300, esModule: false },
},
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.(woff2|woff|eot|ttf|ttf)(\?\S*)?$/,
use: "file-loader",
},
],
},
// 产物输出路径,因为开发跟生产环境输出不一致,在各自环境中自己配置
output: {
filename: "js/[name]_[chunkhash:8].bundle.js",
path: path.join(process.cwd(), "app/public/dist/prod"),
publicPath: "/dist/prod",
crossOriginLoading: "anonymous",
},
// 配置模块解析的具体行为
resolve: {
//js': ['.js', '.jsx']
extensions: [".js", ".vue", ".less", ".css"],
alias: {
// 用$pages代替项目中app/pages的绝对路径
$pages: path.resolve(process.cwd(), "app/pages"),
$common: path.resolve(process.cwd(), "app/pages/common"),
$widgets: path.resolve(process.cwd(), "app/pages/widgets"),
$store: path.resolve(process.cwd(), "app/pages/store"),
},
},
// 配置webpack插件
plugins: [
// 处理.vue文件必备插件 ,处理 .vue文件的三个板块
new VueLoaderPlugin(),
// 把第三方库暴露到 window context 上
new webpack.ProvidePlugin({
Vue: "vue",
}),
// 定义全局常量
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: "true", //支持vue解析option api
__VUE_PROD_DEVTOOLS__: "false", //生产环境禁用devtools,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false", //生产环境禁用显示‘水合’信息
}),
//构造最终渲染的页面模板
...htmlWebpackPlugins,
],
//配置打包输出优化(代码分割,模块合并 缓存 Treesharing 压缩等优化策略)
// 针对vue这种包只需要打包一次,针对一些通用模块比如 common 也会被多个地方使用, z也只需要独立打包
optimization: {
// 分包,对上面产出的js进行模块划分
splitChunks: {
chunks:'all',
maxAsyncRequests: 10, //每次最大异步请求数量
maxInitialRequests: 10, //入口点的最大请求数量
cacheGroups: {
vendor: { //第三方库
name: "vendor",
test: /[\\/]node_modules[\\/]/,
priority: 20, //优先级 数字越大优先级越高
enforce: true, //强制执行
reuseExistingChunk: true, //可复用已经存在的模块
},
common: { //公共模块
name: "common",
priority: 10,
minSize: 1, //最小分割文件大小(1 byte)
minChunks: 2, //被两次引用就是公共模块
reuseExistingChunk: true, //可复用已经存在的模块
},
}
},
// 将 webpack运行时的代码打包到 runtime.js 文件中
runtimeChunk: true
},
};
上述是一个开发环境,但是在实际项目中,我们需要配置一个生产环境和开发环境,两种环境具体需要配置的东西是稍微有些不同的,
在开发环境中,构建工具除了完成代码解析和打包之外,还需要提供一套 开发时的联动能力。 当业务代码发生变化时,让浏览器尽可能快地看到最新结果,也就是实现热更新的能力
浏览器运行
|
业务文件 <- 【 监控能力 通知能力 】 -> 产物文件
devServe
具体流程介绍如下:
在开发环境下,devServer 会监听业务文件的变化(JS、CSS、模板等)。
一旦文件被修改,构建工具能够立刻感知到变化,而不需要手动重新执行打包命令。
当变更被捕获后,Webpack / Vite 会重新执行相关模块的编译流程:
- 只重新构建受影响的模块
- 生成新的构建结果(通常存在于内存中,而不是磁盘)
最后,devServer 会通过 WebSocket 等方式通知浏览器:
- 构建已完成
- 可以刷新或执行热更新
浏览器收到通知后,完成页面刷新或局部替换,从而看到最新效果。
具体配置可以看官网
Reference
起步 | webpack 中文文档 | webpack中文文档 | webpack中文网
玄哲前端
更多推荐
所有评论(0)