第一章 Webpack 简介?

🎉 什么是 Webpack?

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。 在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理。 它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

🎉webpack 五个核心概念?

Entry

入口(Entry):指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。

Output

输出(Output):指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

Loader

Loader:让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件(webpack 自身只理解 JS)

Plugins

插件(Plugins):可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,

一直到重新定义环境中的变量等。

Mode

模式(Mode):指示 webpack 使用相应模式的配置。

选项描述特点
development会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。能让代码本地调试运行的环境
production会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。能让代码优化上线运行的环境

第二章 Webpack 初体验?

🎉 初始化配置?

初始化 package.json:

1
$npm init

下载安装 webpack:

(webpack4 以上的版本需要全局/本地都安装 webpack-cli)

全局安装:

1
$cnpm i webpack webpack-cli -g

本地安装:

1
$cnpm i webpack webpack-cli -D

CNPM 介绍:
说明:因为谷歌安装插件是从国外服务器下载,受网络影响大,可能出现异常,如果谷歌的服务器在中国就好了,所以我们乐于分享的淘宝团队干了这事来自官网:“这是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10 分钟一次以保证尽量与官方服务同步“。
官方网址:http://npm.taobao.org
安装:命令提示符执行 npm install cnpm -g –registry=https://registry.npm.taobao.org
注意:安装完后最好查看其版本 cnpm -v 或关闭命令提示符重新打开,安装完直接使用有可能会出现错误

🎉 编译打包应用?

创建初始文件夹

输入入口:src 文件夹,在文件夹内新建 index.js。

输出出口:build 文件夹,作为输出文件的存放地址。

打包指令

开发环境:

webpack 会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js 整体打包环境,是开发环境。

1
2
3
$webpack ./src/index.js ^
-o ./build/built.js ^
--mode=development

生产环境:

webpack 会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js 整体打包环境,是生产环境。

1
2
3
$webpack ./src/index.js ^
-o ./build/built.js ^
--mode=production

注意:

1.webpack 本身能处理 js/json 资源,不能处理 css/img 等其他资源。

2.生产环境和开发环境将 ES6 模块化编译成浏览器能识别的模块化,

但是不能处理 ES6 的基本语法转化为 ES5(需要借助 loader)。

3.生产环境比开发环境多一个压缩 js 代码。

第三章 Webpack 开发环境的基本配置?

🎉Webpack 安装?

为了保障能够正常运行 Webpack,在开始项目前,可以在项目的根目录安装

🎉Webpack 定义配置文件?

webpack.config.js 是 webpack 的配置文件。

作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)

因此请将 webpack.config.js 放在打包文件的根目录里。

注意:所有构建工具都是基于 nodejs 平台运行的,模块化默认采用 commonjs。

基本的配置文件如下:

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
//resolve用来拼接绝对路径
const { resolve } = require("path");
module.exports = {
//webpack配置
//入口起点
entry: "./src/index.js",
//输出
output: {
//输出文件名
filename: "built.js",
//__dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, "build"),
},
//loader的配置
module: {
rules: [
//详细的loader配置
],
},
//plugins的配置
plugins: [
//详细的插件配置
],
mode: "development",
// mode:'production'
};

第四章 Webpack 打包资源?

🎉 打包前准备?

js 和 css 资源需要提前引入。

1
2
3
import "./index.less";
import "./index.css";
import "./xx.js";

🎉Webpack 打包 css/less 样式资源?

css 打包的方法

安装

使用前需要安装 style-loader 和 css-loader。

1
$cnpm i style-loader css-loader -D

修改配置

1
2
3
4
5
6
7
8
9
10
11
12
rules: [
//详细的loader配置
{ //通过正则表达式取到.css结尾的文件
test: /\.css$/,
//使用哪些loader
use:[
//use数组中loader的执行顺序:从右到左,从下到上依次执行
//创建一个style标签,将js中的样式资源插入进去,添加到页面中的head中生效
'style-loader',
//将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader',
]

less 打包的方法

安装

使用前需要安装 style-loader、css-loader、less@xxx 和 less-loader。

1
$cnpm i style-loader css-loader -D

修改配置

1
2
3
4
5
6
7
8
9
10
11
{//不同文件必须配置不同的loader处理
test: /\.less$/,
use:[
//创建一个style标签,将js中的样式资源插入进去,添加到页面中的head中生效
'style-loader',
//将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader',
//将less文件变为css文件
'less-loader',
]
}

🎉Webpack 打包 loader 与 plugins 的区别?

loader:1.下载 2.使用(配置 loader)

plugins:1.下载 2.引入 3.使用

🎉Webpack 打包 html 和图片资源?

html 打包的方法

安装

使用前需要安装 html-webpack-plugin。

1
$cnpm i html-webpack-plugin -D

引入

1
const HtmlWebpackPlugin = require("html-webpack-plugin");

修改配置

1
2
3
4
5
6
7
8
9
plugins: [
//默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
//需求:需要一个有结构的html文件
new HtmlWebpackPlugin({
//复制一个的html文件,自动引入打包输出的所有资源(js/css)
template:'./src/index.html'
})
//详细的插件配置
],

图片打包的方法

教程中的 4.0 版本的教学在最新 5.0 版本中会报错,需要在 output 中加上

解决报错

1
publicPath: "./";

安装

使用前需要安装 html-loader、file-loader 和 url-loader。

1
$cnpm i html-loader file-loader url-loader -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
//resolve用来拼接绝对路径
const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
//webpack配置
//入口起点
entry: "./src/index.js",
//输出
output: {
//输出文件名
filename: "built.js",
//__dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, "build"),
publicPath: "./",
},
//loader的配置
module: {
rules: [
{
test: /\.less$/,
//使用多个资源处理use
use: ["style-loader", "css-loader", "less-loader"],
}, //详细的loader配置
{
test: /\.(png|jpg|gif)$/,
//使用一个loader
loader: "url-loader",
//url-loader依赖file-loader
options: {
//图片大小小于8kb就会被base64处理
//优点:减少请求数量(减轻服务器压力)
//缺点:图片体积会变大(文件请求速度变慢)
limit: 8 * 1024,
//定义名字的哈希值
name: "[10].[ext]",
//问题:因为url-loader默认使用es6模块化解析,
//而 html-loader使用commonjs,因此这里要转化一下
esModule: false,
},
},
{
test: /\.html$/,
//处理html中的图片(负责引入img)
use: "html-loader",
},
],
},
//plugins的配置
plugins: [
//默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
//需求:需要一个有结构的html文件
new HtmlWebpackPlugin({
//复制一个的html文件,自动引入打包输出的所有资源(js/css)
template: "./src/index.html",
}),
//详细的插件配置
],
mode: "development",
// mode:'production'
};

🎉其他资源的打包的方法

安装

使用前需要安装 file-loader。

1
$cnpm i file-loader -D

修改配置

然后修改配置。

1
2
3
4
5
6
7
//当处理css、js、html之外的资源时
//可使用file-loader进行打包
{
//这里使用exclude进行排除
exclude: /\.(css|js|html)$/,
use:'file-loader'
}

🎉 开发服务器_devServer 配置?

配置中出现的问题:
webpack-dev-server 并没有兼容最新版的 webpack-cli
会报错 Error: Cannot find module ‘webpack-cli/bin/config-yargs’
因此需要卸载最新版的脚手架
采用符合 webpack-dev-server 依赖的版本

卸载之前的 webpack-cli

1
$npm uninstall webpack-cli

安装符合依赖的 webpack-cli(webpack-dev-server 最新版对应 3.3)

1
$npm install webpack-cli webpack-cli@3.3 -D

详细配置:

1
2
3
4
5
6
7
8
9
10
11
//用来自动化编译和hexo s差不多
//只会在内存中编译打包,不会有任何输出
//启动指令为:webpack-dev-server,需要下载对应的包
devServer:{
设置路径
contentBase:resolve(__dirname,'build'),
//是否使用gzip进行压缩
compress:true,
//服务端口号
port:3000
}

🎉 最终开发环境配置?

切记单一 loader 请用 loader 不要用 use,不然会报错!!!

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
//resolve用来拼接绝对路径
const { resolve } = require("path");
//引入'html-webpack-plugin'插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
//webpack配置
//入口起点
entry: "./src/js/index.js",
//输出
output: {
//输出文件名
filename: "js/built.js",
//__dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, "build"),
//公共路径为当前路径
publicPath: "./",
},
//loader的配置
module: {
//过滤条件
rules: [
//过滤css
{
test: /\.css$/,
//使用多个资源处理use
use: ["style-loader", "css-loader"],
},
//过滤less
{
test: /\.less$/,
//使用多个资源处理use
use: ["style-loader", "css-loader", "less-loader"],
},
//过滤图片
{
test: /\.(png|jpg|gif)$/,
//使用一个loader
loader: "url-loader",
//url-loader依赖file-loader
options: {
outputPath: "images",
//图片大小小于8kb就会被base64处理
//优点:减少请求数量(减轻服务器压力)
//缺点:图片体积会变大(文件请求速度变慢)
limit: 8 * 1024,
//定义名字的哈希值
name: "[10].[ext]",
//问题:因为url-loader默认使用es6模块化解析,
//而 html-loader使用commonjs,因此这里要转化一下
esModule: false,
// outputPath: 'images'
},
},
//过滤html
{
test: /\.html$/,
//处理html中的图片(负责引入img)
loader: "html-loader",
},
//过滤其他
//当处理css、js、html之外的资源时
//可使用file-loader进行打包
{
//这里使用exclude进行排除
exclude: /\.(gif|png|jpg|less|css|js|html)$/,
loader: "file-loader",
options: {
outputPath: "media",
},
},
],
},
//plugins的配置
plugins: [
//默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
//需求:需要一个有结构的html文件
new HtmlWebpackPlugin({
//复制一个的html文件,自动引入打包输出的所有资源(js/css)
template: "./src/index.html",
}),
//详细的插件配置
],
mode: "development",
// mode:'production'
//用来自动化编译和hexo s差不多
//只会在内存中编译打包,不会有任何输出
//启动指令为:webpack-dev-server,需要下载对应的包
devServer: {
//设置路径
contentBase: resolve(__dirname, "build"),
//是否使用gzip进行压缩
compress: true,
//服务端口号
port: 3000,
//自动打开浏览
open: true,
},
};

第五章 Webpack 生产环境的基本配置?

🎉 提取 css 成为单个文件?

安装插件

1
$cnpm i mini-css-extract-plugin -D

引入插件

1
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

插入插件

修改 ccs 使用的 loader

1
2
3
4
5
6
7
8
9
{
test: /\.css$/,
//使用多个资源处理use
use: [
//这里使用该插件的loader实现css从js中抽出
MiniCssExtractPlugin.loader,
'css-loader'
]
},

引入配置

通过修改 filename 修改 css 导出的路径。

1
2
3
new MiniCssExtractPlugin({
filename: "css/main.css",
});

🎉css 兼容性处理?

安装依赖插件

1
$cnpm i postcss-loader postcss-preset-env autoprefixer -D

配置项 webpack.config.js

1
2
3
4
5
6
/*
css兼容性处理:
postcss --》 postcss-loader postcss-preset-env
*/
//设置环境变量
process.env.NODE_ENV = "development";
1
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"];

postcss 配置

在 webpack.config.js 同级目录新建 postcss.config.js 配置项。

1
2
3
4
5
6
7
module.exports = {
plugins: [
//这里写你需要引入的插件
require("autoprefixer"), //处理前缀兼容问题
require("postcss-preset-env"), //适配不同浏览器环境
],
};

修改 package.json

1
2
3
4
5
6
7
8
9
10
11
12
"browserslist":{
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",//兼容大多数浏览器
"not dead",//去除死亡浏览器
"not op_mini all"//去除open—mini
]
}

🎉 压缩 css 文件?

安装插件

1
$cnpm i optimize-css-assets-webpack-plugin -D

插件引入

1
const OptimizeCssAssetWebpackPlugin = require("optimize-css-assets-webpack-plugin");

🎉js 语法检查?

安装插件

1
$cnpm i eslint eslint-loader -D

使用 airbnb 风格语言

1
$cnpm i eslint eslint-plugin-import eslint-config-airbnb-base -D

配置 package.json

1
2
3
"eslintConfig":{
"extends":"airbnb-base"
}

配置自动修复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{ //只检查自己写的代码,第三方库不用检查
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
enforce:'pre',
options: {
//设置检查规则
//在package.json中进行设置
//在eslintConfig中进行设置
//推荐使用airbnb规则
//为了使用它
//需要下载三个库 airbnb -》 eslint eslint-plugin-import eslint-config-airbnb-base
//fix:true自动修复
fix:true
}

忽略某一行(如 console.log)

加入注释,忽略下一行检查

1
//eslint-disable-next-line

🎉js 的基本兼容性问题?

安装插件

1
$cnpm i babel-loader @babel/preset-env @babel/core@^7.0.0-0 -D

新增配置

1
2
3
4
5
6
7
8
9
10
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
//预设:指示babel做哪些兼容处理
//不兼容高级语法,如promise
presets: ['@babel/preset-env']
}
},

🎉js 的全部兼容性问题处理?(非常暴力,但已弃用)

安装插件

1
$cnpm i @babel/pollyfill -D  //已经弃用

使用

1
import '@babel/pollyfill' //已经弃用

🎉js 的按需求处理兼容性问题?

安装插件

1
$cnpm i core-js -D

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
//预设:指示babel做哪些兼容处理
//这里是数组套数组的写法
presets: [['@babel/preset-env',
{//按需加载
useBuiltIns: 'usage',
//指定core-js的版本
corejs: {
version: 3,
},
//指定具体要做到某个浏览器的哪一个版本
targets: {
chrome: '60',
firefox: '60'
}
}]]
}
}

🎉js 和 html 的压缩处理?

js 压缩

将 mode 从 development 变为 production。

1
2
//mode: 'development',
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
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
149
150
151
152
153
154
155
156
157
158
159
160
161
//resolve用来拼接绝对路径
const { resolve } = require("path");
//引入'html-webpack-plugin'插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetWebpackPlugin = require("optimize-css-assets-webpack-plugin");
//设置环境变量
//process.env.NODE_ENV='development';
//复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
];
module.exports = {
//webpack配置
//入口起点
entry: "./src/js/index.js",
//输出
output: {
//输出文件名
filename: "js/built.js",
//__dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, "build"),
//公共路径为当前路径
publicPath: "./",
},
//loader的配置
module: {
//过滤条件
rules: [
//js兼容性处理
// 1.使用babel进行基本的兼容性处理
// 使用babel-loader @babel/preset-env @babel/core
{
test: /\.js$/,
exclude: /node_modules/,

loader: "babel-loader",
options: {
//预设:指示babel做哪些兼容处理
presets: ["@babel/preset-env"],
// presets: [['@babel/preset-env',
// {//按需加载
// useBuiltIns: 'usage',
// //指定core-js的版本
// corejs: {
// version: 3,
// },
// //指定具体要做到哪一个版本
// targets: {
// chrome: '60',
// firefox: '60'
// }
// }]]
},
},
{
//只检查自己写的代码,第三方库不用检查
test: /\.js$/,
exclude: /node_modules/,
loader: "eslint-loader",
//优先执行
enforce: "pre",
options: {
//设置检查规则
//在package.json中进行设置
//在eslintConfig中进行设置
//推荐使用airbnb规则
//为了使用它
//需要下载三个库 airbnb -》 eslint eslint-plugin-import eslint-config-airbnb-base
//fix:true自动修复
fix: true,
},
},
//过滤css
{
test: /\.css$/,
//使用多个资源处理use
use: [...commonCssLoader],
},
//过滤less
{
test: /\.less$/,
//使用多个资源处理use
use: [...commonCssLoader, "less-loader"],
},
//过滤图片
{
test: /\.(png|jpg|gif)/,
//使用一个loader
loader: "url-loader",
//url-loader依赖file-loader
options: {
//图片大小小于8kb就会被base64处理
//优点:减少请求数量(减轻服务器压力)
//缺点:图片体积会变大(文件请求速度变慢)
limit: 8 * 1024,
//定义名字的哈希值
name: "[10].[ext]",
//问题:因为url-loader默认使用es6模块化解析,
//而 html-loader使用commonjs,因此这里要转化一下
esModule: false,
outputPath: "images",
},
},
//过滤html
{
test: /\.html$/,
//处理html中的图片(负责引入img)
loader: "html-loader",
},
//过滤其他
//当处理css、js、html之外的资源时
//可使用file-loader进行打包
{
//这里使用exclude进行排除
exclude: /\.(gif|png|jpg|less|css|js|html)$/,
loader: "file-loader",
options: {
outputPath: "media",
},
},
],
},
//plugins的配置
plugins: [
//默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
//需求:需要一个有结构的html文件
new HtmlWebpackPlugin({
//复制一个的html文件,自动引入打包输出的所有资源(js/css)
template: "./src/index.html",
minify: {
//移除空格
collapseWhitespace: true,
//移除注释
removeComments: true,
},
}),
new MiniCssExtractPlugin({
filename: "css/main.css",
}),
new OptimizeCssAssetWebpackPlugin(),
//详细的插件配置
],
// mode: 'development',
mode: "production",
//用来自动化编译和hexo s差不多
//只会在内存中编译打包,不会有任何输出
//启动指令为:webpack-dev-server,需要下载对应的包
devServer: {
//设置路径
contentBase: resolve(__dirname, "build"),
//是否使用gzip进行压缩
compress: true,
//服务端口号
port: 3000,
//自动打开浏览
open: true,
},
};

第六章开发环境webpack 性能优化?

🎉 基本介绍?

主要分为开发环境性能优化和生产环境性能优化。

开发环境的优化目的:优化打包速度及优化代码调试

生产环境的优化目的:优化代码打包速度及优化代码的性能

🎉 打包速度优化-HMR?

webpack 在使用 webpack-dev-server 时,如果你修改了打包原始文件中任意一项,打包会重新调用所有模块资源。为了实现资源的模块化刷新功能,需要使用 HMR(Hot module repalcement)——热模块替换/模块热替换。
一个模块变化,只会打包这一个模块,而不是打包所有
帮我们极大提升构建速度

方法

只需要在 dev-server 处开启 hot:true 就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
devServer: {
//设置路径
contentBase: resolve(__dirname, 'build'),
//是否使用gzip进行压缩
compress: true,
//服务端口号
port: 3000,
//自动打开浏览
open: true,
//当修改了webpack配置记得重启webpack服务
//新配置要想生效必须重启webpack服务
hot:true
}

存在问题

CSS 样式文件:可以使用 HMR 功能,style-loader 内部实现

JS 文件:默认不能使用 HMR 功能 –》需要修改 js 代码,添加支持 mhr 功能的代码

HTML 文件:默认不能使用 HMR 功能,同时导致 html 文件不能热更新。在创建项目时只会创建 1 个 html 文件,当改变 js 和 css 时,html 一定会改,所以不需要 HMR。

解决策略

html 问题的解决措施为修改 entry 为数组并加入 html 文件,重启服务

1
2
//入口起点
entry: ["./src/js/index.js", "./src/index.html"];

js 需要手动设置热更代码,只针对非入口文件的 js 来做。

1
2
3
4
5
6
7
8
if (module.hot) {
//一旦发现开启热模块,就执行以下方法
module.hot.accept("./print.js", function () {
//方法会监听print.js的变化,其他默认不会打包构建
//会执行后面的回调函数
print();
});
}

🎉 代码调试优化-source-map?

简介

source-map 是一种提供源代码到构建后代码映射的技术。

如果构建后的代码出错了,通过映射关系可以追踪到源代码的错误。

基本使用方法

只需加入 devtool:’source-map’即可。

1
2
3
mode:'development',
devtool:'source-map',
//提供错误信息和源代码的错误未知

常用指令

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
devtool: "xxx-source-map";
/*1.内联source-map文件,
嵌入js之中,
只会生成一个,
因此不会生成本地文件,构建速度更快
提供错误信息和源代码的错误位置
*/
inline - source - map;
/*2.外联source-map文件
提供错误信息和提示构建后代码错误位置
*/
hidden - source - map;
/*3.内联source-map文件,
会在每一个js文件后生成对应的source-map
都在eval函数之中
提供错误信息和提示构建后代码错误位置和哈希值
*/
eval - source - map;
//4.外联source-map文件
//只有错误信息,无源代码信息
nosource - source - map;
//5.外联source-map文件
//只精确到行而不是列
cheap - source - map;
cheap - module - source - map;

具体使用方法

1
2
3
4
5
6
7
8
9
//开发环境,速度快,调试更友好--》eval-source-map
(eval>inline>cheap...)
速度最快:eval-cheap-source-map
调试最友好:source-map/cheap-module-source-map/cheap-source-map
//生产环境,源代码要不要隐藏?调试要不要更友好
//生产环境内联会增大代码体积,所以不用内联
nosource-source-map
hidden-source-map
--》source-map/cheap-module-source-map

第七章生产环境webpack 性能优化?

🎉 打包速度优化-oneOf?

概念

以下 loader 只会匹配一个。

1
2
3
4
5
6
7
8
9
rules: [
(oneOf: [
{ ... }, //处理js的loader
{ ... }, //处理css的loader
{ ... }, //处理less的loader
{ ... }, //处理图片的loader
{ ... } //处理其他文件的loader
]),
];

注意

当两个 loader 处理同一种文件类型时,把其中一个放在 oneof 外面。

🎉 打包速度优化-缓存?

babel 缓存

开启 babel 缓存,第二次构建时会读取第一次的缓存。

1
cacheDirectory: true;

基本的服务器代码

1
2
3
4
5
6
7
8
9
/*
服务器代码
启动服务器指令:
nodemon server.js
*/
const express = require('express');
const app = express();
app.use(express.static('build',{maxAge:1000*3600}));
app.listen(3000);

文件资源缓存

方案一 hash

1
2
3
4
5
//使用哈希值,在webpack构建时将filename附上哈希值
//问题:这样会导致所有缓存失效
//js和css哈希值一样
filename:built.[hash:10].js
filename:built.[hash:10].css

方案二 chunkhash

1
2
3
4
5
//根据chunk生成哈希值,如果打包来源于同一个chunk,那么哈希值就一样
//问题:js和css哈希值一样
//原因:因为css是在js中被引入的,所以同属于一个chunk
filename:built.[chunkhash:10].js
filename:built.[chunkhash:10].css

最终方案三 contenthash

1
2
3
//根据文件内容生成哈希值,如果文件内容不一样,那么哈希值就不一样
filename:built.[contenthash:10].js
filename:built.[contenthash:10].css

🎉 打包速度优化-多进程打包?

安装

使用前需要安装 html-webpack-plugin。

1
$cnpm i thread-loader -D

使用

在 use 中引入

1
use: [thread - loader, xxx - loader];

使用多进程打包的利弊

启动需要 600 毫秒,进程通信也具有时间成本。如果本身打包时间较短,无需使用多进程打包。

只有工作时间较长才需要。

1
2
3
4
{loader:thread-loader,
options:{
workers:2, //进程2个
}}

🎉 代码性能优化-tree-shaking?

概念

将代码中未经应用的代码去除。

前提:1.使用 ES6 模块化 2.开启 production 环境

作用:减少代码体积

配置

在 package.json 中配置

“sodeEffects“:false 所有代码都没有副作用(都可以进行 tree shaking)

问题:可能会把 css/其他文件文件干掉

解决:

“sodeEffects“:[“*.css”]

🎉 代码性能优化-代码分割 code split?

多入口出口文件的重命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//将entry修改为对象,并对入口进行命名
//在filename处增加[name]
entry: {
index:'./src/js/index.js',
main:'./src/index.html',
text:'./src/js/text.js',
},
//输出
output: {
//输出文件名
filename: "js/[name].built.[contenthash:10].js",
//__dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, 'build'),
//公共路径为当前路径
publicPath: './'
},

多文件处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.optimization配置可以将node_modules中的代码单独打包成一个chunk最终输出
//2.optimization配置会自动分析多入口chunk中,有没有公共的代码
//如果有会打包成单独的一个chunk
optimization:{
splitChunks:{
chunks:'all'
}
}
//3.通过js代码,让某个文件被单独打包成一个chunk
//import动态导入语法:能将某个文件单独打包
//通过注释可以给单独打包的Chunk起名字
import (/* webpackChunkName:'test'*/'./text')
.then((result) =>{
console.log(result)
})
.catch(() =>{
console.log('加载失败')
})

🎉 代码性能优化-lazy loading?

概念

当需要加载某个 js 的时候再进行加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//import {mul} form './test'
//这里是直接加载的方法
//这里是懒加载
document.getElementById('btn').onclick = function(){
//这里的webPrefetch是预加载的意思,会等其他资源加载完毕再加载预加载资源
//正常加载我们可以理解为并行加载(同一时间加载多个文件)
import (/* webpackChunkName:'test',webPrefetch:true*/'./text')
.then(({mul}) =>{
console.log(mul(4,5))
})
.catch(() =>{
console.log('加载失败')
})
}

🎉 代码性能优化-PWA?

简介

渐进式网络开发应用程序(离线可访问技术)

workbox –》 workbox-webpack-plugin

安装

使用前需要安装 html-webpack-plugin。

1
$cnpm i workbox-webpack-plugin -D

引入

1
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");

修改配置

1
2
3
4
5
6
7
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim:true,
skipWaiting:true
//1.帮助sw快速启动
//2.删除旧的sw
//生成sw配置文件
})

注册 sw

1
2
3
4
5
6
7
8
9
10
11
if('serviceWorker' in navigator){
windows.addEventListener('load',() => {
navigator.serviceWorker.register('/service-worker');
.then(() =>{
console.log('SW注册失败')
})
.catch(() =>{
console.log('SW注册失败')
}
}
)}

存在的问题

1
2
3
4
5
6
7
8
9
10
//1.eslint 不认识window、 navigator等变量。
//解决:需要修改package.json中的eslintConfig配置。
"env":{
"browser":true//支持浏览器变量
}
//2.sw代码必须运行在服务器上
//--》 nodeJS代码
//--》
cnpm i serve -g
serve -s build 启动服务器,将build下所有资源作为静态资源暴露

🎉 代码性能优化-externals?

概念

将需要用 cdn 资源引入的 js 屏蔽掉。

方法

1
2
3
4
5
6
externals: {
// 拒绝jQuery被打包进来(通过cdn引入,速度会快一些)
// 忽略的库名 -- npm包名
//需要手动引入cdn链接
jquery: "jQuery";
}

🎉 代码性能优化-DLL?

概念

dll:让某些库单独打包,后直接引入到 build 中。可以在 code split 分割出 node_modules 后再用 dll 更细的分割,优化代码运行的性能。

配置

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
/*
node_modules的库会打包到一起,但是很多库的时候打包输出的js文件就太大了
使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
当运行webpack时,默认查找webpack.config.js配置文件
需求:需要运行webpack.dll.js文件
--> webpack --config webpack.dll.js(运行这个指令表示以这个配置文件打包)
*/
const { resolve } = require("path");
const webpack = require("webpack");
module.exports = {
entry: {
// 最终打包生成的[name] --> jquery
// ['jquery] --> 要打包的库是jquery
jquery: ["jquery"],
},
output: {
// 输出出口指定
filename: "[name].js", // name就是jquery
path: resolve(__dirname, "dll"), // 打包到dll目录下
library: "[name]_[hash]", // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个manifest.json --> 提供jquery的映射关系(告诉webpack:jquery之后不需要再打包和暴露内容的名称)
new webpack.DllPlugin({
name: "[name]_[hash]", // 映射库的暴露的内容名称
path: resolve(__dirname, "dll/manifest.json"), // 输出文件路径
}),
],
mode: "production",
};

webpack.config.js 配置:(告诉 webpack 不需要再打包 jquery,并将之前打包好的 jquery 跟其他打包好的资源一同输出到 build 目录下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入插件
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
// plugins中配置:
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出到build目录下,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],

第八章 webpack 配置详解?

🎉entry?

string 类型

–> ‘./src/index.js’,单入口

打包形成一个 chunk。 输出一个 bundle 文件。此时 chunk 的名称默认是 main。

array 类型

–> [‘./src/index.js’, ‘./src/add.js’],多入口

所有入口文件最终只会形成一个 chunk,输出出去只有一个 bundle 文件。

(一般只用在 HMR 功能中让 html 热更新生效)。

object 类型

多入口,有几个入口文件就形成几个 chunk,输出几个 bundle 文件,此时 chunk 的名称是 key 值。

–> 特殊用法:

1
2
3
4
5
6
entry: {
// 最终只会形成一个chunk, 输出出去只有一个bundle文件。
index: ['./src/index.js', './src/count.js'],
// 形成一个chunk,输出一个bundle文件。
add: './src/add.js'
}

🎉output?

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
 output: {
// 文件名称(指定名称+哈希值+目录)
filename: 'js/[name].[contenthash:10].js',
// 输出文件目录(将来所有资源输出的公共目录)
path: resolve(__dirname, 'build'),
// 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
publicPath: '/',
chunkFilename: 'js/[name]..[contenthash:10]_chunk.js', // 指定非入口chunk的名称
library: '[name].[contenthash:10]', // 打包整个库后向外暴露的变量名
libraryTarget: 'window' // 变量名添加到哪个上 browser:window
// libraryTarget: 'global' // node:global
// libraryTarget: 'commonjs' // conmmonjs模块 exports
},

🎉module?

配置

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
module: {
rules: [
// loader的配置
{
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/,
// 只检查src下的js文件
include: resolve(__dirname, 'src'),
enforce: 'pre', // 优先执行
// enforce: 'post', // 延后执行
// 单个loader用loader
loader: 'eslint-loader',
options: {} // 指定配置选项
},
{
// 以下配置只会生效一个
oneOf: []
}
]
},

🎉module?

配置

1
2
3
4
5
6
7
8
9
10
11
// 解析模块的规则
resolve: {
// 配置解析模块路径别名: 优点:当目录层级很复杂时,简写路径;缺点:路径不会提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名(引入时就可以不写文件后缀名了)
extensions: ['.js', '.json', '.jsx', '.css'],
// 告诉 webpack 解析模块应该去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}

这样配置后,引入文件就可以这样简写:import '$css/index';

🎉devServer?

配置

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
devServer: {
// 运行代码所在的目录
contentBase: resolve(__dirname, 'build'),
// 监视contentBase目录下的所有文件,一旦文件变化就会reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本信息外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示
overlay: false,
// 服务器代理,--> 解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接收到/api/xxx的请求,就会把请求转发到另外一个服务器3000
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将/api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}

其中,跨域问题:同源策略中不同的协议、端口号、域名就会产生跨域。
正常的浏览器和服务器之间有跨域,但是服务器之间没有跨域。代码通过代理服务器运行,所以浏览器和代理服务器之间没有跨域,浏览器把请求发送到代理服务器上,代理服务器替你转发到另外一个服务器上,服务器之间没有跨域,所以请求成功。代理服务器再把接收到的响应响应给浏览器。这样就解决开发环境下的跨域问题。

contenthash 缓存会导致一个问题:修改 a 文件导致 b 文件 contenthash 变化。

因为在 index.js 中引入 a.js,打包后 index.js 中记录了 a.js 的 hash 值,而 a.js 改变,其重新打包后的 hash 改变,导致 index.js 文件内容中记录的 a.js 的 hash 也改变,从而重新打包后 index.js 的 hash 值也会变,这样就会使缓存失效。(改变的是 a.js 文件但是 index.js 文件的 hash 值也改变了)

解决办法:runtimeChunk –> 将当前模块记录其他模块的 hash 单独打包为一个文件 runtime,这样 a.js 的 hash 改变只会影响 runtime 文件,不会影响到 index.js 文件

🎉module?

配置

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
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build'),
chunkFilename: 'js/[name].[contenthash:10]_chunk.js' // 指定非入口文件的其他chunk的名字加_chunk
},
optimization: {
splitChunks: {
chunks: 'all',
/* 以下都是splitChunks默认配置,可以不写
miniSize: 30 * 1024, // 分割的chunk最小为30kb(大于30kb的才分割)
maxSize: 0, // 最大没有限制
minChunks: 1, // 要提取的chunk最少被引用1次
maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量为5
maxInitialRequests: 3, // 入口js文件最大并行请求数量
automaticNameDelimiter: '~', // 名称连接符
name: true, // 可以使用命名规则
cacheGroups: { // 分割chunk的组
vendors: {
// node_modules中的文件会被打包到vendors组的chunk中,--> vendors~xxx.js
// 满足上面的公共规则,大小超过30kb、至少被引用一次
test: /[\\/]node_modules[\\/]/,
// 优先级
priority: -10
},
default: {
// 要提取的chunk最少被引用2次
minChunks: 2,
prority: -20,
// 如果当前要打包的模块和之前已经被提取的模块是同一个,就会复用,而不是重新打包
reuseExistingChunk: true
}
} */
},
// 将index.js记录的a.js的hash值单独打包到runtime文件中
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
// 配置生产环境的压缩方案:js/css
new TerserWebpackPlugin({
// 开启缓存
cache: true,
// 开启多进程打包
parallel: true,
// 启用sourceMap(否则会被压缩掉)
sourceMap: true
})
]
}

第九章 webpack5 的新特性?

🎉 重点内容?

此版本重点关注以下内容:

  • 通过持久缓存提高构建性能.
  • 使用更好的算法和默认值来改善长期缓存.
  • 通过更好的树摇和代码生成来改善捆绑包大小.
  • 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
  • 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.

🎉 下载?(截止 2020 年 11 月已经可以直接下了)

1
$cnpm i webpack webpack-cli -D

🎉 自动删除 Node.js Polyfills?

早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。

尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。

webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。

迁移:

  • 尽可能尝试使用与前端兼容的模块。
  • 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标。

Chunk 和模块 ID

添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。

1
chunkIds: "deterministic", moduleIds: "deterministic"

🎉Chunk ID?

你可以不用使用import(/* webpackChunkName: "name" */ "module")在开发环境来为 chunk 命名,生产环境还是有必要的

webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了

🎉Tree Shaking?

  1. webpack 现在能够处理对嵌套模块的 tree shaking
1
2
3
4
5
6
7
8
9
// inner.js
export const a = 1;
export const b = 2;
// module.js
import * as inner from "./inner";
export { inner };
// user.js
import * as module from "./module";
console.log(module.inner.a);

在生产环境中, inner 模块暴露的b会被删除

  1. webpack 现在能够多个模块之前的关系
1
2
3
4
5
6
7
import { something } from "./something";
function usingSomething() {
return something;
}
export function test() {
return usingSomething();
}

当设置了"sideEffects": false时,一旦发现test方法没有使用,不但删除test,还会删除"./something"

  1. webpack 现在能处理对 Commonjs 的 tree shaking

🎉Output?

webpack 4 默认只能输出 ES5 代码

webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.

如:output.ecmaVersion: 2015

SplitChunk

1
2
3
4
5
6
7
// webpack4
minSize: 30000;
// webpack5
minSize: {
javascript: 30000,
style: 50000,
}

🎉Caching?

1
2
3
4
5
6
7
8
9
// 配置缓存
cache: {
// 磁盘存储
type: "filesystem",
buildDependencies: {
// 当配置修改时,缓存失效
config: [__filename]
}
}

缓存将存储到node_modules/.cache/webpack

🎉 监视输出文件?

之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。

此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。

🎉 默认值?

  • entry: "./src/index.js
  • output.path: path.resolve(__dirname, "dist")
  • output.filename: "[name].js"

尚硅谷 2020 最新版 Webpack5 实战教程(从入门到精通)
https://www.bilibili.com/video/BV1e7411j7T5?p=1