webpack基础
# 一、webpack知识框架
webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。在webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块
处理。
它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。
# webpack 是什么?
webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序;
我们来对上面的解释进行拆解:
打包bundler:webpack可以将帮助我们进行打包,所以它是一个打包工具
静态的static:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器);
模块化module:webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;
现代的modern:我们前端说过,正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展;
概念 | webpack 中文文档 (docschina.org) (opens new window)
# 1、安装
webpack4以上的版本需要全局/本地都安装webpack-cli)
npm install webpack webpack-cli -g 全局安装
npm init #初始化项目
npm i webpack webpack-cli -D 当前项目也安装,全局安装了的话,这里可以省略
webpack // 即可进行编译,要配置更多,可以在webpack.config.js中进行设置
常用的loader安装
npm install -D css-loader style-loader less less-loader postcss postcss-cli postcss-loader postcss-preset-env autoprefixer url-loader file-loader html-webpack-plugin copy-webpack-plugin clean-webpack-plugin babel-loader @babel/cli @babel/core @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping @babel/preset-env
"devDependencies": {
"@babel/cli": "^7.14.3",
"@babel/core": "^7.14.3",
"@babel/plugin-transform-arrow-functions": "^7.13.0",
"@babel/plugin-transform-block-scoping": "^7.14.2",
"@babel/preset-env": "^7.14.2",
"autoprefixer": "^10.2.5",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^4.0.0-alpha.0",
"copy-webpack-plugin": "^9.0.0",
"css-loader": "^5.2.5",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.1",
"less": "^4.1.1",
"less-loader": "^9.0.0",
"postcss": "^8.3.0",
"postcss-cli": "^8.3.1",
"postcss-loader": "^5.3.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.37.1",
"webpack-cli": "^4.7.0"
}
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
npm会从
node_modules
目录里面找包,如果当前目录没有,会一直往上级目录找
webpack的安装目前分为两个:webpack
、webpack-cli
那么它们是什么关系呢?
执行webpack命令,会执行node_modules下的.bin目录下的webpack;
webpack在执行时是依赖webpack-cli的,如果没有安装就会报错;
而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程;
所以在安装webpack时,我们需要同时安装webpack-cli(第三方的脚手架事实上是没有使用webpack-cli的,而是类似于自己的vue-service-cli的东西)
# 2、运行
#打包指定文件, webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/js/main.js 整体打包环境
webpack ./src/index.js -o ./build/js/ --mode=product|development
# 从当前目录中找webpack.config.js, 根据里面的配置进行打包
webpack
2
3
4
5
- 直接运行
webpack
命令,是调用的全局webpack版本 - 如果想要使用依赖的webpack版本,可以使用
npx webpack
命令,或者在package.json里面指定命令
1、webpack 本身能处理 js/json 资源,不能处理 css/img 等其他资源
2、生产环境和开发环境将 ES6 模块化编译成浏览器能识别的模块化,但是不能处理 ES6 的基本语法转化为 ES5(需要借助 loader)
3、生产环境比开发环境多一个压缩 js 代码
--config
指定配置文件,默认为webpack.config.js
--entry
指定入口文件,默认为src/index.js
--output-path
指定出口路径,为绝对路径
# 3、概念
核心概念:
- 入口(entry) (opens new window) 指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
- 输出(output) (opens new window) 指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
- loader (opens new window) 让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件(webpack 自身只理解JS)
- 插件(plugin) (opens new window) 可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
- 模式(mode) (opens new window)
- 浏览器兼容性(browser compatibility) (opens new window)
- 环境(environment) (opens new window)
# 1、入口起点(entry point)
指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) (opens new window) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
默认值是 ./src/index.js
,但你可以通过在 webpack configuration (opens new window) 中配置 entry
属性,来指定一个(或多个)不同的入口起点。
webpack.config.js
module.exports = {
entry: './path/to/my/entry/file.js',
};
2
3
在 入口起点 (opens new window) 章节可以了解更多信息
# 2、输出(output)
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
你可以通过在配置中指定一个 output
字段,来配置这些处理过程:
webpack.config.js
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};
2
3
4
5
6
7
8
9
在上面的示例中,我们通过 output.filename
和 output.path
属性,来告诉 webpack bundle 的名称,以及我们想要 bundle 生成(emit)到哪里。可能你想要了解在代码最上面导入的 path 模块是什么,它是一个 Node.js 核心模块 (opens new window),用于操作文件路径。
output
属性还有 更多可配置的特性 (opens new window),如果你想要了解更多关于output
属性的概念,可以通过阅读 输出章节 (opens new window) 来了解更多。
# 3、loader
loader是什么呢?
loader 可以用于对模块的源代码进行转换;
我们可以将css文件也看成是一个模块,我们是通过import来加载这个模块的;
在加载这个模块时,webpack其实并不知道如何对其进行加载,我们必须制定对应的loader来完成这个功能;
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块 (opens new window),以供应用程序使用,以及被添加到依赖图中。
- loader的执行顺序是从右向左(或者说从下到上,或者说从后到前的)
注意,loader 能够
import
导入任何类型的模块(例如.css
文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是很有必要的,因为这可以使开发人员创建出更准确的依赖关系图。在更高层面,在 webpack 的配置中,loader 有两个属性:
test
属性,识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个 loader。
webpack.config.js
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
};
2
3
4
5
6
7
8
9
10
以上配置中,对一个单独的 module 对象定义了 rules
属性,里面包含两个必须属性:test
和 use
。这告诉 webpack 编译器(compiler) 如下信息:
“嘿,webpack 编译器,当你碰到「在
require()
/import
语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用)raw-loader
转换一下。”重要的是要记住,在 webpack 配置中定义 rules 时,要定义在
module.rules
而不是rules
中。为了使你便于理解,如果没有按照正确方式去做,webpack 会给出警告。请记住,使用正则表达式匹配文件时,你不要为它添加引号。也就是说,
/\.txt$/
与'/\.txt$/'
或"/\.txt$/"
不一样。前者指示 webpack 匹配任何以 .txt 结尾的文件,后者指示 webpack 匹配具有绝对路径 '.txt' 的单个文件; 这可能不符合你的意图。在使用 loader 时,可以阅读 loader 章节 (opens new window) 查看更深入的自定义配置。
Loaders | webpack (opens new window)
# 4、插件(plugin)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
查看 插件接口(plugin interface) (opens new window),学习如何使用它来扩展 webpack 能力。
想要使用一个插件,你只需要
require()
它,然后把它添加到plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用new
操作符来创建一个插件实例。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
2
3
4
5
6
7
8
9
在上面的示例中,html-webpack-plugin
为应用程序生成一个 HTML 文件,并自动注入所有生成的 bundle。
webpack 提供许多开箱可用的插件!查阅 插件列表 (opens new window) 获取更多。
在 webpack 配置中使用插件是简单直接的。然而,也有很多值得我们进一步探讨的用例。查看这里了解更多 (opens new window)。
# 5、模式(mode)
通过选择 development
, production
或 none
之中的一个,来设置 mode
参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production
。
module.exports = {
mode: 'production',
};
2
3
想要了解更多,请查阅 mode 配置 (opens new window),这里有具体每个值相应的优化行为。
# 6、浏览器兼容性(browser compatibility)
webpack 支持所有符合 ES5 标准 (opens new window) 的浏览器(不支持 IE8 及以下版本)。webpack 的 import()
和 require.ensure()
需要 Promise
。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill (opens new window)。
# 7、环境(environment)
webpack 5 运行于 Node.js v10.13.0+ 的版本。
# 二、loader转换各种资源
在webpack.config.js
里面配置
# 1.加载样式资源
# css-loader
npm install css-loader -D
npm install style-loader -D
2
如何使用这个loader来加载css文件呢?有三种方式:
内联方式;
CLI方式(webpack5中不再使用);
loader配置方式;
**内联方式:**内联方式使用较少,因为不方便管理;
- 在引入的样式前加上使用的loader,并且使用!分割;
import "css-loader!../css/style.css";
loader配置方式配置方式表示的意思是在我们的webpack.config.js文件中写明配置信息:
module.rules中允许我们配置多个loader(因为我们也会继续使用其他的loader,来完成其他文件的加载);
这种方式可以更好的表示loader的配置,也方便后期的维护,同时也让你对各个Loader有一个全局的概览;
module.rules的配置如下:
rules属性对应的值是一个数组:[Rule]
数组中存放的是一个个的Rule,Rule是一个对象,对象中可以设置多个属性:
test属性:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;
use属性:对应的值时一个数组:[UseEntry]
UseEntry是一个对象,可以通过对象的属性来设置一些其他属性
loader:必须有一个 loader属性,对应的值是一个字符串;
options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;
query:目前已经使用options来替代;
传递字符串(如:use: [ 'style-loader' ])是 loader 属性的简写方式(如:use: [ { loader: 'style-loader'} ]);
loader属性: Rule.use: [ { loader } ] 的简写。
module: {
rules: [
{
test: /\.css$/, //正则表达式
// 1.loader的写法(语法糖)
// loader: "css-loader"
// 2.完整的写法
use: [
// {loader: "css-loader"}
"style-loader",
"css-loader",
"postcss-loader"
// {
// loader: "postcss-loader",
// options: {
// postcssOptions: {
// plugins: [
// require("autoprefixer")
// ]
// }
// }
// }
]
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# style-loader
loader的执行顺序是从右向左(或者说从下到上,或者说从后到前的)
css-loader只是负责将.css文件进行解析,并不会将解析之后的css插入到页面中;
如果我们希望再完成插入style的操作,那么我们还需要另外一个loader,就是style-loader;
# Less工具处理
npm install less less-loader -D
npx lessc ./src/css/title.less title.css
2
3
{
test: /\.(less|css)$/i,
use: [
"style-loader",
"css-loader",
"less-loader"
]
}
2
3
4
5
6
7
8
# PostCSS工具
什么是PostCSS呢?
PostCSS是一个通过JavaScript来转换样式的工具;
这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置;
但是实现这些功能,我们需要借助于PostCSS对应的插件;
npm install postcss postcss-cli -D
# postcss的插件autoprefixer
npm install autoprefixer -D
npx postcss --use autoprefixer -o end.css ./src/css/style.css
2
# postcss的插件postcss-preset-env
它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境添加所需的polyfill;
也包括会自动帮助我们添加autoprefixer(所以相当于已经内置了autoprefixer);
npm install postcss-preset-env -D
# postcss-loader
npm install postcss-loader -D
/*
webpack.config.js webpack的配置文件
作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)
所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。
*/
// 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配置
// 不同文件必须配置不同loader处理
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些loader进行处理
use: [
// use数组中loader执行顺序:从右到左,从下到上 依次执行
// 创建style标签,将js中的样式资源插入进行,添加到head中生效
'style-loader',
// 将css文件变成commonjs模块加载js中,里面内容是样式字符串
'css-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将less文件编译成css文件
// 需要下载 less-loader和less
'less-loader'
]
}
]
},
// plugins的配置
plugins: [
// 详细plugins的配置
],
// 模式
mode: 'development', // 开发模式
// mode: 'production'
}
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
# 2.加载图片、字体资源
图片资源主要用在: img标签里面; 样式backgroud引入。打包时需要对图片资源进行处理
src目录下的文件,都把他们当成资源,可以用import 或者require进行导入
# file-loader
{
test: /\.(eot|ttf|woff2?)$/,
use: {
loader: "file-loader",
options: {
// outputPath: "font",
name: "font/[name]_[hash:6].[ext]"
}
}
},
2
3
4
5
6
7
8
9
10
# 文件命名规则
[ext]: 处理文件的扩展名;
[name]:处理文件的名称;
[hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);
[contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样,后面会讲到);
[hash:<length>]
:截图hash的长度,默认32个字符太长了;[path]:文件相对于webpack配置文件的路径;
更多文档请参考:file-loader | webpack (opens new window)
# url-loader
功能和file-loader一样,可以将小文件打包成base64的URI
开发中我们往往是小的图片需要转换,但是大的图片直接使用图片即可
- 这是因为小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程;而大的图片也进行转换,反而会影响页面的请求速度;
那么,我们如何可以限制哪些大小的图片转换和不转换呢?
- url-loader有一个options属性
limit
,可以用于设置转换的限制;
下面的代码38kb的图片会进行base64编码,而295kb的不会;
{
test: /\.(jpe?g|png|gif|svg)$/,
use: {
loader: "url-loader",
options: {
// outputPath: "img",
name: "img/[name]_[hash:6].[ext]",
limit: 100 * 1024
}
}
},
2
3
4
5
6
7
8
9
10
11
参考文档:url-loader | webpack (opens new window)
# asset module type资源模块类型
在webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loader 、url-loader、file-loader;
- 在webpack5开始,我们可以直接使用资源模块类型(asset module type),来替代上面的这些loader;
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现;
{
test: /\.(jpe?g|png|gif|svg)$/,
type: "asset",
generator: {
filename: "asset/img/[name]_[hash:6][ext][query]"
},
parser: {
dataUrlCondition: {
maxSize: 100 * 1024
}
}
},
{
test: /\.(eot|ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "asset/font/[name]_[hash:6][ext][query]"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
参考文档: Asset Modules | webpack (opens new window)
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.less$/,
// 要使用多个loader处理用use
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 问题:默认处理不了html中img图片
// 处理图片资源
test: /\.(jpe?g|png|gif|svg)$/,
// 使用一个loader
// 下载 url-loader file-loader
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
// 解析时会出问题:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
// 给图片进行重命名
// [hash:10]取图片的hash的前10位
// [ext]取文件原来扩展名
name: '[hash:10].[ext]'
}
},
{
test: /\.html$/,
// 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};
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
# 3、加载vue文件
npm install -D vue-loader @vue/compiler-sfc
@vue/compiler-sfc
可以对单文件SFC里面的template进行解析, 在vue2中使用@vue/vue-template-compiler
const { VueLoaderPlugin } = require('vue-loader/dist/index'); // vue-loader里面包括的插件
// rule里面配置
{
test: /\.vue$/,
loader: "vue-loader"
}
plugins: [
new CleanWebpackPlugin(), // 打包时自动先删除旧打包的文件
new VueLoaderPlugin(),
new DefinePlugin({
BASE_URL: "'./'",
__VUE_OPTIONS_API__: true, // 兼容vue2的options写法
__VUE_PROD_DEVTOOLS__: false // 生产环境的话,改为true,可以打包更小体积
}),
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
引入的vue文件,需要加后缀.vue, 因为这时是作为资源加载进来的,不然会报错
vue-loader会调用@vue/compiler-sfc去转换vue文件,因此import vue的时候,不用引入运行编译版
import {createApp} from 'vue/dist/vue.esm-bundler';
vue3里面如果vue写法还是vue2的options方式,开启
__VUE_OPTIONS_API__
参考: core/packages/vue at main · vuejs/core (github.com) (opens new window)生产环境将
__VUE_PROD_DEVTOOLS__
改为true,可以打包更小体积
# 4、加载ts文件
npm i -g typescript
npm i -D typescript ts-loader
tsc --init #初始化ts的配置
npm install tslint -g #安装tslint来约束
tslint -i
npm install cross-env -D #安装env多环境
2
3
4
5
6
7
8
resolve: {
extensions: ['.ts','.wasm', ".mjs", ".js", ".json",".vue",".jsx", ".tsx"],
alias: {
"@": path.resolve(__dirname, "./src"),
"js": path.resolve(__dirname, "./src/js")
}
},
rules:[
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
}]
2
3
4
5
6
7
8
9
10
11
12
13
14
TypeScript(二)使用Webpack搭建环境 (qq.com) (opens new window)
# 三、plugin插件
Loader是用于特定的模块类型进行转换;
Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等
# 1、CleanWebpackPlugin
前面我们演示的过程中,每次修改了一些配置,重新打包时,都需要手动删除dist文件夹,我们可以借助于一个插件来帮助我们完成,这个插件就是CleanWebpackPlugin
npm install clean-webpack-plugin -D
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "./public/index.html",
title: "哈哈哈哈"
}),
new DefinePlugin({
BASE_URL: "'./'"
}),
new CopyWebpackPlugin({
patterns: [
{
from: "public",
to: "./",
globOptions: {
ignore: [
"**/index.html"
]
}
}
]
})
]
}
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
# 2、HtmlWebpackPlugin插件
我们的HTML文件是编写在根目录下的,而最终打包的dist文件夹中是没有index.html文件的。在进行项目部署的时,必然也是需要有对应的入口文件index.html,所以我们也需要对index.html进行打包处理
# 自定义HTML模板
默认情况下是根据ejs的一个模板来生成的;在html-webpack-plugin的源码中,有一个default_index.ejs
模块
- 语法
<% 变量 %>
,这个是EJS模块填充数据的方式
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "./public/index.html",
title: "哈哈哈哈"
})
],
2
3
4
5
6
7
webpack多页面打包 - 哥哦狗子 - 博客园 (cnblogs.com) (opens new window)
# 3、webpack内置插件
用法一:
const webpack = require("webpack")
new webpack.插件名(options)
2
用法二:
const {DefinePlugin, BannerPlugin, ProvidePlugin} = require("webpack"); // 解构出来
new ProvidePlugin(options)
2
# DefinePlugin
DefinePlugin允许在编译时创建配置的全局常量,是一个webpack内置的插件(不需要单独安装):
可以读取到BASE_URL的值
new DefinePlugin({
BASE_URL: "'./'" // 这里要加引号,不然会被当成变量
}),
2
3
# BannerPlugin
它可以为每个chunk生成的文件头部添加一行注释,一般用于添加作者、公司、版权等信息
new webpack.BannerPlugin({
banner: `
hash:[hash]
chunkhash:[chunkhash]
name:[name]
author:yuanjin
corporation:duyi
`
})
2
3
4
5
6
7
8
9
# ProvidePlugin
自动加载模块,而不必到处 import 或 require
new webpack.ProvidePlugin({
$: 'jquery',
_: 'lodash'
})
2
3
4
# 4、CopyWebpackPlugin
在vue的打包过程中,如果我们将一些文件放到public的目录下,那么这个目录会被复制到dist文件夹中。这个复制的功能,我们可以使用CopyWebpackPlugin来完成;
new CopyWebpackPlugin({
patterns: [
{
from: "public", // 设置从哪一个源中开始复制
to: "./", //复制到的位置,可以省略,会默认复制到打包的目录下
globOptions: { //设置一些额外的选项,其中可以编写需要忽略的文件
ignore: [
"**/.DS_Store", //mac目录下回自动生成的一个文件
"**/index.html" // index.html不需要复制,因为我们已经通过HtmlWebpackPlugin完成了index.html的生成;
]
}
}
]
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4、add-asset-html-webpack-plugin
new AddAssetHtmlPlugin([
{filepath: path.resolve(__dirname, '../src/axios.min.js'),
outputPath: 'smc_public/dll/',
publicPath: path.join(publicPath, 'smc_public/dll'),
includeSourcemap: true}
])
2
3
4
5
6
# 6.devServer
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 打包其他资源(除了html/js/css资源以外的资源)
{
// 排除css/js/html资源
exclude: /\.(css|js|html|less)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
// 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
// 特点:只会在内存中编译打包,不会有任何输出
// 启动devServer指令为:npx webpack-dev-server
devServer: {
// 项目构建后路径
contentBase: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true
}
};
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
# 7.提取css成单独文件
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 创建style标签,将样式放入
// 'style-loader',
// 这个loader取代style-loader。作用:提取js中的css成单独文件
MiniCssExtractPlugin.loader,
// 将css文件整合到js文件中
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/built.css'
})
],
mode: 'development'
};
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
# 8.css兼容
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
/*
css兼容性处理:postcss --> postcss-loader postcss-preset-env
帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
"browserslist": {
// 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境:默认是看生产环境
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
*/
// 使用loader的默认配置
// 'postcss-loader',
// 修改loader的配置
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
})
],
mode: 'development'
};
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
# 9.css压缩
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin()
],
mode: 'development'
};
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
# 10.eslint 语法检查
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
/*
语法检查: eslint-loader eslint
注意:只检查自己写的源代码,第三方的库是不用检查的
设置检查规则:
package.json中eslintConfig中设置~
"eslintConfig": {
"extends": "airbnb-base"
}
airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复eslint的错误
fix: true
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};
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
# js,html压缩只要将mode改为production就会自动压缩
# 11、js兼容性检查
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
/*
js兼容性处理:babel-loader @babel/core
1. 基本js兼容性处理 --> @babel/preset-env
问题:只能转换基本语法,如promise高级语法不能转换
2. 全部js兼容性处理 --> @babel/polyfill
问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
3. 需要做兼容性处理的就做:按需加载 --> core-js
*/
{
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',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
],
mode: 'development'
};
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
# 12.开发性能提升
# 1、HMR:热模块替换
/*
HMR: hot module replacement 热模块替换 / 模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)
极大提升构建速度
样式文件:可以使用HMR功能:因为style-loader内部实现了~
js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码
注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。
html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ (不用做HMR功能)
解决:修改entry入口,将html文件引入
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader的配置
{
// 处理less资源
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 处理css资源
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块化
esModule: false,
outputPath: 'imgs'
}
},
{
// 处理html中img资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启HMR功能
// 当修改了webpack配置,新配置要想生效,必须重新webpack服务
hot: true
}
};
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
# 2、source-map
/*
source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map:外部
错误代码准确信息 和 源代码的错误位置
inline-source-map:内联
只生成一个内联source-map
错误代码准确信息 和 源代码的错误位置
hidden-source-map:外部
错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的source-map,都在eval
错误代码准确信息 和 源代码的错误位置
nosources-source-map:外部
错误代码准确信息, 但是没有任何源代码信息
cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确的行
cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入
内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快
开发环境:速度快,调试更友好
速度快(eval>inline>cheap>...)
eval-cheap-souce-map
eval-source-map
调试更友好
souce-map
cheap-module-souce-map
cheap-souce-map
--> eval-source-map / eval-cheap-module-souce-map
生产环境:源代码要不要隐藏? 调试要不要更友好
内联会让代码体积变大,所以在生产环境不用内联
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
--> source-map / cheap-module-souce-map
*/
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
# 3、tree-shaking(去除没用到的代码)
# 4、externals:让某些库不打包
可以通过 cdn 引入, webpack.config.js 中配置:
externals: {
// 拒绝jQuery被打包进来(通过cdn引入,速度会快一些)
// 忽略的库名 -- npm包名
jquery: 'jQuery'
}
2
3
4
5
需要在 index.html 中通过 cdn 引入:
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
# 5、babel 缓存
babel 缓存:类似 HMR,将 babel 处理后的资源缓存起来(哪里的 js 改变就更新哪里,其他 js 还是用之前缓存的资源),让第二次打包构建速度更快
# 6、多进程打包
多进程打包:某个任务消耗时间较长会卡顿,多进程可以同一时间干多件事,效率更高。
优点是提升打包速度,缺点是每个进程的开启和交流都会有开销(babel-loader消耗时间最久,所以使用thread-loader针对其进行优化)
# 7、dll
dll:让某些库单独打包,后直接引入到 build 中。可以在 code split 分割出 node_modules 后再用 dll 更细的分割,优化代码运行的性能。
# 8、pwa(离线可访问技术)
pwa:离线可访问技术(渐进式网络开发应用程序),使用 serviceworker 和 workbox 技术。优点是离线也能访问,缺点是兼容性差。
# mode和devtool
mode:'development', // development、production
devtool: 'source-map', // 建立js映射文件,方便调试代码和错误
2
# 二、webpack配置文件
# loader 和 plugin 的不同
plugin 一定要先引入才能使用
loader:1. 下载 2. 使用(配置 loader)
plugins:1.下载 2. 引入 3. 使用
# 引入依赖
npm i -D css-loader file-loader html-loader html-webpack-plugin style-loader less less-loader postcss-loader mini-css-extract-plugin optimize-css-assets-webpack-plugin add-asset-html-webpack-plugin url-loader thread-loader webpack webpack-cli webpack-dev-server
{
"name": "webpack_code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/polyfill": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"add-asset-html-webpack-plugin": "^3.1.3",
"babel": "^6.23.0",
"babel-loader": "^8.0.6",
"core-js": "^3.6.4",
"css-loader": "^3.4.2",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.20.1",
"file-loader": "^5.0.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3",
"terser-webpack-plugin": "^2.3.5",
"thread-loader": "^2.1.3",
"url-loader": "^3.0.0",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"workbox-webpack-plugin": "^5.0.0"
},
"dependencies": {
"jquery": "^3.4.1"
},
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
},
"sideEffects": [
"*.css"
]
}
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
npm upgrade
升级里面的库版本, 再npm install
/*
webpack.config.js webpack的配置文件
作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)
所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。
*/
// 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配置
// 不同文件必须配置不同loader处理
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些loader进行处理
use: [
// use数组中loader执行顺序:从右到左,从下到上 依次执行
// 创建style标签,将js中的样式资源插入进行,添加到head中生效
'style-loader',
// 将css文件变成commonjs模块加载js中,里面内容是样式字符串
'css-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将less文件编译成css文件
// 需要下载 less-loader和less
'less-loader'
]
}
]
},
// plugins的配置
plugins: [
// 详细plugins的配置
],
// 模式
mode: 'development', // 开发模式
// mode: 'production'
}
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
# 三、搭建本地服务器开发
webpack提供了几种可选的方式:
webpack watch mode;
webpack --watch
webpack-dev-server(常用);
webpack-dev-middleware;
# 开启watch模式
"scripts": {
"build": "webpack",
"watch": "webpack --watch",
"serve": "webpack serve"
},
或者配置 watch: true
2
3
4
5
6
7
此方式可以监听到文件的变化,但没有自动刷新浏览器的功能
# webpack-dev-server 模块热替换
webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中:
- 事实上webpack-dev-server使用了一个库叫memfs(memory-fs webpack自己写的)
npm install webpack-dev-server -D
# contentBase配置参数
会自动从将这个目录下去找文件, webpack-dev-server 4
版本已移除了该属性,更换为static
target:'web', //默认:browserslist, web node
devServer: {
contentBase: "./public", // 自动从这个目录下去找资源内容, 更换为static
hot: true, // 模块热替换HMR
host: "0.0.0.0",
port: 7777,
open: true,
// compress: true,
proxy: {
"/api": {
target: "http://localhost:8888",
pathRewrite: {
"^/api": "" //替换掉api
},
secure: false, // 访问https
changeOrigin: true
}
}
},
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- index.html页面里面会引入一些外部的js、图片等资源,在打包阶段,可以用copy-webpack-plugin插件进行拷贝,但是在开发阶段,经常拷贝会影响开发效率,可以指定
contentBase
, express服务器会从这个目录下去找资源 - 开发阶段,注释掉CopyWebpackPlugin相关配置,而指定contentBase,打包阶段,再打开CopyWebpackPlugin;
# hot参数
开启热替换HMR, 一般情况下,还需要设置target (node, web)
热替换
会保留浏览器之前的状态数据,浏览器并没有刷新。热加载
是修改了文件,不需要重新编译,浏览器会刷新需要指定哪些模块(webpack认为文件就是一个模块)需要热替换。 全局变量module
vue-loader已对所有的vue文件进行了hmr热替换操作
react-refresh也作了hmr热替换处理
if (module.hot) { module.hot.accept("./js/element.js", () => { console.log("element模块发生更新了"); }) }
1
2
3
4
5
# HMR原理
webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket服务(net.Socket);
express server负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析);
HMR Socket Server,是一个socket的长连接:
长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端);
当服务器监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk);
通过长连接,可以直接将这两个文件主动发送给客户端(浏览器);
浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新;
参考
Target | webpack (opens new window):
Hot Module Replacement | webpack (opens new window)
# compress
是否为静态文件开启gzip compression,默认false
DevServer | webpack (opens new window)
# port
# open
# Proxy
**target:**表示的是代理到的目标地址,比如 /api/moment会被代理到 http://localhost:8888/api/moment;
**pathRewrite:**默认情况下,我们的 /api 也会被写入到URL中,如果希望删除,可以使用pathRewrite;
**secure:**默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;
**changeOrigin:**它表示是否更新代理后请求的headers中host地址;
# historyApiFallback
historyApiFallback是开发中一个非常常见的属性,它主要的作用是解决SPA页面在路由跳转之后,进行页面刷新时,返回404的错误。
boolean值:默认是false
如果设置为true,那么在刷新时,返回404错误时,会自动返回 index.html 的内容;
object类型的值,可以配置rewrites属性(了解):
p可以配置from来匹配路径,决定要跳转到哪一个页面;
n 事实上devServer中实现historyApiFallback功能是通过connect-history-api-fallback库的:
可以查看bripkens/connect-history-api-fallback (opens new window) 文档
# 四、resolve配置
- resolve可以帮助webpack从每个 require/import 语句中,找到需要引入到合适的模块代码;
- webpack 使用 webpack/enhanced-resolve (opens new window)来解析文件路径;
webpack能解析三种文件路径:
绝对路径
- 由于已经获得文件的绝对路径,因此不需要再做进一步解析。
相对路径
在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录;
在 import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径;
模块路径
在 resolve.modules中指定的所有目录检索模块;
默认值是 ['node_modules'],所以默认会从node_modules中查找文件;
我们可以通过设置别名的方式来替换初识模块路径,具体后面讲解alias的配置;
当引入的是目录时,会自动找index文件
当引入的是文件时,会自动加上默认后缀去匹配,默认后缀为
['.wasm', '.mjs', '.js', '.json']
;要快速定位到文件目录,可以设置别名alias
resolve: {
extensions: ['.wasm',".mjs", ".js",".json", ".vue", ".ts", ".jsx", ".tsx"],
alias: {
"@": path.resolve(__dirname, "./src"),
"js": path.resolve(__dirname, "./src/js")
}
},
2
3
4
5
6
7
# 五、多环境配置
目前我们所有的webpack配置信息都是放到一个配置文件中的:webpack.config.js,当配置越来越多时,这个文件会变得越来越不容易维护; 并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的;
那么,在启动时如何可以区分不同的配置呢?
方案一:编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可;
方式二:使用相同的一个入口配置文件,通过设置参数来区分它们;
"scripts": {
"build": "webpack --config ./config/webpack.prod.config.js",
"serve": "webpack serve --config ./config/webpack.dev.config.js"
},
2
3
4
# 配置文件的合并
我们创建三个文件:
webpack.common.conf.js、webpack.dev.conf.js、webpack.prod.conf.js
npm i -D webpack-merge
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.config');
export default merge(commonConfig, {
}) ;
2
3
4
5
# Webpack的代码分包
默认的打包过程:
默认情况下,在构建整个组件树的过程中,因为组件和组件之间是通过模块化直接依赖的,那么webpack在打包时就会将组件模块打包到一起(比如一个app.js文件中);这个时候随着项目的不断庞大,app.js文件的内容过大,会造成首屏的渲染速度变慢;
打包时,代码的分包:
所以,对于一些不需要立即使用的组件,我们可以单独对它们进行拆分,拆分成一些小的代码块chunk.js;
这些chunk.js会在需要时从服务器加载下来,并且运行代码,显示对应的内容;n
那么webpack中如何可以对代码进行分包呢?
# 三、Webpack 开发环境的基本配置
webpack.config.js 是 webpack 的配置文件。
作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)
所有构建工具都是基于 nodejs 平台运行的,模块化默认采用 commonjs。
开发环境配置主要是为了能让代码运行。主要考虑以下几个方面:
- 打包样式资源
- 打包 html 资源
- 打包图片资源
- 打包其他资源
- devServer
下面是一个简单的开发环境webpack.confg.js配置文件
// resolve用来拼接绝对路径的方法
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引用plugin
module.exports = {
// webpack配置
entry: './src/js/index.js', // 入口起点
output: {
// 输出
// 输出文件名
filename: 'js/build.js',
// __dirname是nodejs的变量,代表当前文件的目录绝对路径
path: resolve(__dirname, 'build'), // 输出路径,所有资源打包都会输出到这个文件夹下
},
// loader配置
module: {
rules: [
// 详细的loader配置
// 不同文件必须配置不同loader处理
{
// 匹配哪些文件
test: /\.less$/,
// 使用哪些loader进行处理
use: [
// use数组中loader执行顺序:从右到左,从下到上,依次执行(先执行css-loader)
// style-loader:创建style标签,将js中的样式资源插入进去,添加到head中生效
'style-loader',
// css-loader:将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader',
// less-loader:将less文件编译成css文件,需要下载less-loader和less
'less-loader'
],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
// url-loader:处理图片资源,问题:默认处理不了html中的img图片
test: /\.(jpe?g|png|gif|svg)$/,
// 需要下载 url-loader file-loader
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理,优点:减少请求数量(减轻服务器压力),缺点:图片体积会更大(文件请求速度更慢)
// base64在客户端本地解码所以会减少服务器压力,如果图片过大还采用base64编码会导致cpu调用率上升,网页加载时变卡
limit: 8 * 1024,
// 给图片重命名,[hash:10]:取图片的hash的前10位,[ext]:取文件原来扩展名
name: '[hash:10].[ext]',
// 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是conmonjs,解析时会出问题:[object Module]
// 解决:关闭url-loader的es6模块化,使用commonjs解析
esModule: false,
outputPath: 'imgs',
},
},
{
test: /\.html$/,
// 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
loader: 'html-loader',
},
// 打包其他资源(除了html/js/css资源以外的资源)
{
// 排除html|js|css|less|jpe?g|png|gif文件
exclude: /\.(html|js|css|less|jpe?g|png|gif)/,
// file-loader:处理其他文件
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media',
},
},
],
},
// plugin的配置
plugins: [
// html-webpack-plugin:默认会创建一个空的html文件,自动引入打包输出的所有资源(JS/CSS)
// 需要有结构的HTML文件可以加一个template
new HtmlWebpackPlugin({
// 复制这个./src/index.html文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html',
}),
],
// 模式
mode: 'development', // 开发模式
// 开发服务器 devServer:用来自动化,不用每次修改后都重新输入webpack打包一遍(自动编译,自动打开浏览器,自动刷新浏览器)
// 特点:只会在内存中编译打包,不会有任何输出(不会像之前那样在外面看到打包输出的build包,而是在内存中,关闭后会自动删除)
// 启动devServer指令为:npx webpack-dev-server
devServer: {
// 项目构建后路径
contentBase: resolve(__dirname, 'build'),
// 启动gzip压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true,
},
}
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
# 四、Webpack 生产环境的基本配置
而生产环境的配置需要考虑以下几个方面:
- 提取 css 成单独文件
- css 兼容性处理
- 压缩 css
- js 语法检查
- js 兼容性处理
- js 压缩
- html 压缩
下面是一个基本的生产环境下的webpack.config.js配置
const { resolve } = require('path')
const MiniCssExtractorPlugin = require('mini-css-extract-plugin')
const OptimiziCssAssetsWebpackPlugin = require('optimizi-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 定义node.js的环境变量,决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
// 复用loader的写法
const commonCssLoader = [
// 这个loader取代style-loader。作用:提取js中的css成单独文件然后通过link加载
MiniCssExtractPlugin.loader,
// css-loader:将css文件整合到js文件中
// 经过css-loader处理后,样式文件是在js文件中的
// 问题:1.js文件体积会很大2.需要先加载js再动态创建style标签,样式渲染速度就慢,会出现闪屏现象
// 解决:用MiniCssExtractPlugin.loader替代style-loader
'css-loader',
/*
postcss-loader:css兼容性处理:postcss --> 需要安装:postcss-loader postcss-preset-env
postcss需要通过package.json中browserslist里面的配置加载指定的css兼容性样式
在package.json中定义browserslist:
"browserslist": {
// 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
"development": [ // 只需要可以运行即可
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境。默认是生产环境
"production": [ // 需要满足绝大多数浏览器的兼容
">0.2%",
"not dead",
"not op_mini all"
]
},
*/
{
loader: 'postcss-loader',
options: {
ident: 'postcss', // 基本写法
plugins: () => [
// postcss的插件
require('postcss-preset-env')(),
],
},
},
]
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader'],
},
/*
正常来讲,一个文件只能被一个loader处理
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
先执行eslint再执行babel(用enforce)
*/
{
/*
js的语法检查: 需要下载 eslint-loader eslint
注意:只检查自己写的源代码,第三方的库是不用检查的
airbnb(一个流行的js风格) --> 需要下载 eslint-config-airbnb-base eslint-plugin-import
设置检查规则:
package.json中eslintConfig中设置
"eslintConfig": {
"extends": "airbnb-base", // 继承airbnb的风格规范
"env": {
"browser": true // 可以使用浏览器中的全局变量(使用window不会报错)
}
}
*/
test: /\.js$/,
exclude: /node_modules/, // 忽略node_modules
enforce: 'pre', // 优先执行
loader: 'eslint-loader',
options: {
// 自动修复
fix: true,
},
},
/*
js兼容性处理:需要下载 babel-loader @babel/core
1. 基本js兼容性处理 --> @babel/preset-env
问题:只能转换基本语法,如promise高级语法不能转换
2. 全部js兼容性处理 --> @babel/polyfill
问题:只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了
3. 需要做兼容性处理的就做:按需加载 --> core-js
*/
{
// 第三种方式:按需加载
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设:指示babel做怎样的兼容性处理
presets: [
'@babel/preset-env', // 基本预设
{
useBuiltIns: 'usage', //按需加载
corejs: { version: 3 }, // 指定core-js版本
targets: { // 指定兼容到什么版本的浏览器
chrome: '60',
firefox: '50',
ie: '9',
safari: '10',
edge: '17'
},
},
],
},
},
{
// 图片处理
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false, // 关闭url-loader默认使用的es6模块化解析
},
},
// html中的图片处理
{
test: /\.html$/,
loader: 'html-loader',
},
// 处理其他文件
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media',
},
},
],
},
plugins: [
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/built.css',
}),
// 压缩css
new OptimiziCssAssetsWebpackPlugin(),
// HtmlWebpackPlugin:html文件的打包和压缩处理
// 通过这个插件会自动将单独打包的样式文件通过link标签引入
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true,
},
}),
],
// 生产环境下会自动压缩js代码
mode: 'production',
}
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
162
163
164
165
166
167
168
169
170
171
172
# webpack配置常见问题
# 1、.mjs文件import js文件时,不带js后缀报错问题
描述:在集成element-plus时,出现以下错误; 修改其源码,import dayjs的文件加上后缀.js,错误消失
# webpack构建多页面
只需要将多个js源文件,进行打包生成一个个对应的js文件,然后html页面引入对应的js文件,形成多页面
entry: {index: "./src/pages/index.js", list: "./src/pages/list.js"},
output: {
path: path.resolve(__dirname, "./dist"),
filename: "[name].bundle.js",
chunkFilename: "[id].chunk.js"
},
new HtmlWebpackPlugin({
template: './src/pages/user/list.html',
filename:'userList.html',//打包好后,新建的html名字
chunks: ['list'],
}),
2
3
4
5
6
7
8
9
10
11
12
- 多个页面的话,需要设置多个HtmlWebpackPlugin,且需要指定生成后的html文件名称,默认是index.html
- HtmlWebpackPlugin插件三个关键属性: template、chunks (数组,可指定html引入哪些入口js)、filename
抽取方法实现自动扫描目录下面的html文件作为模板, 下面的js作为入口
const path = require('path')
const glob = require('glob'); // 扫描文件工具类库
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 入口文件
const entry = {}
// 入口文件对应的模板
const entryTemplate = []
exports.entry = () => {
seekAllFile()
setEntryTemplate()
return { entry, entryTemplate }
}
// 得到pages文件夹下所有入口文件(支持无限嵌套), 路径为相对当前js文件的位置
var rootPath = "src/pages/";
const seekAllFile = (parent = rootPath + '**/*.js') => {
//const fileList = glob.sync(path.resolve(__dirname, parent));
const fileList = glob.sync(parent);
console.log(fileList, "目录");
if (fileList.length > 0) {
fileList.forEach( file => {
const filename = file.split(rootPath)[1] // 截取掉根目录
.replace( new RegExp(/\//, "g"), '-') // 重命名成 user-edit.js形式
.split(".js")[0]; // 去掉js后缀
entry[filename] = path.join(__dirname, file);
console.log(filename);
})
} else {
return
}
}
// 设置入口文件的模板文件(附加功能)
const setEntryTemplate = () => {
Object.keys(entry).forEach(key => {
console.log("entry[key]: "+key);
entryTemplate.push(new HtmlWebpackPlugin({
template: entry[key].replace(/\.js/, '.html'),
filename: key + '.html',
chunks: [key],
inject: 'body', // 静态文件放入body后
minify: false // 是否压缩html
}))
})
}
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
参考文章:
webpack - 标签 - sloong - 博客园 (cnblogs.com) (opens new window)
html-webpack-plugin详解 - wonyun - 博客园 (cnblogs.com) (opens new window)
参考文档与视频:
官方| webpack 中文文档 (docschina.org) (opens new window)
尚硅谷最新版Webpack5实战教程(从入门到精通)_哔哩哔哩_bilibili (opens new window)
geektime-webpack-course: 《玩转webpack》极客时间课程源码和课件 (gitee.com) (opens new window)
【No1442】深入掌握Webpack5构建工具视频教程
下载地址:
网盘链接:https://pan.baidu.com/s/1ugdXmKfOWof1HrCH1LapCQ
提取密码:469m
解压密码:www.javaxxz.com_)O*I7hIk
2
3
4
5