webpack
本质上,webpack是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当webpack处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
静态模块:指的是编写代码过程中的,html,css,js,图片等固定内容的文件
为什么要使用webpack
开发时,我们会使用框架(React、Vue),ES6模块化语法,Less/sass 等 css 预处理器等语法进行开发。
这样的代码要想在浏览器运行必须经过编译成浏览器能识别的JS、Css 等语法,才能运行。
所以我们需要打包工具帮我们做完这些事。
除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。
使用webpack
资源目录
1 2 3 4 5 6
| webpack_code # 项目根目录(所有指令必须在这个目录运行) └── src # 项目源码目录 ├── js # js文件目录 │ ├── count.js │ └── sum.js └── main.js # 项目主文件
|
创建测试代码文件
根目录下创建src文件夹,创建如下两个js文件
1 2 3 4
| export function sub(x, y) { return x - y }
|
1 2 3 4
| export function sum(arr) { return arr.reduce((sum, item) => sum + item, 0) }
|
创建main.js文件
1 2 3 4 5 6
| import {sub} from './src/sub' import {sum} from './src/sum'
console.log(sub(2,1)) console.log(sum([1,2,3,4]))
|
下载相关依赖
打开终端,初始化package.json
需要注意的是 package.json
中 name
字段不能叫做 webpack
, 否则下一步会报错
安装webpack依赖
1
| npm i webpack webpack-cli -D
|
启用 Webpack
1
| npx webpack ./main.js --mode=development
|
1
| npx webpack ./main.js --mode=production
|
npx webpack
: 是用来运行本地安装 Webpack
包的。
./main.js
: 指定 Webpack
从 main.js
文件开始打包,不但会打包 main.js
,还会将其依赖也一起打包进来。
--mode=xxx
:指定模式(环境)。
观察输出文件
默认 Webpack
会将文件打包输出到 dist
目录下,我们查看 dist
目录下文件情况就好了
webpack的基本配置
五大核心概念
- entry(入口)
- output(输出)
- 指示 Webpack 打包完的文件输出到哪里去,如何命名等
- loader(加载器)
- webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
- plugins(插件)
- mode(模式)主要由两种模式:
- 开发模式:development
- 生产模式:production
配置文件
在项目根路径下创建配置文件webpack.config.js
,内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const path = require("path");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "main.js", }, module: { rules: [], }, plugins: [], mode: "development", };
|
运行打包命令
开发模式介绍
开发模式顾名思义就是我们开发代码时使用的模式。
这个模式下我们主要做两件事:
编译代码,使浏览器能识别运行
开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源
代码质量检查,树立代码规范
提前检查代码的一些隐患,让代码运行时能更加健壮。
提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观。
处理样式资源
建议去官方文件寻找loader使用
css-loader文档地址:css-loader | webpack 中文文档 | webpack中文文档 | webpack中文网 (webpackjs.com)
处理css资源
下载loader包
1
| npm i css-loader style-loader -D
|
功能介绍
css-loader
:负责将 Css 文件编译成 Webpack 能识别的模块
style-loader
:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| const path = require("path");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "main.js", }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, ], }, plugins: [], mode: "development", };
|
添加css资源
webpack会入口文件找引用了什么资源,将引用的资源进行打包
所以我们如果想要看到css资源被打包,需要在入口文件引入这个css
1 2 3 4 5
| .box { width: 100px; height: 100px; background-color: red; }
|
创建一个css文件,添加一些样式,然后在main.js中引入
执行打包命令
处理less资源
less-loader文档地址:less-loader | webpack 中文文档 | webpack中文文档 | webpack中文网 (webpackjs.com)
下载loader包
1
| npm install less less-loader --save-dev
|
功能介绍
less-loader
:负责将 Less 文件编译成 Css 文件
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| const path = require("path");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "main.js", }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, ], }, plugins: [], mode: "development", };
|
添加less资源
1 2 3 4 5
| .box2 { width: 100px; height: 100px; background-color: pink; }
|
执行打包命令
处理Sass Scss资源
下载包
1
| npm i sass-loader sass -D
|
功能介绍
sass-loader
:负责将 Sass 文件编译成 css 文件
sass
:sass-loader
依赖 sass
进行编译
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const path = require("path");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "main.js", }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, ], }, plugins: [], mode: "development", };
|
添加Sass Scss资源
1 2 3 4
| .box3 width: 100px height: 100px background-color: yellow
|
1 2 3 4 5
| .box4 { width: 100px; height: 100px; background-color: pink; }
|
执行打包命令
处理stylus资源
下载包
功能介绍
stylus-loader
:负责将 Styl 文件编译成 Css 文件
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| // Node.js的核心模块,专门用来处理文件路径 const path = require("path");
module.exports = { // 入口 // 相对路径和绝对路径都行 entry: "./main.js", // 输出 output: { // path: 文件输出目录,必须是绝对路径 // path.resolve()方法返回一个绝对路径 // __dirname 当前文件的文件夹绝对路径 path: path.resolve(__dirname, "dist"), // filename: 输出文件名 filename: "main.js", }, // 加载器 module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'],// 执行loader的顺序是从又往左 }, { test: /\.less$/i, // loader: "less-loader", loader这种配置方式只能配置一个加载器 // use这种配置方式可以配置多个 use: [ // compiles Less to CSS 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, ], }, // 插件 plugins: [], // 模式 mode: "development", // 开发模式 };
|
添加资源
1 2 3 4
| .box5 width 100px height 100px background-color beige
|
执行打包命令
处理图片资源
过去在 Webpack4 时,我们处理图片资源通过 file-loader
和 url-loader
进行处理
现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,我们只需要简单配置即可处理图片资源
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| const path = require("path");
module.exports = { entry: "./src/main.js", output: { path: path.resolve(__dirname, "dist"), filename: "main.js", }, module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"], }, { test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", }, ], }, plugins: [], mode: "development", };
|
输出资源情况输出资源情况
此时如果查看 dist 目录的话,会发现多了三张图片资源
因为 Webpack 会将所有打包好的资源输出到 dist 目录下
因为经过 style-loader
的处理,样式资源打包到 main.js 里面去了,所以没有额外输出出来
对图片资源进行优化对图片资源进行优化
将小于某个大小的图片转化成 data URI 形式(Base64 格式)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const path = require("path");
module.exports = { entry: "./src/main.js", output: { path: path.resolve(__dirname, "dist"), filename: "main.js", }, module: { rules: [ { test: /\.css$/, use: ["style-loader", "css-loader"], }, { test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } } }, ], }, plugins: [], mode: "development", };
|
此时输出的图片文件就只有两张,有一张图片以 data URI 形式内置到 js 中了 (注意:需要将上次打包生成的文件清空,再重新打包才有效果)
修改输出文件的目录
配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| const path = require("path");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "static/js/main.js", }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { filename: "static/imgs/[hash:8][ext][query]", } } ], }, plugins: [], mode: "development", };
|
打包自动清空上次打包资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| // Node.js的核心模块,专门用来处理文件路径 const path = require("path");
module.exports = { // 入口 // 相对路径和绝对路径都行 entry: "./main.js", // 输出 output: { // path: 文件输出目录,必须是绝对路径 // path.resolve()方法返回一个绝对路径 // __dirname 当前文件的文件夹绝对路径 path: path.resolve(__dirname, "dist"), // filename: 输出文件名 filename: "static/js/main.js", clean: true, // 自动将上次打包目录资源清空 }, // 加载器 module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'],// 执行loader的顺序是从又往左 }, { test: /\.less$/i, // loader: "less-loader", loader这种配置方式只能配置一个加载器 // use这种配置方式可以配置多个 use: [ // compiles Less to CSS 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "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]", } } ], }, // 插件 plugins: [], // 模式 mode: "development", // 开发模式 };
|
处理字体图标资源
添加字体资源
配置
type: "asset/resource"
和type: "asset"
的区别:
type: "asset/resource"
相当于file-loader
, 将文件转化成 Webpack 能识别的资源,其他不做处理
type: "asset"
相当于url-loader
, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| const path = require("path");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { filename: "static/imgs/[hash:8][ext][query]", } }, { test: /\.(ttf|woff2?)$/, type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, ], }, plugins: [], mode: "development", };
|
处理其他资源
在处理字体图标资源基础上增加其他文件类型,统一处理即可
处理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
.eslintrc.json
- 区别在于配置格式不一样
package.json
中 eslintConfig
:不需要创建文件,在原有文件基础上写
ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
具体配置
我们以 .eslintrc.js
配置文件为例:
1 2 3 4 5 6 7 8 9 10
| module.exports = { parserOptions: {}, rules: {}, extends: [], };
|
parserOptions 解析选项
1 2 3 4 5 6 7
| parserOptions: { ecmaVersion: 6, sourceType: "module", ecmaFeatures: { jsx: true } }
|
rules 具体规则
"off"
或 0
- 关闭规则
"warn"
或 1
- 开启规则,使用警告级别的错误:warn
(不会导致程序退出)
"error"
或 2
- 开启规则,使用错误级别的错误:error
(当被触发的时候,程序会退出)
1 2 3 4 5 6 7 8 9 10 11 12
| rules: { semi: "error", 'array-callback-return': 'warn', 'default-case': [ 'warn', { commentPattern: '^no default$' } ], eqeqeq: [ 'warn', 'smart' ], }
|
更多规则详见:规则文档open in new window
extends 继承
开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。
现有以下较为有名的规则:
1 2 3 4 5 6 7 8 9
| module.exports = { extends: ["react-app"], rules: { eqeqeq: ["warn", "smart"], }, };
|
在webpack中使用
下载包
1
| npm i eslint-webpack-plugin eslint -D
|
定义配置文件
.eslintrc.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| module.exports = { extends: ["eslint:recommended"], env: { node: true, browser: true, }, parserOptions: { ecmaVersion: 6, sourceType: "module", }, rules: { "no-var": 2, }, };
|
配置webpack
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { filename: "static/imgs/[hash:8][ext][query]", } }, { test: /\.(ttf|woff2?)$/, type: "asset/resource", generator: { filename: "static/media/[hash:8][ext][query]", }, }, ], }, plugins: [ new ESLintWebpackPlugin({ context: path.resolve(__dirname, "src"), }), ], mode: "development", };
|
vscode eslint插件
打开 VSCode,下载 Eslint 插件,即可不用编译就能看到错误,可以提前解决
但是此时就会对项目所有文件默认进行 Eslint 检查了,我们 dist 目录下的打包后文件就会报错。但是我们只需要检查 src 下面的文件,不需要检查 dist 下面的文件。
所以可以使用 Eslint 忽略文件解决。在项目根目录新建下面文件:
Babel
js编译器,主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中
配置文件
配置文件由很多种写法:
babel.config.*
:新建文件,位于项目根目录
babel.config.js
babel.config.json
.babelrc.*
:新建文件,位于项目根目录
.babelrc
.babelrc.js
.babelrc.json
package.json
中 babel
:不需要创建文件,在原有文件基础上写
Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
具体配置
我们以 babel.config.js
配置文件为例:
1 2 3 4
| module.exports = { presets: [], };
|
presets 预设
简单理解:就是一组 Babel 插件, 扩展 Babel 功能
@babel/preset-env
: 一个智能预设,允许您使用最新的 JavaScript。
@babel/preset-react
:一个用来编译 React jsx 语法的预设
@babel/preset-typescript
:一个用来编译 TypeScript 语法的预设
在 Webpack 中使用
下载包
1
| npm i babel-loader @babel/core @babel/preset-env -D
|
定义 Babel 配置文件
babel.config.js
1 2 3
| module.exports = { presets: ["@babel/preset-env"], };
|
配置webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { 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/, loader: "babel-loader", }, ], }, plugins: [ new ESLintWebpackPlugin({ context: path.resolve(__dirname, "./src"), }), ], mode: "development", };
|
处理HTML资源
下载包
1
| npm i html-webpack-plugin -D
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { 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/, loader: "babel-loader", }, ], }, plugins: [ new ESLintWebpackPlugin({ context: path.resolve(__dirname, "./src"), }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, "./public/index.html"), }), ], mode: "development", };
|
开发服务器&自动化
每次写完代码都需要手动输入指令才能编译代码,太麻烦了,我们希望一切自动化
下载包
1
| npm i webpack-dev-server -D
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, use: [ 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { 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/, loader: "babel-loader", }, ], }, plugins: [ new ESLintWebpackPlugin({ context: path.resolve(__dirname, "./src"), }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, "./public/index.html"), }), ], devServer: { host: "localhost", port: "3000", open: true, }, mode: "development", };
|
运行指令启动服务器
注意运行指令发生了变化
并且当你使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。
开发时我们只关心代码能运行,有效果即可,至于代码被编译成什么样子,我们并不需要知道。
生产模式介绍
生产模式是开发完成代码后,我们需要得到代码将来部署上线。
这个模式下我们主要对代码进行优化,让其运行性能更好。
优化主要从两个角度出发:
- 优化代码运行性能
- 优化代码打包速度
生产模式准备
我们分别准备两个配置文件来放不同的配置
1 2 3 4 5 6 7 8 9 10 11 12
| ├── 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 (包的依赖管理配置文件)
|
修改 webpack.dev.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| // Node.js的核心模块,专门用来处理文件路径 const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = { // 入口 // 相对路径和绝对路径都行 entry: "./main.js", // 输出 output: { // path: 文件输出目录,必须是绝对路径 // path.resolve()方法返回一个绝对路径 // __dirname 当前文件的文件夹绝对路径 path: undefined,// 开发模式没有输出,不需要指定目录 // filename: 输出文件名 filename: "static/js/main.js", // clean: true, // 开发模式没有输出,不需要清空 }, // 加载器 module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'],// 执行loader的顺序是从又往左 }, { test: /\.less$/i, // loader: "less-loader", loader这种配置方式只能配置一个加载器 // use这种配置方式可以配置多个 use: [ // compiles Less to CSS 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "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"), }), ], // 开发服务器 devServer: { host: "localhost", // 启动服务器域名 port: "3000", // 启动服务器端口号 open: true, // 是否自动打开浏览器 }, // 模式 mode: "development", // 开发模式 };
|
运行开发模式的指令
1
| npx webpack serve --config ./config/webpack.dev.js
|
修改 webpack.prod.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| // Node.js的核心模块,专门用来处理文件路径 const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = { // 入口 // 相对路径和绝对路径都行 entry: "./main.js", // 输出 output: { // path: 文件输出目录,必须是绝对路径 // path.resolve()方法返回一个绝对路径 // __dirname 当前文件的文件夹绝对路径 path: path.resolve(__dirname, "../dist"), // filename: 输出文件名 filename: "static/js/main.js", clean: true, // 自动将上次打包目录资源清空 }, // 加载器 module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader'],// 执行loader的顺序是从又往左 }, { test: /\.less$/i, // loader: "less-loader", loader这种配置方式只能配置一个加载器 // use这种配置方式可以配置多个 use: [ // compiles Less to CSS 'style-loader', 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.styl$/, use: ["style-loader", "css-loader", "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"), }), ], // 模式 mode: "production", // 开发模式 };
|
运行生产模式的指令
1
| npx webpack --config ./config/webpack.prod.js
|
配置运行指令
为了方便运行不同模式的指令,我们将指令定义在 package.json 中 scripts 里面
1 2 3 4 5 6 7 8 9
| { "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 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式
这样对于网站来说,会出现闪屏现象,用户体验不好
我们应该是单独的 Css 文件,通过 link 标签加载性能才好
下载包
1
| npm i mini-css-extract-plugin -D
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "../dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, { test: /\.less$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'less-loader', ], }, { test: /\.s[ac]ss$/, use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"], }, { test: /\.styl$/, use: [MiniCssExtractPlugin.loader, "css-loader", "stylus-loader"], }, { test: /\.(png|jpe?g|gif|webp)$/, type: "asset", parser: { dataUrlCondition: { maxSize: 10 * 1024 } }, generator: { 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/, loader: "babel-loader", }, ], }, plugins: [ new ESLintWebpackPlugin({ context: path.resolve(__dirname, "../src"), }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, "../public/index.html"), }), new MiniCssExtractPlugin({ filename: "static/css/main.css", }), ], mode: "production", };
|
运行命令
1
| npx webpack --config ./config/webpack.prod.js
|
css兼容性处理
下载包
1
| npm i postcss-loader postcss postcss-preset-env -D
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
| // Node.js的核心模块,专门用来处理文件路径 const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = { // 入口 // 相对路径和绝对路径都行 entry: "./main.js", // 输出 output: { // path: 文件输出目录,必须是绝对路径 // path.resolve()方法返回一个绝对路径 // __dirname 当前文件的文件夹绝对路径 path: path.resolve(__dirname, "../dist"), // filename: 输出文件名 filename: "static/js/main.js", clean: true, // 自动将上次打包目录资源清空 }, // 加载器 module: { rules: [ { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解决大多数样式兼容性问题 ], }, }, }, ],// 执行loader的顺序是从又往左 }, { test: /\.less$/i, // loader: "less-loader", loader这种配置方式只能配置一个加载器 // use这种配置方式可以配置多个 use: [ // compiles Less to CSS MiniCssExtractPlugin.loader, 'css-loader', { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解决大多数样式兼容性问题 ], }, }, }, 'less-loader', ], }, { test: /\.s[ac]ss$/, use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解决大多数样式兼容性问题 ], }, }, }, "sass-loader" ], }, { test: /\.styl$/, use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", // 能解决大多数样式兼容性问题 ], }, }, }, "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", }), ], // 模式 mode: "production", // 开发模式 };
|
合并配置
可以通过封装函数的方式来简化重复的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const getStyleLoaders = (preProcessor) => { return [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { postcssOptions: { plugins: [ "postcss-preset-env", ], }, }, }, preProcessor, ].filter(Boolean); };
module.exports = { entry: "./main.js", output: { path: path.resolve(__dirname, "../dist"), filename: "static/js/main.js", clean: true, }, module: { rules: [ { test: /\.css$/i, use: getStyleLoaders(), }, { test: /\.less$/i, 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 } }, generator: { 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/, loader: "babel-loader", }, ], }, plugins: [ new ESLintWebpackPlugin({ context: path.resolve(__dirname, "../src"), }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, "../public/index.html"), }), new MiniCssExtractPlugin({ filename: "static/css/main.css", }), ], mode: "production", };
|
设置兼容性
我们可以在 package.json
文件中添加 browserslist
来控制样式的兼容性做到什么程度。
1 2 3 4
| { "browserslist": ["ie >= 8"] }
|
想要知道更多的 browserslist
配置,查看browserslist 文档open in new window
以上为了测试兼容性所以设置兼容浏览器 ie8 以上。
实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
1 2 3 4
| { "browserslist": ["last 2 version", "> 1%", "not dead"] }
|
执行打包命令
CSS压缩
下载包
1
| npm i css-minimizer-webpack-plugin -D
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| // Node.js的核心模块,专门用来处理文件路径 const path = require("path"); const ESLintWebpackPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-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: "./main.js", // 输出 output: { // path: 文件输出目录,必须是绝对路径 // path.resolve()方法返回一个绝对路径 // __dirname 当前文件的文件夹绝对路径 path: path.resolve(__dirname, "../dist"), // filename: 输出文件名 filename: "static/js/main.js", clean: true, // 自动将上次打包目录资源清空 }, // 加载器 module: { rules: [ { test: /\.css$/i, use: getStyleLoaders(),// 执行loader的顺序是从又往左 }, { test: /\.less$/i, // loader: "less-loader", loader这种配置方式只能配置一个加载器 // use这种配置方式可以配置多个 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", }), // css压缩 new CssMinimizerPlugin() ], // 模式 mode: "production", // 开发模式 };
|
执行打包命令
html 压缩
默认生产模式已经开启了:html 压缩和 js 压缩
不需要额外进行配置