# webpack 基础
环境:webpack 5, node.js 16+
# 1. 前言
# 1.1. 为什么需要打包工具?
开发时,我们会使用框架(React、Vue)、ES6(语法、API)、CSS 预处理器(Less/Sass/Stylus)等等 进行开发。
这样的代码要想在浏览器运行,必须将其编译成浏览器能识别的 JS、CSS 文件。
这些重复繁琐的工作交给构建工具正好合适。
除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。
# 1.2. 有哪些打包工具?
- Grunt
- Gulp
- Parcel
- webpack
- Rollup
- Vite
- ...
目前市面上最流行的是 webpack,所以我们主要以 webpack 来介绍使用打包工具
# 2. 基本使用
webpack 是一个静态资源打包工具。
它会以一个或多个文件作为打包的入口,将我们整个项目所有涉及的文件编译组合成一个或多个文件。
webpack 输出的文件叫做 bundle,可以在浏览器端运行。
# 2.1. 功能介绍
webpack 本身功能是有限的:
- 开发模式:只能处理
ES Module
- 生产模式:处理
ES Module
,压缩 JS 代码
# 2.2. 开始使用
# 2.2.1. 资源目录
webpack_code # 项目根目录(所有指令必须在这个目录运行)
└── src # 项目源码目录
│ ├── js # js文件目录
│ │ ├── subtract.js
│ │ └── sum.js
│ └── main.js # 项目主文件
└── public
├── index.html
# 2.2.2. 创建文件
// subtract.js:
export default function (num1, num2) {
return num1 - num2;
}
// sum.js:
export default (...nums) => {
return nums.reduce(
(prevRes, curr) => (prevRes + curr), 0
);
};
// main.js:
import sum from "./js/sum";
import subtract from "./js/subtract";
console.log( sum(1, 2, 3) );
console.log( subtract(10, 9) );
index.html:
<h1>hello webpack</h1>
<script src="../dist/main.js"></script>
# 2.2.3. 下载依赖
打开终端,切换到项目根目录
创建
package.json
文件npm init -y
package.json
中name
字段不能叫做webpack
, 否则会报错
安装依赖
npm i webpack webpack-cli -D
- 安装包及版本:
"webpack": "^5.79.0", "webpack-cli": "^5.0.1"
# 2.2.4. 启用 webpack
## 开发模式:
npx webpack ./src/main.js --mode=development
## 生产模式:
npx webpack ./src/main.js --mode=production
说明:
npx webpack
: 是用来运行本地安装 webpack 包的。./src/main.js
: 指定 webpack 从main.js
文件开始打包。--mode=development
:指定模式(环境)。
默认 webpack
会将文件打包输出到 dist
目录下
# 2.3. 小结
webpack
本身功能比较少,只能处理 js
资源,一旦遇到 css
等其他资源就会报错。
我们学习 webpack
,就是主要学习如何处理其他资源。
# 3. 基本配置
# 3.1. 五大核心概念
核心 | 说明 |
---|---|
entry(入口) | 指示 webpack 从哪个文件开始打包 |
output(输出) | 指示 webpack 打包完的文件输出到哪里去,如何命名等 |
loader(加载器) | webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,webpack 才能解析 |
plugins(插件) | 扩展 webpack 的功能 |
mode(模式) | 开发模式,development ;生产模式,production |
# 3.2. webpack 配置文件
默认位置: 项目根目录
默认名称: webpack.config.js
简单配置:
const path = require("path");
module.exports = {
// 入口:相对路径和绝对路径都行
entry: "./src/main.js",
// 输出
output: {
// path: 文件输出目录,必须是绝对路径
path: path.resolve(__dirname, "dist"),
// filename: 输出文件名
filename: "main.js",
},
// 加载器
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
mode: "development", // 开发模式
};
运行指令:
npx webpack
# 4. 处理样式资源
css、less、sass、scss、styl
说明:webpack 不能识别样式文件,需要借助 loader 来解析
参考:webpack 官方 loader 文档 (opens new window)
安装包及版本:
"css-loader": "^6.7.3",
"less-loader": "^11.1.0",
"sass": "^1.62.0",
"sass-loader": "^13.2.2",
"style-loader": "^3.3.2",
"stylus-loader": "^7.1.0",
# 4.1. css
安装:
# 把 CSS 插入到 DOM 中。
npm i -D style-loader
# css-loader: 将 css 文件编译成 webpack 能识别的模块(cjs)
npm i -D css-loader
配置:
module.rules = [
{
test: /\.css$/, // 匹配:文件名以 .css 结尾的
use: ["style-loader", "css-loader"], // loader 执行顺序是从右到左
},
// ...
]
参考:css-loader —— webpack5 (opens new window)
# 4.2. less
安装:
# 将 less 文件编译成 css 文件
npm i -D less-loader
配置:
module.rules = [
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
}
// ...
];
参考:less-loader —— webpack5 (opens new window)
# 4.3. sass、scss
安装:
# sass-loader 依赖 sass
npm i -D sass
# 将 sass 文件编译成 css 文件
npm i -D sass-loader
配置:
module.rules = [
{
test: /\.s[ac]ss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
// ...
]
参考:sass-loader —— webpack5 (opens new window)
# 4.4. styl
安装:
# 将 styl 文件编译成 css 文件
npm i -D stylus-loader
配置:
module.rules = [
{
test: /\.styl$/,
use: ["style-loader", "css-loader", "stylus-loader"],
}
// ...
]
参考:stylus-loader —— webpack5 (opens new window)
# 5. 处理图片资源
webpack 4 处理资源,需要以下 loader:
raw-loader
: to import a file as a stringurl-loader
: to inline a file into the bundle as a data URIfile-loader
: to emit a file into the output directory
webpack 5 已内置上面的 loader,只需要配置即可:
asset/resource
: emits a separate file and exports the URL. Previously achievable by using file-loader.asset/inline
: exports a data URI of the asset. Previously achievable by using url-loader.asset/source
: exports the source code of the asset. Previously achievable by using raw-loader.asset
: automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using url-loader with asset size limit.
配置:
module.rules = [
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
/*
小于 10kb 的图片会转成 base64
优点:减少 HTTP 请求。
缺点:体积会增大一点点
*/
maxSize: 10 * 1024,
}
}
},
// ...
],
备注:
- 就算不配置,webpack 默认会使用
asset/resource
进行处理
参考:asset-modules —— webpack5 (opens new window)
# 6. 修改输出资源的名称和路径
打包后,dist 目录的结构:
├── dist
└── static
├── imgs
│ └── 7003350e.png
└── js
└── main.js
配置:
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
/*
将 js 文件输出到 static/js 目录中
*/
filename: "static/js/main.js",
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: { dataUrlCondition: { maxSize: 10 * 1024 } },
generator: {
/*
将图片文件输出到 static/imgs 目录中
[hash:8]: hash 值取前 8 位
[ext]: 使用之前的文件扩展名
[query]: 添加之前的query参数
*/
filename: "static/imgs/[hash:8][ext][query]",
},
},
],
},
};
# 7. 自动清空上次打包资源
配置:
module.exports = {
output: {
/*
Clean the output directory before emit.
在生成文件之前清空 output.path 目录
*/
clean: true,
}
}
参考:output.clean (opens new window)
# 8. 处理字体图标资源
配置:
module.rules = [
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource", // 原封不动的输出
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
// ...
]
# 9. 处理其他资源
不需要对内容进行特殊处理的资源,原封不动输出即可,如:
- 音频:mp3, wma
- 视频:mp4, avi
- 文档:Excel, Word, PDF, ...
与 “处理字体图标资源” 的方式一样:
module.rules = [
{
test: /\.(ttf|woff2?|mp4|mp3|avi)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
// ...
]
备注:
- 媒体资源一般存放在服务器,不需要前端处理
# 10. 处理 JS 资源
webpack 只能处理 JS 的模块化语法(ESM、CommonJS),我们需要:
- babel: 将高版本 ES 语法 转为 低版本 ES 语法
- eslint: 语法风格检查
# 10.1. eslint
# 10.1.1. 配置文件
有两种方式:
创建额外的配置文件
- .eslintrc
- .eslintrc.json
- ...
- .eslintrc.js
在 package.json 的
eslintConfig
属性中进行配置{ "name": "my-package", "version": "1.0.0", "eslintConfig": { "extends": [], "env": {}, "parserOptions": {}, "rules": {}, } }
# 10.1.2. 具体配置
.eslintrc.js:
module.exports = {
// 继承其他规则
extends: [
'eslint:recommended',
],
/*
An environment provides predefined global variables.
https://eslint.org/docs/latest/use/configure/language-options
*/
env: {
browser: true, // browser global variables
node: true, // Node.js global variables and Node.js scoping.
es2022: true, // adds all ECMAScript 2022 globals and automatically sets the ecmaVersion parser option to 13.
jquery: false, // jQuery global variables
},
/*
Specifying Parser Options
https://eslint.org/docs/latest/use/configure/language-options#specifying-parser-options
*/
parserOptions: {
ecmaVersion: 'latest', // use the most recently supported version
sourceType: 'module', // your code is in ECMAScript modules
},
/*
Configure Rules
"off" or 0 - turn the rule off
"warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
"error" or 2 - turn the rule on as an error (exit code is 1 when triggered)
https://eslint.org/docs/latest/use/configure/rules
*/
rules: {
"eqeqeq": "warn",
},
};
# 10.1.3. 忽略文件
说明:
- 在 webpack 配置中已经指定了 eslint 的校验目录为 src 目录
- 忽略文件主要是给 IDE 用的,避免其对 dist 目录的文件进行校验
.eslintignore
:
dist/
参考:eslintignore的忽略配置 (opens new window)
# 10.1.4. 在 webpack 中使用
说明:(启用 eslint)
- webpack 4 中使用的是 loader
- webpack 5 中使用的是 plugin
安装:
npm i -D eslint-webpack-plugin eslint
## "eslint-webpack-plugin": "^4.0.1",
## "eslint": "^8.38.0",
配置:
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
module.exports = {
// ...
plugins: [
new ESLintWebpackPlugin({
// A string indicating the root of your files.
context: path.resolve(__dirname, "src"),
// Will cause the module build to fail if there are any errors, default true
failOnError: false,
// Specify the files and/or directories to exclude. Must be relative to options.context.
exclude: 'node_modules', // Default: 'node_modules'
}),
],
}
参考:eslint-webpack-plugin (opens new window)
# 10.2. babel
# 10.2.1. 配置文件
有两种方式:
- 创建额外的配置文件
- .babelrc
- .babelrc.js
- .babelrc.json
- babel.config
- babel.config.json
- babel.config.js
在 package.json 的
babel
属性中进行配置{ "name": "my-package", "version": "1.0.0", "babel": { "presets": [ ... ], "plugins": [ ... ], } }
# 10.2.2. 具体配置
babel.config.js:
module.exports = {
/*
预设:一组 babel 插件,扩展 babel 功能,类比 eslint 的 extends 属性
Babel presets can act as sharable set of Babel plugins and/or config options.
Official Presets:
@babel/preset-env for compiling ES2015+ syntax
@babel/preset-typescript for TypeScript
@babel/preset-react for React
@babel/preset-flow for Flow
*/
presets: [ '@babel/preset-env' ],
plugins: []
};
# 10.2.3. 在 webpack 中使用
安装:
npm i -D babel-loader @babel/core @babel/preset-env
## "babel-loader": "^9.1.2",
## "@babel/core": "^7.21.4",
## "@babel/preset-env": "^7.21.4",
配置:
module.rules = [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
},
// ...
]
参考:babel-loader (opens new window)
# 11. 处理 Html 资源
作用:
- 根据模板生成 HTML 文件
- 自动注入 bundle(打包后的文件)
安装:
npm i -D html-webpack-plugin
## "html-webpack-plugin": "^5.5.1",
配置:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// ...
plugins: [
// ...
new HtmlWebpackPlugin({
/*
指定模板位置
*/
template: path.resolve(__dirname, "public/index.html"),
}),
]
};
参考:html-webpack-plugin (opens new window)
# 12. 开发服务器
说明:
- 启用开发服务器后,所有代码都会在内存中编译打包,不会输出到 dist 目录
- 源代码一旦被修改,就会重新编译打包
安装:
npm i -D webpack-dev-server
## "webpack-dev-server": "^4.13.3"
配置:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// ...
devServer: {
host: "localhost",
port: "3000",
open: true, // 自动打开浏览器
},
};
运行:
npx webpack serve
参考:dev-server (opens new window)
# 13. 区分模式
# 13.1. 介绍
开发模式:
- 启用 dev-server,不需要把编译结果输出到 dist
生产模式:
- 优化代码运行性能
所以,我们需要准备两套配置文件
# 13.2. 目录
${root}/
config/
webpack.dev.js
webpack.prod.js
备注:
- 移动 webpack 配置文件后,相对路径不用改,绝对路径需要修改
# 13.3. webpack.dev.js
module.exports = {
mode: "development",
devServer: {
host: "localhost",
port: "3000",
open: true,
},
// ...
}
# 13.4. webpack.prod.js
module.exports = {
mode: "production",
// ...
}
# 13.5. 运行指令
package.json:
{
"scripts": {
"start": "npm run dev",
"dev": "webpack serve --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
}
}
备注:
- 使用 npm script,
.bin
目录也会加入环境变量,因此 npx 也可以省略掉
# 14. css 优化
在生产环境下,对 css 的处理进行优化
# 14.1. 提取成单独文件
问题:
- 使用
style-loader
,所有的样式都在 JS 里 - 要等到 JS 下载完,执行 JS 的时候才会将 CSS 插入页面中
- 这会造成闪屏的问题(先出现 HTML 结构,再设置样式)
解决:
- 在
<head>
使用<link>
提前引入所有样式
安装:
npm i -D mini-css-extract-plugin
## "mini-css-extract-plugin": "^2.7.5",
配置:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.rules = [
// ...
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
},
]
plugins = [
// ...
new MiniCssExtractPlugin({
// 指定输出文件名
filename: "static/css/main.css",
}),
]
参考:mini-css-extract-plugin (opens new window)
# 14.2. 兼容性处理
说明:
- 解决样式兼容问题
- 添加浏览器厂商前缀
安装:
npm i -D postcss-loader postcss postcss-preset-env
## "postcss": "^8.4.23",
## "postcss-loader": "^7.2.4",
## "postcss-preset-env": "^8.3.2",
配置:
module.rules = [
// ...
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: { plugins: [ "postcss-preset-env", ] },
},
},
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: { plugins: [ "postcss-preset-env", ] },
},
},
"less-loader"
],
},
]
在 package.json
中设置兼容哪些浏览器:
{
"browserslist": [
"last 2 version", // 最近的两个版本
"> 1%", // 覆盖 99% 的浏览器
"not dead" // “死掉”的浏览器和版本就不考虑了
]
}
优化代码:(抽取重复代码)
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env",
],
},
},
},
preProcessor,
].filter(Boolean); // Boolean(undefined) === false
};
module.rules = [
// ...
{ test: /\.css$/, use: getStyleLoaders() },
{ test: /\.less$/, use: getStyleLoaders('less-loader') },
{ test: /\.s[ac]ss$/, use: getStyleLoaders('sass-loader') },
{ test: /\.styl$/, use: getStyleLoaders('stylus-loader') },
]
参考:
# 14.3. 压缩
安装:
npm i -D css-minimizer-webpack-plugin
## "css-minimizer-webpack-plugin": "^5.0.0",
配置:
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
plugins = [
// ...
new CssMinimizerPlugin(),
]
// 也可以配置在 optimization 中,建议配置 plugins 中
optimization.minimizer = [
// ...
new CssMinimizerPlugin(),
]
参考:css-minimizer-webpack-plugin (opens new window)
# 15. HTML、JS 压缩
默认生产模式已经开启了:HTML 压缩和 JS 压缩
不需要额外进行配置
# 16. 总结
两种开发模式
- 开发模式:代码能编译自动化运行
- 生产模式:代码编译优化输出
webpack 基本功能
- 开发模式:可以编译 ES Module 语法
- 生产模式:可以编译 ES Module 语法,压缩 js 代码
webpack 配置文件
- 五个核心概念
- entry
- output
- loader
- plugins
- mode
- devServer 配置
- 五个核心概念
webpack 脚本指令用法
webpack
直接打包输出webpack serve
启动开发服务器,内存编译打包没有输出
上一篇: 下一篇: