webpack
一、为什么需要打包工具?
开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。
这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、Css 等语法,才能运行。
所以我们需要打包工具帮我们做完这些事。
除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。
Webpack 本身功能是有限的:
- 开发模式:仅能编译 JS 中的
ES Module
语法 - 生产模式:能编译 JS 中的
ES Module
语法,还能压缩 JS 代码
二、有哪些打包工具?
- Grunt
- Gulp
- Parcel
- Webpack
- Rollup
- Vite
- …
目前市面上最流量的是 Webpack,所以我们主要以 Webpack 来介绍使用打包工具
三、webpack基本使用
打开终端,来到项目根目录。运行以下指令:
初始化package.json
npm init -y
此时会生成一个基础的 package.json
文件。
需要注意的是 package.json
中 name
字段不能叫做 webpack
, 否则下一步会报错
下载依赖
- 执行webpack命令,会执行node_modules下的.bin目录下的webpack;
- webpack在执行时是依赖webpack-cli的,如果没有安装就会报错;
- 而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程;
- 所以在安装webpack时,我们需要同时安装webpack-cli
npm i webpack webpack-cli -D
- 资源目录
webpack_code # 项目根目录(所有指令必须在这个目录运行)
└── src # 项目源码目录
├── js # js文件目录
├── app.js
└── m1.js
- 创建文件
app.js
import m1 from './m1';
console.log(m1)
m1.js
export default {
a:1,
b:2
}
启用 Webpack
- 开发模式
npx
是 node 内置的命令,用于解决命令全局安装的问题
即使用 npx
调用当前项目依赖版本
的命令,即去当前项目的 node_modules/.bin
下找 webpack.cmd
命令
npx webpack ./src/js/app.js --mode=development
- 生产模式
npx webpack ./src/js/app.js --mode=production
-
参数解释
-
npx webpack
: 是用来运行本地安装Webpack
包的。 -
./src/js/app.js
: 指定Webpack
从app.js
文件开始打包,不但会打包app.js
,还会将其依赖也一起打包进来。
-
--mode=xxx
:指定模式(环境)。
-
Webpack
本身功能比较少,只能处理 js和json
资源,一旦遇到 css
等其他资源就会报错。
所以我们学习 Webpack
,就是主要学习如何处理其他资源,可以给webpack安装插件。
四、基本配置
虽然之前我们已经可以使用npx命令来对js文件做打包,但是在实际开发项目中大多数都是通过配置文件完成打包
在开始使用 Webpack
之前,我们需要对 Webpack
的配置有一定的认识,
在真正开发项目构建的时候很少使用命令,一般都是通过设置不同环境的配置文件。
- 开发配置文件: config/webpack.dev.js
- 生产配置文件: config/webpack.prod.js
4.1 五大核心概念
- entry(入口)
指示 Webpack 从哪个文件开始打包
- output(输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等
- loader(加载器)
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
- plugins(插件)
扩展 Webpack 的功能,例如在dist目录中添加项目资源(img、css)目录以及index.html文件
- mode(模式)
主要由两种模式:
- 开发模式:development
- 生产模式:production
->无模式:none
4.2 开发模式介绍
开发模式顾名思义就是我们开发代码时使用的模式。
这个模式下我们主要做两件事:
- 编译代码,使浏览器能识别运行( babel , browserify )
开发时我们有样式资源、字体图标、图片资源、html 资源等
webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源
- 代码质量检查,树立代码规范
类似于gulp中的jshint,在webpack中是eslint
提前检查代码的一些隐患,让代码运行时能更加健壮。
提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观。
4.3 准备 Webpack 配置文件
在config目录下新建文件:webpack.config.js
webpack_code # 项目根目录(所有指令必须在这个目录运行)
└── config #webpack的配置文件
├── webpack.config.js
Webpack 是基于 Node.js 运行的,所以采用 Common.js 模块化规范
module.exports = {
// 入口
entry: "",
// 输出
output: {},
// 加载器(loader)
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
mode: "",
};
4.4 修改配置文件
- 配置文件
// Node.js的核心模块,专门用来处理文件路径
const path = require("path");
module.exports = {
// 入口
// 相对路径和绝对路径都行
entry: "./src/app.js",
// 输出
output: {
// path: 文件输出目录,必须是绝对路径
// path.resolve()方法返回一个绝对路径
// __dirname 当前文件的文件夹绝对路径
path: path.resolve(__dirname, "../dist"),
// filename: 输出文件名
filename: "bundle.js",
},
// 模式
mode: "development", // 开发模式
};
- 运行指令(在项目根目录中运行)
npx webpack --config ./config/webpack.config.js
# npx 运行局部安装的包命令
# --config 指定要运行的配置文件
# ./config/webpack.config.js 配置文件的相对路径
虽然前面运行成功了,但是相对来讲运行命令的语句偏长,容易写错,所以我们可以使用之前的方式
在package.json中配置运行脚本
{
"scripts":{
"dev":"npx webpack --config ./config/webpack.config.js"
}
}
这样,我们后面运行的时候就可以变成npm run dev
啦。
Webpack 将来都通过 webpack.config.js
文件进行配置,来增强 Webpack 的功能
我们后面会以两个模式来分别搭建 Webpack 的配置,先进行开发模式,再完成生产模式
五、处理样式资源
Webpack 本身是不能识别样式资源
的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源
我们找 Loader 都应该去官方文档中找到对应的 Loader,然后使用官方文档找不到的话,
可以从社区 Github 中搜索查询Webpack 官方 Loader 文档
5.1 处理 Css 资源
- 下载包
注意:需要下载两个 loader【加载器】
npm i css-loader style-loader -D
-
功能介绍
-
css-loader
:负责将 Css 文件编译成 Webpack 能识别的模块 -
style-loader
:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容
-
注意:css-loader是将css搞成js引入页面,通过style-loader将样式以 Style 标签的形式在页面上生效。
<style>
div{color:red;}
</style>
- 配置
// 加载器(loader)
module:
{
rules:
[
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左,顺序不能变
use: ["style-loader", "css-loader"],
/*
* 1、先执行css-loader,将css写入到js文件中
* 2、在执行style-loader,在html页面中添加style标签
*/
}
]
}
- 添加 CSS 资源
src/css/index.css
.box1 {
width: 100px;
height: 100px;
background-color: pink;
}
src/app.js
// 引入 Css 资源,Webpack才会对其打包
import "../css/index.css";
public/index.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>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<!-- 准备一个使用样式的 DOM 容器 -->
<div class="box1"></div>
<!-- 引入打包后的js文件,才能看到效果 -->
<script src="../dist/main.js"></script>
</body>
</html>
- 运行指令
npm run dev
打开 index.html 页面查看效果
5.2 处理 Less 资源
- 下载包
npm i less-loader -D
- 功能介绍
less-loader
:负责将 Less 文件编译成 Css 文件
- 配置
module:
{
rules:
[
{
// 用来匹配 .less 结尾的文件
test: /\.less$/,
//执行顺序:仍然从右向左
use: ["style-loader", "css-loader", "less-loader"],
/*
* 1、先执行less-loader,将less转换成css
* 2、在执行css-loader,写到js文件中
* 3、最后执行style-loader,在页面中添加style标签
*/
}
]
}
- 添加 Less 资源
src/less/index.less
@color:deeppink;
@width:100px;
@height:100px;
.box2 {
width: @width;
height: @height;
background-color: @color;
}
src/main.js
// 引入资源,Webpack才会对其打包
import "./less/index.less";
public/index.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>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<div class="box1"></div>
<div class="box2"></div>
<script src="../dist/main.js"></script>
</body>
</html>
- 运行指令
npm run dev
打开 index.html 页面查看效果
六、处理图片资源
- 配置
module:
{
rules:
[
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
/*
* 在这里说一下,type类型中的asset,当使用的图片大于8KB,则按照图片路径来直接解析,
* 如果小于8KB,则会转换成base64格式的字符串,这其中记录着图片的信息,
* 好处是减少请求,信息直接读取。
* 那么在这里,你容易会疑问,那为什么不都转换成base64格式的字符串,还需要区分8KB之前之后呢?
* 原因在于转换成base64之后的文件会变大,一般用于小图,
* 所以才会有这个8KB的分水岭之说。
*/
}
]
}
-
添加图片资源
- src/images/vue.png
- src/images/react.png
-
使用图片资源
src/less/index.less
.box2 {
width: @width;
height: @height;
}
.vue {
width: 200px;
height: 200px;
background-image: url("../images/vue.png");
background-size: cover;
}
.react {
width: 200px;
height: 200px;
background-image: url("../images/react.png");
background-size: cover;
}
- 运行指令
npm run dev
打开 index.html 页面查看效果
此时如果查看 dist 目录的话,会发现多了图片资源
因为 Webpack 会将所有打包好的资源输出到 dist 目录下
为什么样式资源没有呢?
因为经过 style-loader
的处理,样式资源打包到 main.js 里面去了,所以没有额外输出出来
- 对图片资源进行优化
在前面咱们提到当小于8KB时,会被转化成Base64格式的字符串,在这里的8KB是可以灵活设置的
优点:减少请求数量缺点:体积变得更大
module:
{
rules:
[
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser:
{
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
}
}
]
}
- 修改输出资源的名称和路径
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位,可自定义,写几文件名就是几位的
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
//这个文件的路径是根据之前output中path里面的../dist目录判断的,相当于在dist目录下做配置
filename: "static/imgs/[hash:8][ext][query]",
}
}
]
}
修改 index.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>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
<div class="box4"></div>
<div class="box5"></div>
<!-- 修改 js 资源路径 -->
<script src="../dist/static/js/main.js"></script>
</body>
</html>
- 运行指令
npm run dev
- 此时输出文件目录:
├── dist
└── static
├── imgs
│ └── 7003350e.png
└── js
└── main.js
- 自动清空上次打包资源
module.exports = {
output: {
filename: "./static/js/bundle.js",
clean: true, // 自动将上次打包目录资源清空
}
};
- 运行指令
npm run dev
七、处理字体图标资源
下载字体图标文件,打开阿里巴巴矢量图标库,选择想要的图标添加到购物车,统一下载到本地
- 添加字体图标资源
在src目录下新建一个fonts目录
-
src/fonts/iconfont.ttf
-
src/fonts/iconfont.woff
-
src/fonts/iconfont.woff2
-
src/css/iconfont.css
注意css文件中的字体路径需要修改
src: url('../fonts/iconfont.woff2?t=1660919556256') format('woff2'),
url('../fonts/iconfont.woff?t=1660919556256') format('woff'),
url('../fonts/iconfont.ttf?t=1660919556256') format('truetype');
- src/main.js
// 引入资源,Webpack才会对其打包
import "./css/iconfont.css";
- public/index.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>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<!-- 使用字体图标 -->
<i class="iconfont icon-aixin"></i>
<i class="iconfont icon-dianzan"></i>
<i class="iconfont icon-hongbao"></i>
<script src="../dist/static/js/bundle.js"></script>
</body>
</html>
- 配置
由于新增了字体文件,所以需要针对字体文件的后缀要单独配置
module:
{
rules:
[
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
}
}
]
}
type: "asset/resource"
和type: "asset"
的区别:
-
type: "asset/resource"
相当于file-loader
, 将文件转化成 Webpack 能识别的资源,其他不做处理 -
type: "asset"
相当于url-loader
, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
-
运行指令
npm run dev
打开 index.html 页面查看效果
八、处理其他资源
开发中可能还存在一些其他资源,如音视频等,我们也一起处理了
8.1 音频和视频
- 配置
module: {
rules: [
{
test: /\.(ttf|woff2?|mp4|mp3|avi)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
}
}
]
}
- app.js引入
import "../video/123.mp4";
就是在处理字体图标资源基础上增加其他文件类型,统一处理即可
- 运行指令
npm run dev
引入标签,并打开 index.html 页面查看效果
<video src="../dist/static/media/0b28c161.mp4" controls></video>
8.2 处理 js 资源
有人可能会问,js 资源 Webpack 不能已经处理了吗,为什么我们还要处理呢?
原因是 Webpack 对 js 处理是有限的,只能编译 js 中 ES 模块化语法,不能编译其他语法,
导致 js 不能在 IE 等浏览器运行,所以我们希望做一些兼容性处理。
其次开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。
- 针对 js 兼容性处理,我们使用 Babel 来完成
- 针对代码格式,我们使用 Eslint 来完成
我们先完成 Eslint,检测代码格式无误后,在由 Babel 做代码兼容性处理
九、Eslint
可组装的 JavaScript 和 JSX 检查工具。
这句话意思就是:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能
我们使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,
将来运行 Eslint 时就会以写的规则对代码进行检查
- 配置文件
配置文件由很多种写法:
.eslintrc.*`:新建文件,位于项目根目录
.eslintrc:.eslintrc.js
package.json 中 eslintConfig:不需要创建文件,在原有文件基础上写
ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
- 具体配置
配置的基本结构如下:
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
我们以 .eslintrc
配置文件为例
{
"parserOptions": {
"ecmaVersion": 6, // 支持es6
"sourceType": "module", // 使用es6模块化
"ecmaFeatures": { // ES 其他特性
"jsx": true // 如果是 React 项目,就需要开启 jsx 语法
}
}
"parser":"@babel/eslint-parser", // 需要安装 npm i @babel/eslint-parser -D
"env": { // 设置环境
"browser": true, // 支持浏览器环境: 能够使用window上的全局变量
"node": true // 支持服务器环境: 能够使用node上global的全局变量
},
"rules": { // eslint检查的规则 0 忽略 1 警告 2 错误
"no-console": 0, // 不允许出现 console
"eqeqeq": 0, // 必须使用 ===
"no-alert": 0, // 不能使用 alert
"semi": "error", // 禁止使用分号
},
"extends": "eslint:recommended" // 使用eslint推荐的默认规则
}
-
rules 具体规则
-
"off"
或0
- 关闭规则 -
"warn"
或1
- 开启规则,使用警告级别的错误:warn
(不会导致程序退出) -
"error"
或2
- 开启规则,使用错误级别的错误:error
(当被触发的时候,程序会退出)更多规则详见:规则文档
-
-
extends 继承
开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。
现有以下较为有名的规则:
- Eslint 官方的规则:
eslint:recommended
- Vue Cli 官方的规则:
plugin:vue/essential
- React Cli 官方的规则:
react-app
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends: ["react-app"],
rules: {
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq: ["warn", "smart"],
},
};
- 在 Webpack 中使用
下载包
npm i eslint-webpack-plugin eslint -D
定义 Eslint 配置文件:.eslintrc.js
module.exports = {
// 继承 Eslint 规则
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
},
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
};
- 修改 js 文件代码
main.js
var a = 10;
var b = 20;
console.log(a,b)
配置 webpack.config.js
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
}),
]
运行指令
npm run dev
在控制台查看 Eslint 检查效果
十、Babel
JavaScript 编译器,主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,
以便能够运行在当前和旧版本的浏览器或其他环境中
- 配置文件
配置文件由很多种写法:
babel.config.*
:新建文件,位于项目根目录babel.config.js
package.json
中babel
:不需要创建文件,在原有文件基础上写
Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
- 具体配置
我们以 babel.config.js
配置文件为例:
module.exports = {
// 预设
presets: [],
};
presets 预设
简单理解:就是一组 Babel 插件, 扩展 Babel 功能
@babel/preset-env
: 一个智能预设,允许您使用最新的 JavaScript。
@babel/preset-react
:一个用来编译 React jsx 语法的预设
@babel/preset-typescript
:一个用来编译 TypeScript 语法的预设
- 在 Webpack 中使用
下载包
npm i babel-loader @babel/core @babel/preset-env -D
定义 Babel 配置文件
babel.config.js
module.exports = {
presets: ["@babel/preset-env"],
};
修改 js 文件代码
main.js
let fn = ()=>{
console.log('我是fn');
}
fn();
配置 webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
],
}
};
运行指令
npm run dev
十一、处理 Html 资源
- 下载包
npm i html-webpack-plugin -D
- 配置
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
//指定html入口文件的模板
template: path.resolve(__dirname, "../public/index.html"),
//输出目录中html文件的名字
filename:'index.html',
//打包后的js放置的位置
inject:'body'
})
]
};
- 修改 index.html
去掉引入的 js 文件,因为 HtmlWebpackPlugin 会自动引入
<!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>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
<div class="box4"></div>
<div class="box5"></div>
<i class="iconfont icon-arrow-down"></i>
<i class="iconfont icon-ashbin"></i>
<i class="iconfont icon-browse"></i>
</body>
</html>
- 运行指令
npm run dev
此时 dist 目录就会输出一个 index.html 文件
十二、开发服务器&自动化&proxy代理
每次写完代码都需要手动输入指令才能编译代码,太麻烦了,我们希望一切自动化
- 下载包
npm i webpack-dev-server -D
- 配置
webpack.config.js
module.exports = {
// 开发服务器
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
// 当找不打开到匹配路径时,自动打开index.html文件
historyApiFallback: true,
//代理跨域
proxy: {
'/api': {
//转发的目标服务器是什么
target: 'http://localhost',
//真正发起请求的时候路径是什么
//当别名是/api,在路径重写的时候也是需要与之对应
pathRewrite: { '^/api': '' }
}
}
}
};
- 运行指令
npx webpack serve
# 配置文件运行
npx webpack serve --config ./config/webpack.config.js
注意运行指令发生了变化
并且当你使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。
开发时我们只关心代码能运行,有效果即可,至于代码被编译成什么样子,我们并不需要知道。
十三、生产模式介绍
生产模式是开发完成代码后,我们需要得到代码将来部署上线。
这个模式下我们主要对代码进行优化,让其运行性能更好。
优化主要从两个角度出发:
- 优化代码运行性能
- 优化代码打包速度
13.1 生产模式准备
我们分别准备两个配置文件来放不同的配置
13.2. 文件目录
├── webpack-test (项目根目录)
├── config (Webpack配置文件目录)
│ ├── webpack.dev.js(开发模式配置文件)
│ └── webpack.prod.js(生产模式配置文件)
├── node_modules (下载包存放目录)
├── src (项目源码目录,除了html其他都在src里面)
│ └── 略
├── public (项目html文件)
│ └── index.html
├── .eslintrc.js(Eslint配置文件)
├── babel.config.js(Babel配置文件)
└── package.json (包的依赖管理配置文件)
13.3. 修改 webpack.dev.js
const configProd = require('./webpack.prod');
module.exports = {
...configProd,
//模式
mode:'development',
//开启服务
devServer:{
//主机
host:'localhost',
//端口号
port:3000,
//自动打开浏览器
open:true
}
}
运行开发模式的指令:
npx webpack serve --config ./config/webpack.dev.js
13.4. 修改 webpack.prod.js
const path = require('path');
const eslintWebpackPlugin = require("eslint-webpack-plugin")
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//入口
entry:'./src/js/index.js',
//出口
output:{
path:path.resolve(__dirname,'../dist'),
filename:'js/bundle.js',
clean:true
},
//模式
mode:'development',
//加载器
module:{
rules:[
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左,顺序不能变
use: ["style-loader", "css-loader"],
/*
* 1、先执行css-loader,将css写入到js文件中
* 2、在执行style-loader,在html页面中添加style标签
*/
},
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser:
{
dataUrlCondition: {
maxSize: 5 * 1024 // 小于10kb的图片会被base64处理
}
},
generator: {
filename: "static/imgs/[hash:8][ext][query]",
}
},
{
test: /\.(ttf|woff2?|mp4|mp3|avi)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
}
},
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
]
},
//插件
plugins:[
new eslintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
}),
new htmlWebpackPlugin({
template:path.resolve(__dirname,'../public/index.html'),
filename:'index.html',
inject:'body'
})
]
}
运行生产模式的指令:
npx webpack --config ./config/webpack.prod.js
13.5. 配置运行指令
为了方便运行不同模式的指令,我们将指令定义在 package.json 中 scripts 里面
// package.json
{
// 其他省略
"scripts": {
"start": "npm run dev",
"dev": "npx webpack serve --config ./config/webpack.dev.js",
"build": "npx webpack --config ./config/webpack.prod.js"
}
}
以后启动指令:
- 开发模式:
npm start
或npm run dev
- 生产模式:
npm run build
十四、Css 处理
功能:提取 Css 成单独文件
Css 文件目前被打包到 bundle.js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式
这样对于网站来说,会出现闪屏现象,用户体验不好
我们应该是单独的 Css 文件,通过 link 标签加载性能才好
- 下载包
npm i mini-css-extract-plugin -D
- 配置 webpack.prod.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module:{
rules:[
{
test: /\.css$/,
//注意第一个参数使用miniCssExtractPlugin.loader替换
//css结尾文件、sass等结尾的文件都需要调整
use: [miniCssExtractPlugin.loader, "css-loader"],
},
]
}
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
]
};
- 运行指令
npm run build
十五、Css 兼容性处理
- 下载包
npm i postcss-loader postcss postcss-preset-env -D
- 配置 webpack.prod.js
module.exports = {
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
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
来控制样式的兼容性做到什么程度。
{
// 其他省略
"browserslist": ["ie >= 8"]
}
想要知道更多的 browserslist
配置,查看browserslist 文档
以上为了测试兼容性所以设置兼容浏览器 ie8 以上。
实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
{
// 其他省略
"browserslist": ["last 2 version", "> 1%", "not dead"]
}
如果写完并没有增加前缀,可以适当调整兼容版本数量以及包括旧版的浏览器
{
// 其他省略
"browserslist": ["last 20 version", "> 1%"]
}
- 合并配置
webpack.prod.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
preProcessor,
].filter(Boolean);
};
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
clean: true,
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
],
},
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
}),
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
],
// devServer: {
// host: "localhost", // 启动服务器域名
// port: "3000", // 启动服务器端口号
// open: true, // 是否自动打开浏览器
// },
mode: "production",
};
- 运行指令
npm run dev
十六、Css 压缩
- 下载包
npm i css-minimizer-webpack-plugin -D
webpack.prod.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
plugins: [
// css压缩
new CssMinimizerPlugin(),
]
};
- 运行指令
npm run dev
十七、html 压缩
默认生产模式已经开启了:html 压缩和 js 压缩
不需要额外进行配置
十八、提升开发体验
- SourceMap
开发时我们运行的代码是经过 webpack 编译后的,例如下面这个样子:
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/less/index.less":
/*!**********************************************************************************************************!*\
!*** ./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/less/index.less ***!
\**********************************************************************************************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".box2 {\\n width: 100px;\\n height: 100px;\\n background-color: deeppink;\\n}\\n\", \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://webpack5/./src/less/index.less?./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js");
/***/ }),
// 其他省略
所有 css 和 js 合并成了一个文件,并且多了其他代码。
此时如果代码运行出错那么提示代码错误位置我们是看不懂的。
一旦将来开发代码文件很多,那么很难去发现错误出现在哪里。
所以我们需要更加准确的错误提示,来帮助我们更好的开发代码。
- SourceMap是什么
SourceMap(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案。
它会生成一个 xxx.map 文件,里面包含源代码和构建后代码每一行、每一列的映射关系。
当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错位置找到映射后源代码出错位置,
从而让浏览器提示源代码文件出错位置,帮助我们更快的找到错误根源。
通过查看Webpack DevTool 文档可知,SourceMap 的值有很多种情况.
但实际开发时我们只需要关注两种情况即可:
开发模式:cheap-module-source-map
- 优点:打包编译速度快,只包含行映射
- 缺点:没有列映射
module.exports = {
// 其他省略
mode: "development",
devtool: "cheap-module-source-map",
};
生产模式:source-map
- 优点:包含行/列映射
- 缺点:打包编译速度更慢
module.exports = {
// 其他省略
mode: "production",
devtool: "source-map",
};
十九、总结
本章节我们学会了 Webpack 基本使用,掌握了以下功能:
- 两种开发模式 (development, production)
- 开发模式:代码能编译自动化运行
- 生产模式:代码编译优化输出
- Webpack 基本功能
- 开发模式:可以编译 ES Module 语法
- 生产模式:可以编译 ES Module 语法,压缩 js 代码
- Webpack 配置文件
- 5 个核心概念
- entry
- output
- loader
- plugins
- mode
- devServer 配置
- Webpack 脚本指令用法
webpack
直接打包输出webpack serve
启动开发服务器,内存编译打包没有输出
webpak.prod.js
//导入模块
const path = require('path');
//导入安装的第三方的包
//单独抽离成独立的css文件
const miniCssExtractPlugin = require('mini-css-extract-plugin');
//css压缩的
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
//html文件的打包
const htmlWebpackPlugin = require('html-webpack-plugin');
//eslint代码检查包
const eslintWebpackPlugin = require('eslint-webpack-plugin');
// console.log(path.resolve('E:\\BJ0313\\day20\\代码\\webpack\\demo1', './abc'));
//__dirname:表示当前文件在定义时期所在的绝对路径:E:\\BJ0313\\day20\\代码\\webpack\\demo1\\config
// console.log(path.resolve(__dirname, '../dist'));
//向外暴露一个对象
module.exports = {
//webpack打包的时候有五大核心:
/*
* entry(打包入口文件地址)
* output(打包文件路径出口)
* mode(打包的模式)
* ----------------------
* 前面的三个属性可以单独为以.js结尾的文件进行打包
*
* 但是,如果文件结尾为.css/.less/.png/.jpg/.gif/.mp4/.woff....等等这些后缀
* 则需要模块以及插件来支持
* module(加载器)
* plugins(插件)
*/
//这个对象中的五个属性书写没有任何的顺序,但是一般情况下先从入口、出口、模块、插件、模式等这种顺序
//打包的入口文件地址是针对于运行终端来说,而非这个webpack.prod.js和src目录之间的关系!!!!
entry: './src/js/app.js',
//打包的出口
output: {
//打包后的文件夹路径
//path属性是语法,不能修改,后面的值是一个绝对路径
//E:\BJ0313\day20\代码\webpack\demo1\dist
//自动产生一个路径,需要借助于node中的内置模块:path
path: path.resolve(__dirname, '../dist'),
filename: 'js/bundle.js',
//每一次重新打包的时候都会将上一次的结果清除掉
clean: true,
//公共路径
publicPath: '/'
},
//打包模式
mode: 'production',
//加载器(loader)
module: {
//rules规则,这个rules后面是一个数组,里面存入的是对象形式的值,也就是说,有一个规则就配置一个对象
rules: [
//.css后缀的规则,那么就定义一个对象
{
//test是正则中的检测,后面的值写的是正则的规则
//其中[.]和\.的含义等同,目的都是为了将.这个字符转化成普通的字符.
//因为.在正则表达式中表示除了换行符以外的任意单个字符,保证用户在创建文件名的时候一定是xxx.css
test: /[.]css$/i,
//use后面到底是数组还是字符串,取决于使用的时候是一个加载器还是多个加载器
//如果是多个加载器的话,那么需要写成数组
//反之如果是一个加载器,则可以直接定义成字符串
//并且如果是数组的话,则数组的执行顺序是由右->左
//css-loader的作用主要是加载.css结尾的文件,并解析执行里面你的css语法,返回css语句
//style-loader的作用主要是在index.html的head标签里面添加一个style标签存入css语法
//也就是说,src/css文件夹里面有一个css文件,就把这个文件里面的语句在style标签中写一遍
//考虑到文件的数量和文件中内容的数量,这种形式不是很合适
// use: ['style-loader', 'css-loader']
//如果css代码偏多,文件偏多,最终会使得index.html文件内容偏多
//所以需要一个单独的插件包将所有开发时使用的css文件的内容抽离出来成一个单独的新的css文件
//这样的话就需要一个插件包:miniCssExtractPlugin
use: [miniCssExtractPlugin.loader, 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env'
],
],
},
},
}]
},
{
test: /\.less$/i,
//顺序:由右->左
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(gif|png|jpe?g|webp)$/i,
//如果某一个文件的后缀需要使用加载器,则是use配置属性, 如果是图片则直接是type类型
type: 'asset',
//默认是小于8KB会自动转化成base64,大于8KB的图片会参与打包,保存在dist目录中
//如果想要修改这种默认的图片大小,需要单独设置parser这个属性
parser:
{
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理,则不会打包进dist文件夹,这样就可以减少针对于图片的请求,也降低了图片保存在目录中的占位
}
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位,可自定义,写几文件名就是几位的
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
//这个文件的路径是根据之前output中path里面的../dist目录判断的,相当于在dist目录下做配置
filename: "static/imgs/[hash:8][ext][query]",
}
},
{
test: /\.(ttf|woff2?|mp4|avi|wmv)$/,
type: "asset/resource", //相当于执行了file-loader 将字体文件输出到dist打包目录中
generator: {
filename: "static/media/[hash:10][ext]",
}
},
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
{
//检测以.ejs结尾的文件
test: /\.ejs$/,
//使用ejs-loader加载器来检测
loader: 'ejs-loader',
//配置项
options: {
//ejs模板页面中的变量名
//可以自定义
variable: 'data'
}
},
]
},
//插件
plugins: [
new miniCssExtractPlugin({
//抽离的独立css文件名
//filename中的路径和文件名都可以自定义
filename: 'static/css/main.css'
}),
new CssMinimizerPlugin(),
new htmlWebpackPlugin({
//需要打包的文件路径是什么???
template: path.resolve(__dirname, '../public/index.html'),
//打包后的文件叫什么???
filename: 'index.html',
//是否将打包的bundle.js文件自动添加到打包的html文件里面,如果是,放在哪里???
inject: 'body'
}),
new eslintWebpackPlugin({
context: path.resolve(__dirname, '../src'),
})
],
//源代码和构建后的代码需要产生一个映射关系
devtool: 'source-map'
}
webpak.dev.js
//导入
const ProdConfig = require('./webpack.prod');
//暴露
module.exports = {
...ProdConfig,
//打包模式
mode: 'development',
//配置开发服务器
devServer: {
//主机地址:
host: 'localhost',
//端口号
port: 3000,
//是否自动打开浏览器
open: true
},
//源代码和构建后的代码需要产生一个映射关系
devtool: 'cheap-module-source-map'
}
{
"name": "frontend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "npx webpack server --config ./config/webpack.dev.js",
"build": "npx webpack --config ./config/webpack.prod.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^5.5.2",
"webpack": "^5.86.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"css-loader": "^6.8.1",
"ejs-loader": "^0.5.0",
"less-loader": "^11.1.3",
"style-loader": "^3.3.3"
},
"dependencies": {
"axios": "^1.4.0",
"sme-router": "^0.12.8"
}
}