一、前言
本来想将webpack的内容整理成一篇到底,但太长了容易引起视觉疲劳。所以单独将内容和实践内容分开。
在了解到理论内容之后,脑子总会出现这么一个问题。
脑瓜子: 只看不写真的能让你学废吗?
我的回答是:会的!脑子暂时会了,手脚还没会!
所以本文偏向于实践,通过实践配置,结合测试结果图表。让你对webpack配置有个更清晰理解。
二、实践案例
实现 webpack 工程化打包的一些配置案例,完成案例将得到的收益:
基础入门案例:了解 webpack 的相关基本配置
搭建开发案例:脱离脚手架结合 React 框架实现开发案例配置,达到更了解工程开发的运作过程。
2.0.环境&版本
本文包管理采用pnpm
"webpack": "^5.88.2"
node:18.12.0 或 16.14.2
2.1.基础配置案例
2.1.1.初始化项目
打开终端初始化项目配置文件 package.json
bash
pnpm init
安装 Webpack 及脚手架
bash
pnpm add -D webpack webpack-cli
新建文件夹src
,并在其目录下添加一个文件 main.js
并写点内容:
javascript
console.log("Hi! Webpack 你也会打篮球吗🤣");
测试是否初始化成功!🚀
配置package.json
脚本
javascript
"scripts": {
"build": "webpack ./src/main.js"
},
执行脚本 ♻️
bash
pnpm run build
webpack 开箱即用,可以无需使用任何配置文件。这时查看根目录发现
dist
文件夹 dist/main.js
到这里说明已初始化成功!✅
官方在线案例预览
2.1.2.出入口基础配置
完成上一步,说明咱已经实现了 webpack 的第一次打包 😄
但其实我们还没有做任何配置,打包生成的内容只是 webpack 的默认配置。下面咱们来添加更丰富的配置。
新建build
文件夹,并添加webpack.config.js
文件
javascript
/**
* @fileName webpack.config.js
* @description based.config|基础配置
* @param mode|开发模式
* @param entry|入口文件路径
* @param output|打包输出配置
*/
const path = require("path");
module.exports = {
mode: "development",
entry: path.resolve(__dirname, "../src/main.js"),
output: {
filename: "[name].[hash:8].js",
path: path.resolve(__dirname, "../dist"), // 打包后的目录
},
};
filename: "[name].[hash:8].js"
中的
[name]
是占位符,表示输出文件的名称将根据入口文件的名称进行命名。
[hash:8]
也是占位符,表示在输出文件的名称中插入一个哈希值,:8
表示使用哈希的前八位字符。
接下来测试配置是否成功!🚀
修改之前的package.json
脚本配置
javascript
"scripts": {
"build": "webpack --config build/webpack.config.js"
}
根据特定情况使用不同的配置文件时,则可以通过在命令行中使用--config
标志修改。也可以用-c
缩写形式
执行脚本 ♻️
bash
pnpm run build

如图根目录下dist/main.js
文件后缀多了插入的[hash:8]
哈希值。
说明已配置成功!✅
2.1.3.配置 HTML
上步打包好的内容,可通过<script src='./main.xxx.js'>
引入html
文档并使用。
但配置的动态命名哈希值,每次打包后都不一样,不可能每次都手动引入,所以引入插件来完成这个自动插入 <script >
标签的过程。
根目录下新建public
文件夹,添加index.html
文件,
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpack config Template</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
并安装插件
bash
pnpm install -D html-webpack-plugin
继续添加配置
javascript
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// code...
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
],
};
执行脚本测试配置是否成功!🚀
bash
pnpm run build
如图根目录下
dist/index.html
会自动引入打包好的文件。
html 自动注入 js 配置成功!✅
官方插件详情
2.1.4.配置 CSS 解析
clean-webpack-plugin 配置
基础三件套还差css
没有配置,在配置前先解决一个问题。
你可能会发现在修改main.js
内容后,执行打包脚本时输出的 dist
文件夹会有新的打包内容,并且之前打包的文件依然存在。如下图所示:
正常来讲这是不合理的,旧的内容应当被更新的内容替换。为了解决这个问题还需引入clean-webpack-plugin
插件,在打包文件生成前清空输出的文件夹。
继续添加插件配置
webpack4
javascript
// webpack.config.js
...
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
...
plugins:[new CleanWebpackPlugin()]
}
上方是 webpack4 的配置方式,webpack5 的已经将该方式移入output
配置项目中。官方详情
javascript
// webpack.config.js
const path = require("path");
// ...
module.exports = {
mode: "development",
entry: path.resolve(__dirname, "../src/main.js"),
output: {
filename: "[name].[hash:8].js",
path: path.resolve(__dirname, "../dist"),
clean: true, //w4 - clean-webpack-plugin
},
// ...
};
执行脚本测试配置是否成功!🚀
如图在修改main.js
内容后,执行打包脚本时输出的 dist
文件夹不再出现之前的内容。
打包前清空输出文件夹目录配置成功!✅
css,less,sass 基本配置
接下来就是 CSS 的配置
安装样式解析器loader
bash
pnpm add -D style-loader css-loader
如要使用less
或sass
则需要再安装
bash
pnpm add -D less less-loader
#or
pnpm add -D sass sass-loader node-sass
继续添加配置
javascript
// ...
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.s[ac]ss$/i,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
};
测试配置是否成功!🚀
在pulic/index.html
中添加如下测试 div
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpack config Template</title>
<script defer src="./main.d269bcb0.js"></script></head>
<body>
<div id="app"></div>
<div class="demo-css">css demo</div>
<div class="demo-less">less demo</div>
<div class="demo-sass">sass demo</div>
</body>
</html>
在src
文件夹下新建 index.css
index.less
index.scss 或 .sass
样式文件,并在入口文件引入
css
/* index.css */
.demo-css {
width: 100vw;
height: 100px;
background-color: orangered;
}
css
/* index.less */
.demo-less {
width: 100vw;
height: 100px;
background-color: green;
}
css
/* index.sass */
.demo-sass
width: 100vw
height: 100px
background-color: orange
javascript
// main.js
import "./index.css";
import "./index.less";
import "./index.sass";
console.log("hello webpack");
执行打包脚本测试 ♻️
按传统的加载资源方式来讲,此时 html 上应该会引入一个样式标签,<style>
对 css 资源加载。
但查看打包好的dist/index.html
文件你会发现,并没有<style>
标签,浏览器打开发现运行。如下测试图所示:
运行打包后的 html 文件,发现<style>
其实是有被引入的,这也说明了 webpack 并不是按传统的方式加载,它是以js
模块为主导,通过不同的loader
解析 js 中对应 import 的资源。
到这里说明你 Css 的基本配置已经成功!✅
css 添加浏览器厂商前缀配置
但还没有配置完,由于市面上浏览器类型众多,为了 Css 能兼容到不同浏览器,还需添加浏览器厂商前缀。此时需要使用到 autoprefixer
前缀自动补全插件,并配合PostCss-loader
完成对样式的解析。
bash
pnpm add -D postcss-loader autoprefixer
继续添加配置
javascript
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
},
{
test: /\.s[ac]ss$/i,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
},
],
},
在根目录下新建.postcssrc.json
或 postcss.config.js
javascript
// .postcssrc.json
{
"plugins": {
"autoprefixer": {}
}
}
javascript
// .postcssrc.json
module.exports = {
plugins: [
require("autoprefixer"), // 直接引用该插件即可
],
};
两种形式使用那种都可以,同时还需在根目录子添加autoprefixer
插件,对不同浏览器盘本的限制的配置文件.browserslistrc
不做配置
autoprefixer
插件不起作用
json
# 支持的不同浏览器厂商的版本
last 10 Chrome versions
last 10 Firefox versions
Safari >= 6
ie >= 10
也可以直接在 package.json 或 postcss-loader 条件中配置暂不展示。官方配置详情
测试配置是否成功!🚀
在 index.css 文件添加测试样式
css
.demo-css {
display: flex;
transform: scale(0.5);
}
执行打包脚本测试 ♻️
浏览运行打包好的dist/index.html
文件,结果如下图:
如图不同浏览器厂商前缀已经加上!
到这里说明你 Css 浏览器厂商前缀配置已经成功!✅
css 拆分配置
样式的配置其实到这里就差不多了,但是为性能提高性性能,需要将 CSS 提取到一个单独的文件中。
这样做的好处是:
- 提高页面加载速度:将 CSS 文件与 JavaScript 文件分离,可以并行加载,加快页面的加载速度。
- 缓存优化:当 CSS 文件发生变化时,浏览器可以只重新加载 CSS 文件,而不需要重新加载整个 JavaScript 文件。 代码分离:将 CSS 文件与 JavaScript 文件分离,可以更好地实现代码的分离与模块化,提高代码的可维护性和可读性。
- 兼容性优化:某些浏览器对于内联的 CSS 样式有限制,将 CSS 提取到单独的文件中可以避免这些问题。
总的来说就是可以优化页面加载速度、提高用户体验,同时也有利于代码的维护和兼容性优化。所以这里需要用到mini-css-extract-plugin
插件。 官方详情
bash
pnpm add -D mini-css-extract-plugin
继续修改配置
javascript
// # webpack.config.js
// ...
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader",
],
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
],
},
plugins: [
//...
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:8].css",
}),
],
};
这里输入的文件名使用contenthash
方式,可以防止第二次打包构建时 css 缓存被清除,也就是只有当 CSS 内容发生改变时,才对该文件进行清除缓存并更新。 官方详情
测试配置是否成功!🚀
再次修改 index.css 测试样式
css
.demo-css {
width: 100vw;
height: 100px;
background-color: orangered;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
}
执行打包脚本测试 ♻️
查看打包文件夹dist
,发现 css 已经配单独取出到指定的文件,结果如下图:
浏览运行打包好的dist/index.html
文件,结果如下图:
但需要注意的是独立抽取出来的.css
一般使用于生产环境,而对于开发环境,为了方便热更新HRM
使用 style-loader
效果可能更好。所以后续优化,可针对不同环境配置不同的loader
到这里 Css 所以基础配置已经成功!✅
2.1.5.配置静态资源(图像、音视频)
关于静态资源文件导出的配置,在 webpack 5 之前,通常使用:
- raw-loader 将文件导入为字符串
- url-loader 将文件作为 data URI 内联到 bundle 中
- file-loader 将文件发送到输出目录
而在 Webpack5 中官方将上述方法内置化,推用资源模块(asset module) 方式替换。具体替换如下:
- asset/resource : 发送一个单独的文件并导出 URL。替换 file-loader
- asset/inline : 导出一个资源的 data URI。替换 url-loader
- asset/source: 导出资源的源代码。替换 raw-loader
- asset :在导出一个 data URI 和发送一个单独的文件之间自动选择,可配置资源体积限制实现。替换 url-loader
在这里采用 asset方法,继续配置
javascript
// # webpack.config.js
// ...
module.exports = {
// ...
module: {
rules: [
// ...
//图像
{
test: /.(png|jpg|jepg|git|svg)$/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, //限制在 10kb
}
},
generator: {
filename: 'static/images/[name][ext]'
}
},
// 视频
{
test: /.(mp4|mp3|webm)$/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
}
},
generator: {
filename: 'static/medias/[name][ext]'
}
}
//字体
{
test: /.(woff2|eot|ttf|otf)$/,
type: 'assets',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
}
},
generator: {
filename: 'static/fonts/[name][ext]'
}
},
],
},
//...
}
测试配置是否成!🚀
使用 asset 时如果是未配置大小限制,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
在这里我们配置的最大限制是10K
:
同理如果图片大于 10K 测自动使用 asset/resource 处理,如果图片小于 10K 测自动 asset/inline 处理。
测试看看是否于预期相似。
新建静态资源文件src/assets
,导入引入两张大于小于 10 的图片
在public/index.html
中新增两个测试 dev,并在src/index.css
添加样式
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpack config Template</title>
</head>
<body>
<div id="app"></div>
<div class="demo-css">css demo</div>
<div class="demo-less">less demo</div>
<div class="demo-sass">sass demo</div>
<div class="demo-img1">img 小于10K </div>
<div class="demo-img2">img 大于10K </div>
</body>
</html>
css
/* index.css */
.demo-img1,
.demo-img2 {
width: 100vw;
height: 100px;
background-color: gray;
}
并在main.js
中引用
javascript
// main.js
// ...
import "./index.css";
import vueSvg1K from "./assets/vue.svg";
import yyxJpg208K from "./assets/yyx.jpg";
document.querySelector(".demo-img1").style.background = `url(${vueSvg1K})`;
document.querySelector(".demo-img2").style.background = `url(${yyxJpg208K})`;
执行打包脚本测试 ♻️
浏览器打开查看dist/index.html
,发现小于限制的图像被转化为Base64
格式,说明配置的type: 'assets'
方式自动使用了asset/inline
模块进行处理。结果如下图:
到这里静态资源的配置已经成功完成!✅
2.1.6.配置 JS 语法解析
在完成上述内容的配置后,前端三件套还差 JS 未配置。
由于目前 ECMAScript 语法标准与过去有所区别,为了向后兼容的 JavaScript 语法,便于运行在当前和旧版本的浏览器或其他环境中。所以需要用到Babel
对 JS 进行编译解析。具体配置如下:
依赖安装
bash
pnpm add -D babel-loader @babel/core @babel/preset-env core-js
webapck 在过去的版本中,为了确保 JS 最新的特性代码在不同浏览器和环境中兼容 ,通常是直接使用@babel/polyfill
库,为了减小体积这里采用按需引入库core-js
代替
在根目录新建.babelrc.json
文件,并添加相关配置:
json
//#.babelrc.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
"useBuiltIns": 'usage'
: 配置后 Babel 会根据代码中实际使用的 ES6+ 特性自动检测并按需引入 polyfills
"corejs": 3
: 提供了对 JS 新标准特性的支持和 polyfill 功能
polyfills
可以用于填充各种不同的功能缺失,比如 Promise、fetch API、ES6+ 新语法等。
javascript
// # webpack.config
// ...其他的配置项
module.exports = {
// ...其他的配置项
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, //排除内容不解析
use: {
loader: "babel-loader",
},
},
],
},
};
测试是否配置成功!🚀
在 main.js 中添加一些 ES6 的语法特性
javascript
// # main.js
// 数组去重
const uniqueArr = [...new Set([1, 1, 1, 2, 3])];
console.log(uniqueArr);
// 箭头函数
const es6Fn = () => "is es6Fn";
console.log(es6Fn());

其实如果你在未配置,babel 前 ES6 语法特性也是能解析的,但是为了支持和适配目标环境的兼容,以及更高级的 JavaScript 特性或 API 还是需要配置 babel 来进行解析的。
到这里所以的基本配置以及完成啦!✅
呱唧呱唧!🤗
在基础案例的基础上,需要达到开发目的我们还需要配置环境变量、结合开发架构、JS/TS 配置,以及性能优化配置等内容。当然如果你还想要继续,请看下一章节。
2.2.基于 React 搭建
开发配置章节!本章将结合 React 实现基础开发的基本配置。
2.2.1.初始化配置文件
在开始配置前,先修改一下基础案例的文件目录结构。
- 将
build
文件名修改为scripts
- 新增
App.jsx
文件,并将main.js
改为main.jsx
- 删除
public/index.html
多余的 div,只保留 - 删除不需要的文件
index.sass
,并将index.css
改为App.css 或 .less
最终目录结构如下:
目录结构修改完成!✅
接下来可以开始基于 React 开发的相关配置了
2.2.2.配置 React JSX/TSX 解析
安装 react 依赖
bash
pnpm add -s react react-dom
pnpm add -D @babel/preset-react
# or TS
pnpm add -s @types/react @types/react-dom
pnpm add -D @babel/preset-typescript
配置 react 预设解析.babelrc.json
json
//# .babelrc.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
],
"@babel/preset-react"
//@babel/preset-typescript //如使用ts需添加
]
}
修改webpack.config.js
文件名为webpack.base.js
,并添加 .jsx
后缀的解析
javascript
// # webpack.base.js
// ....
module: {
rules: [
{
test: /.(js|jsx)$/, // ts : /.(ts|tsx)$/
exclude: /node_modules/, //排除内容不解析
use: {
loader: "babel-loader",
},
},
]
/**
* @description resolve|解析配置
* @param {String} extensions |文件后缀扩展
* @param {object} alias |别名配置
*/
resolve: {
extensions: [ '.jsx', '.js'], // 如使用Ts测加上'.tsx', '.ts'
alias: {
"@": path.join(__dirname, '../src')
},
},
// ....
}
如需使用Ts
进行开发,测安装对于包并更改如上对应配置即可。
就目前的配置,以实现基本的 JSX/TSX 解析,为了像脚手架一样,让浏览器可以实时响应修改内容。需要使用webpack-dev-server
库,进行热更新(HRM)相关配置,具体如下。
2.2.3.配置 HRM 热更新&环境变量
启用 HMR 很容易,且在大多数情况下不需要任何配置。只需在devServer
开启 host
javascript
module.exports = {
// ...
devServer: {
// 开启 HMR 特性
hot: true,
},
};
安装热更新依赖
bash
pnpm add -D webpack-dev-server webpack-merge
webpack-dev-server
创建两个服务器:提供静态资源的服务(express)和 Socket 服务, 浏览器拿到两个新的文件后,通过 HMR runtime 机制实现热更新。
webpack-merge
: 用于合并配置项。可实现环境变量区分配置。
在实际开发中,可能存在诸多模式,而热更新(HRM)通常用于开发环境,在生产环境中,测更倾向于使用代码打包。具体配置如下:
修改基础配置
javascript
// # scripts/webapck.base.js
//...省略其它部分
module.exports = isDev => ({
mode: isDev ? "development" : "production",
//...省略其它部分
});
webpack 内置优化了三个模式, mode:' none' | 'development' | 'production' 默认值设置为 production。这里通过不同环境配置文件传入的值,对不同环境模式进行区分。
环境区分配置,新增开发环境 scripts/webapck.dev.js
、结合webpack-merge
获取基础配置。并开启热更新 hot: true
javascript
/**
* @fileName scripts/webapck.dev.js
* @description development|开发环境
* @param getBaseCfg |使用基础配置
* @param {Object} merge |将多个 webpack 配置文件合并成一个
*/
const getBaseCfg = require("./webpack.base");
const { merge } = require("webpack-merge");
const path = require("path");
module.exports = merge(getBaseCfg(true), {
devtool: "source-map",
devServer: {
port: 8297,
compress: false, //|压缩
hot: true, //|热更新
historyApiFallback: true, //| 解决404的问题
static: {
directory: path.join(__dirname, "../public"),
},
},
});
新增生产环境scripts/webapck.prod.js
配置文件,并结合webpack-merge
获取基础配置。
javascript
/**
* @fileName scripts/webapck.prod.js
* @description productions|生产环境配置
*/
const getBaseCfg = require("./webpack.base");
const { merge } = require("webpack-merge");
const path = require("path");
module.exports = merge(getBaseCfg(false), {
/**
* @description 优化方案配置
* @param minimizer |压缩方案配置
* @param TerserPlugin |压缩JS
* @param CssMinimizerPlugin |压缩css
* @param splitChunks |代码切片 (https://webpack.docschina.org/plugins/split-chunks-plugin)
*/
optimization: {
splitChunks: {
// 缓存配置
chunks: "async",
minSize: 20000,
minChunks: 1,
cacheGroups: {
vendors: {
priority: 1,
test: /node_modules/,
name: "vendors",
},
commons: {
name: "commons",
minChunks: 3,
},
},
},
},
});
2.2.4.最终测试
分别在App.jsx
、App.css
、index.less
添加一些内容,具体如下
jsx
// # App.jsx
import React, { useState } from "react";
import reactLogo from "./assets/react.svg";
import "./App.css";
const App = () => {
const [count, setCount] = useState(0);
return (
<>
<div>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>webpack5 + React</h1>
<div className="card">
<button onClick={() => setCount(count => count + 1)}>
count is {count}
</button>
</div>
</>
);
};
export default App;
css
/* App.css */
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
less
/* index.less */
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
最后修改入口文件src/main.jsx
配置
jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.less";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
修改package.json
脚本配置
json
"scripts": {
"dev": "webpack-dev-server -c scripts/webpack.dev.js",
"start:prod": "webpack-dev-server -c scripts/webpack.prod.js",
"build": "webpack --config scripts/webpack.base.js"
},
其它环境变量配置方法:
- cross-env : 在命令行设置环境变量值
NODE_ENV=xxx
,并通过 Node 全局变量process.env.NODE_ENV
获取对应的变量值. - dotenv : 使用.env 文件和 dotenv 插件,可以使用.env 文件来定义环境变量,并使用 dotenv 插件来加载这些环境变量。
执行 start 脚本! ♻️
浏览器打开终端显示的链接,结果如下:
到这里基于 react 开发的 webpack 基本配置就完成啦!✅
呱唧呱唧!🤗
2.2.5.配置第三方库
至此已完成 React 开发看来的基础配,在开发中除了这些配置,可能还会使用到第三方的 UI 组件库,页面路由,以及状态管理库等。具体安装配置如下:
2.2.5.1.配置原子化 CSS ( Tailwind )
在根目录下安装,并初始化配置文件
bash
pnpm add -D tailwindcss postcss
#初始化
npx tailwindcss init
配置tailwind.config.js
,定制化
javascript
// # tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
根据需求定制化全局 CSS。
在全局样式文件中scr/index.less
引入tailwindcss配置
css
/* index.lesss */
@tailwind base;
@tailwind components;
@tailwind utilities;
最后只需要在 CSS 解析器配置**.postcssrc.json**
插件中引入 tailwindcss 库即可
javascript
// # .postcssrc.json
{
"plugins":{
"autoprefixer": {},
"tailwindcss": {}
}
}
测试配置是否成功!🚀
执行pnpm dev
查看结果如下图所示:
配置 tailwindcss 完成!✅
2.3.优化打包性能
在实际开发中当项目较大时,如果打包性能不佳你可能会体验过这样的情况,项目运行与打包期间格外的长。为了提升开发与用户体验,需要对打包速度与包的体积做相关优化。
2.3.1.打包速度优化
2.3.1.0.按需选择解析方案
在基础案例 css 拆分配置中有用到 mini-css-extract-plugin
抽取独立的 CSS 文件,该方式可提升生产环境 CSS 的加载速度,但对于开发环境 style-loader
方式更方便热更新HRM
。
所以可以结合环境变量进行不同环境的按需加载。具体配置如下:
javascript
//# webpack.base.js
// ....省略其他
module.exports = isDev => ({
// ....省略其他
module: {
rules: [
{
test: /.(css)$/,
use: [
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
],
},
{
test: /.(less)$/,
use: [
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"less-loader",
],
},
{
test: /\.s[ac]ss$/i,
use: [
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
],
},
// ....省略其他
});
为了规范样式和防止命名重复,可以采用 CSS Module 的形式。具体如下配置:
结合 oneOf
属性默认使用第一个配置,可防止使用到css module
样式重复渲染。
javascript
//# webpack.base.js
// ....省略其他
module.exports = (isDev) => ({
// ....省略其他
module: {
rules: [
// 默认使用第一个配置
oneOf: [
{
test: /.module.(less|css)$/,
include: [path.resolve(__dirname, "../src")],
use: [
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: 2,
// 开启 css modules,动态定义样式名称
modules: {
localIdentName: "[path][name]__[local]--[hash:base64:4]",
},
},
},
"postcss-loader",
"less-loader",
],
},
{
test: /.(css)$/,
use: [
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
],
},
// ....省略less,sass
]
]
},
// ....省略其他
})
测试优化配置是否成功!🚀
新建src/app.module.css 或.less
css
// app.module.css
.modtitle {
background-color: orange;
}
在实例中使用
javascript
// App.jsx
// ...
import styles from "./app.module.css";
const App = () => {
return (
<>
// ...
<div className={styles.modtitle}>CSS Module</div>
</>
);
};
export default App;
运行开发环境脚本 ♻️
Css 按需优化配置完成!✅
2.3.1.1.缩小文件解析范围
结合内置属性限制打包时的解析范围:
- exclude/include : 缩小
loader
解析范围
javascript
//# webpack.base.js
// ....省略其他
module.exports = (isDev) => ({
module: {
rules: [
{
test: /.(js|jsx|ts|tsx)$/,
include:path.resolve(__dirname,'../src'),
use: {
loader: "babel-loader",
},
exclude: "/node_modules",
},
]
}
)}
- resolve.extensions 尽可能减少后缀尝试的可能性
- alias 定义文件别名
javascript
module.exports = (isDev) => ({
//...
resolve: {
extensions: [".jsx", ".js", ".tsx", ".ts"],
alias: {
"@": path.join(__dirname, "../src"),
},
},
};
- noParse 对完全不需要解析的库进行忽略
javascript
module.exports = (isDev) => ({
//...
module: {
noParse: /jquery/,
},
};
2.3.2.打包体积优化
对包的大小进行限制或减小处理:
CSS 压缩
将所有样式中多余的空格删除并压缩在一起,
bash
pnpm add -D css-minimizer-webpack-plugin
javascript
/**
* @fileName scripts/webapck.dev.js
* @description development|开发环境
* @param getBaseCfg |使用基础配置
* @param {Object} merge |将多个 webpack 配置文件合并成一个
*/
const getBaseCfg = require("./webpack.base");
const { merge } = require("webpack-merge");
const path = require("path");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = merge(getBaseCfg(false), {
optimization: {
minimizer: [
new CssMinimizerPlugin({
parallel: true, // 并行压缩
}),
],
},
});
JS 压缩
bash
pnpm add -D terser-webpack-plugin
javascript
/**
* @fileName scripts/webapck.dev.js
* @description development|开发环境
* @param getBaseCfg |使用基础配置
* @param {Object} merge |将多个 webpack 配置文件合并成一个
*/
const getBaseCfg = require("./webpack.base");
const { merge } = require("webpack-merge");
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = merge(getBaseCfg(false), {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true, // 并行压缩
terserOptions: {
compress: {
pure_funcs: ["console.log", "console.warn"], //去除
},
},
}),
],
},
});
代码分离
将代码分离到不同的 bundle 中,之后可以按需加载,或者并行加载这些文件, 官方详情
javascript
/**
* @fileName scripts/webapck.dev.js
* @description development|开发环境
* @param getBaseCfg |使用基础配置
* @param {Object} merge |将多个 webpack 配置文件合并成一个
*/
const getBaseCfg = require("./webpack.base");
const { merge } = require("webpack-merge");
const path = require("path");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = merge(getBaseCfg(false), {
optimization: {
splitChunks: {
// 缓存配置
chunks: "async",
minSize: 20000,
minChunks: 1,
cacheGroups: {
vendors: {
priority: 1,
test: /node_modules/,
name: "vendors",
},
commons: {
name: "commons",
minChunks: 3,
},
},
},
},
});
其他优化配置待后续更新...
总结
本文通过对 webpack 的基本配置了解到其Loader,Plugin的配置。
- 通过
clean-webpack-plugin
实现样式独立抽取; - 通过
html-webpack-plugin
将压缩的JS注入到独立的THML文档; - 同时结合webpack5 的最方法
assets
实现对静态资源的加载。为了确保JS最新的特性代码在不同浏览器和环境中兼容还配置了loader-babel
JS解析方法。
在了解的基础案例配置的基础上,结合了React框架实现了基本开发配置
- 其实现了开发中常见的环境变量配置
- 并通过环境变量优化了不同环境对CSS的运用
- 于此同时针对打包速度与包的体积大小做了相关优化处理