Webpack5学习笔记
1. WebPack简介
WebPack是一种前端资源构建工具,一个静态模块打包器。
插一句:所有构建工具都是基于nodejs平台运行的,默认采用commonjs模块规范。
五大核心概念:
1.1 Entry
入口,指示Webpack以哪个文件为入口起点开始打包,构建内部依赖图。
1.2 Output
输出指示Webpack打包后的资源输出到哪里,以及如何命名。
1.3 Loader
Loader让Webpack能够去处理那些非Javascript文件(Webpack自身只理解Javascript, json),如css,图片等文件,类似翻译官。
1.4 Plugins
插件可以用于执行范围更广的任务。插件范围包括:从打包优化到压缩,重新定义环境中的变量等。
1.5 Mode
模式(Mode)指示Webpack使用相应模式的配置。
development:能让代码本地调试运行的环境。
production:能让代码优化上线运行的环境。
两者均会启用不同的插件(Plugins)
Loader和Plugins的区别
1. 从功能角度区分
Loader虽然扩展了Webpack,但它只专注于转化文件这一领域,完成压缩,打包,翻译。从字面意思上也能看出来,loader是用于加载文件的,将文件转化并加载进来。
如:
css-loader和style-loader模块是为了打包css的
babel-loader和babel-core模块时为了把ES6的代码转成ES5
url-loader和file-loader是把图片进行打包的。
Plugins也是扩展了Webpack功能,但是它是作用于Webpack本身的,功能要更加丰富,用于处理loader不能处理的事。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。
2. 从运行时角度区分
loader运行在打包文件之前;
plugins在整个编译周期都起作用
2. Webpack初体验
2.1 运行指令
1#初始化一个npm项目
2 npm init
3
4#本地安装
5 npm i webpack webpack-cli -D
6 # -D 会将包信息写入到package.json中的devDependencies中
7 # devDependencies 里面的插件只用于开发环境,不用于生产环境,而 dependencies 是需要发布到生产环境的。
8
9#开发环境打包
10 webpack --entry ./src/index.js -o ./build --output-filename built.js --mode=development
11#webpack会以./src/index.js为入口,开始打包,打包后输出到./build/built.js
12#整体打包环境是开发环境。
13
14#生产环境打包
15 webpack --entry ./src/index.js -o ./build --output-filename built.js --mode=production
小结:
- Webpack能处理js/json资源,不能处理css/img等其他资源
- 生产环境和开发环境将ES6模块编译成浏览器能识别的模块
- 生产环境比开发环境多一个压缩js代码
3. 打包样式资源
上一章实践可知,Webpack无法处理css/img等样式资源,因此需要借助Loader进行文件的转化与加载处理。
3.1 webpack配置文件
webpack.config.js : webpack的配置文件
作用:指示Webpack干哪些活(当运行Webpack指令时,会加载里面的配置)
1// resolve 是nodejs path模块中用来拼接绝对路径的方法
2const { resolve } = require('path');
3
4module.exports = {
5 // Webpack配置
6 // 入口起点
7 entry: './src/index.js',
8 // 输出
9 output:{
10 // 输出文件名
11 filename: 'built.js',
12 // 输出路径
13 // __dirname是nodejs变量,代表当前文件所在目录
14 path: resolve(__dirname, 'build')
15 },
16 // loader的配置
17 module:{
18 rules:[
19 // 详细地loader配置
20 // *** 不同文件 需要配置不同loader ***
21 {
22 //匹配哪些文件
23 test: /\.css$/,
24 // 使用哪些loader去处理
25 use:[
26 // use数组中loader执行顺序是从尾部到前面依次执行
27 // 2. 创建style标签,将js中的样式资源插入,添加到页面head中生效
28 'style-loader',
29 // 1. 将css文件以字符串的形式编成commonjs模块加载到js中,里面内容是样式字符串
30 'css-loader'
31 ]
32 },
33 {
34 test: /\.less$/,
35 use:[
36 'style-loader',
37 'css-loader',
38 // 将less文件编译成css文件
39 'less-loader'
40 ]
41 }
42 ]
43 },
44 // plugins的配置
45 plugins:[
46 // 详细plugins配置
47
48 ],
49 // 模式
50 mode: 'development', // 开发模式
51 // mode: 'production'
52}
4. 打包html资源
借助plugin实现打包, 这里使用html-webpack-plugin实现.
loader使用:1. 下载; 2. 使用(配置loader)
plugins使用:1. 下载; 2. 引入; 3. 使用
4.1 webpack配置
1 const {resolve} = require('path');
2 const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4 module.exports={
5 entry: './src/index.js',
6 output:{
7 filename:'built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module:{
11 rules:[
12 // loader配置
13 {
14
15 }
16 ]
17 },
18 plugins:[
19 // 功能: 默认会创建一个空的html,自动引入打包输出的所有资源(JS/CSS)
20 // 需要有自己写的结构的html文件
21 // template参数: 复制index.html文件, 并自动引入所有资源(js等)
22 new HtmlWebpackPlugin({
23 template: './src/index.html'
24 })
25 ],
26 mode:'development'
27 }
5. 打包图片资源
使用loader处理图片资源
1. 处理css中图片资源
在Webpack5中url-loader以及file-loader已经废弃,如果一定要使用需要加上一些参数,见下面代码:
*在Webpack5中使用asset来实现图片资源打包(type:‘asset/resource’)
2. 处理html中img标签内的图片
使用html-loader处理, 负责引入img,从而能被url-loader进行处理
1 const {resolve} = require('path');
2 const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4 module.exports = {
5 entry: './src/index.js',
6 output: {
7 filename: 'built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module:{
11 rules:[
12 {
13 test: /\.less$/,
14 // 使用多个loader使用use, 从数组最后往前依次使用
15 use:[
16 'style-loader',
17 'css-loader',
18 'less-loader'
19 ]
20 },
21 {
22 test: /\.(jpg|png|gif)$/,
23 // 只是用一个loader
24 // 需要下载url-loader和file-loader
25 loader: 'url-loader',
26 options: {
27 // 图片大小小于8kb, 就会被当成base64处理
28 // 优点: 减少请求数量(减轻服务器压力)
29 // 缺点: 图片体积更大
30 limit: 30 * 1024,
31 // webpack5使用要加
32 // 因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
33 // 需要关闭utl-loader的es6模块化,使用commonjs解析
34 esModule: false,
35 // 给图片重命名
36 // [ext]取文件原扩展名
37 name: '[hash:10].[ext]'
38 },
39 // Webpack5使用要加
40 type: 'javascript/auto'
41 // use: [
42 // 'url-loader'
43 // ]
44 },
45 // Webpack5打包图片资源
46 // {
47 // test: /\.(jpg|png|gif)$/,
48 // type:'asset/resource'
49 // {
50 test: /\.html$/,
51 loader: 'html-loader'
52 }
53 ]
54 },
55 plugins:[
56 new HtmlWebpackPlugin({
57 template: './src/index.html'
58 })
59 ],
60 mode: 'development'
61 }
6. 打包其他资源
打包如字体等不需要做任何处理,原封不动处理的资源
使用file-loader处理除html/js/css以外的其他资源
Webpack5貌似不需要使用file-loader也可以直接打包
或者使用type:‘asset/resource’来处理字体文件
1 const {resolve} = require('path');
2 const HtmlWebpaclPlugin = require('html-webpack-plugin');
3
4 module.exports = {
5 entry: './src/index.js',
6 output: {
7 filename: 'built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module:{
11 rules:[
12 {
13 test: /\.css$/,
14 use: [
15 'style-loader',
16 'css-loader'
17 ]
18 },
19 // 打包其他资源,除了html/js/css 以外的其他资源
20 {
21 exclude: /\.(css|js|html)/,
22 loader: 'file-loader'
23 }
24 ]
25 },
26 plugins:[
27 new HtmlWebpaclPlugin({
28 template: './src/index.html'
29 })
30 ],
31 mode: 'development'
32 }
7. devServer
开发服务器 devServer: (用来自动化编译,自动打开浏览器,自动刷新浏览器),即热更新
特点: 只在内存中编译打包, 而不会输出
启动指令: npx webpack-dev-Server
需要下载包webpack-dev-server
1 const {resolve} = require('path');
2 const HtmlWebpaclPlugin = require('html-webpack-plugin');
3
4 module.exports = {
5 entry: './src/index.js',
6 output: {
7 filename: 'built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module:{
11 rules:[
12 {
13 test: /\.css$/,
14 use: [
15 'style-loader',
16 'css-loader'
17 ]
18 },
19 // 打包其他资源,除了html/js/css 以外的其他资源
20 // Webpack5用法
21 {
22 exclude: /\.(css|js|html)/,
23 type: 'asset/resource'
24 }
25 ]
26 },
27 plugins:[
28 new HtmlWebpaclPlugin({
29 template: './src/index.html'
30 })
31 ],
32 mode: 'development',
33
34 // 开发服务器 devServer: (用来自动化编译,自动打开浏览器,自动刷新浏览器)
35 // 特点: 只会在内存中编译打包, 不会有任何输出
36 devServer:{
37 // Webpack4用法
38 // contentBase: resolve(__dirname, 'build'),
39 // Webpack5用法
40 static: resolve(__dirname, 'build'),
41 //启动gzip压缩, 使代码体积更小,启动更快
42 compress: true,
43 //端口号
44 port: 3000,
45 // 自动打开浏览器
46 open: true
47 }
48 }
8. 开发环境基本配置
整合前面几个章节学习内容, 集成为统一开发环境
输出资源到指定目录:
Webapck4中使用options.outputPath来实现
Webpack5中使用generator.outputPath来实现
1 const { resolve } = require('path');
2 const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4 module.exports = {
5 entry: './src/js/index.js',
6 output: {
7 filename: 'js/built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module: {
11 rules: [
12 // loader配置
13 {
14 // 处理less资源
15 test: /\.less$/,
16 use: [
17 'style-loader',
18 'css-loader',
19 'less-loader'
20 ]
21 },
22 {
23 // 处理css资源
24 test: /\.css$/,
25 use: [
26 'style-loader',
27 'css-loader'
28 ]
29 },
30 {
31 // 处理图片资源
32 test: /\.(jpg|png|gif)$/,
33 // 在Webpack5中借助内置asset-module来实现
34 type: 'asset/resource',
35 generator:{
36 filename: 'asset/[hash:6][ext]',
37 }
38 // 或者借助url-loader来实现
39 // loader: 'url-loader',
40 // options: {
41 // // 图片大小小于8kb, 就会被当成base64处理
42 // // 优点: 减少请求数量(减轻服务器压力)
43 // // 缺点: 图片体积更大
44 // limit: 30 * 1024,
45 // // webpack5使用要加
46 // // 因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
47 // // 需要关闭utl-loader的es6模块化,使用commonjs解析
48 // esModule: false,
49 // // 给图片重命名
50 // // [ext]取文件原扩展名
51 // name: '[hash:10].[ext]'
52 // },
53 // // Webpack5使用要加
54 // type: 'javascript/auto'
55 },
56 {
57 // 处理html中的img资源
58 test: /\.html$/,
59 loader: 'html-loader',
60 },
61 {
62 // 处理其他资源
63 exclude: /\.(html|js|css|less|jpg|png|jpg|gif)/,
64 // 在Webpack5中借助内置asset-module来实现
65 type: 'asset/resource',
66 generator:{
67 filename: 'asset/[hash:6].[ext]'
68 }
69 // Webpack4用法
70 // loader: 'file-loader'
71 }
72 ]
73 },
74 plugins: [
75 new HtmlWebpackPlugin({
76 template: './src/index.html'
77 })
78 ],
79 mode: 'development',
80
81 devServer:{
82 // Webpack4用法
83 // contentBase: resolve(__dirname, 'build'),
84 // Webpack5用法
85 static: resolve(__dirname, 'build'),
86 // 启动gzip压缩, 使代码体积更小,启动更快
87 compress: true,
88 // 端口号
89 port: 3000,
90 // 自动打开浏览器
91 open: true
92 }
93 }
9. 提取CSS成单独文件
9.1. mini-css-extract-plugin插件提取CSS
通常Webpack会将CSS样式文件打包进js文件中, 这样会造成js文件体积过大, 加载缓慢, 不利于生产环境上线使用
使用mini-css-extract-plugin来进行提取
**注意:**css-loader将css处理整合进js文件中,style-loader创建style标签并插入到html中,因此处理css文件时,不能使用style-loader,
使用MiniCssExtractPlugin.loader取代style-loader,作用是提取js中的css成单独文件。
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4
5module.exports={
6 entry: './src/js/index.js',
7 output:{
8 filename:'js/built.js',
9 path:resolve(__dirname,'build')
10 },
11 module:{
12 rules:[
13 {
14 test: /\.css$/,
15 use:[
16 // 'style-loader',
17 // 这个loader取代style-loader,作用是提取js中的css成单独文件
18 MiniCssExtractPlugin.loader,
19 'css-loader'
20 ]
21 }
22 ]
23 },
24 plugins:[
25 new HtmlWebpackPlugin({
26 template: './src/index.html'
27 }),
28 // 提取css成单独文件
29 new MiniCssExtractPlugin({
30 // 对输出文件进行重命名并指定目录
31 filename: 'css/built.css'
32 })
33 ],
34 mode:'development'
35}
9.2. CSS兼容性处理
使用postcss-loader 以及 postcss-preset-env两个包
具体用法可以去github上看
在package.json中编写browserlist配置,postcss-preset-env插件帮助postcss找到对应browserlist配置,并进行css兼容性处理
1"browserslist":{
2 "development":[
3 "last 1 chrome version",
4 "last 1 firefox version",
5 "last 1 safari version"
6 ],
7 "production":[
8 ">0.2%",
9 "no dead",
10 "not op_mini all"
11 ]
12 }
postcss.config.js编写
1module.exports={
2 plugins:[
3 require('postcss-preset-env')
4 ]
5}
webpack.config.js编写
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4
5// 设置node环境变量
6process.env.NODE_ENV = 'production'
7
8module.exports={
9 entry: './src/js/index.js',
10 output:{
11 filename:'js/built.js',
12 path:resolve(__dirname,'build')
13 },
14 module:{
15 rules:[
16 {
17 test: /\.css$/,
18 use:[
19 // 'style-loader',
20 // 这个loader取代style-loader,作用是提取js中的css成单独文件
21 MiniCssExtractPlugin.loader,
22 'css-loader',
23
24 // css兼容性处理: postcss --> postcss-loader postcss-preset-env
25 // 帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容样式
26 // "browserslist":{
27 // 开发环境 指node环境变量
28 // "development":[
29 // "last 1 chrome version",
30 // "last 1 firefox version",
31 // "last 1 safari version"
32 // ],
33 // 默认生产环境
34 // "production":[
35 // ">0.2%",
36 // "no dead",
37 // "not op_mini all"
38 // ]
39 // }
40
41 // 方法一 使用loader的默认配置
42 // 'postcss-loader',
43 // 方法二 修改loader的配置
44 {
45 loader:'postcss-loader',
46 }
47 ]
48 }
49 ]
50 },
51 plugins:[
52 new HtmlWebpackPlugin({
53 template: './src/index.html'
54 }),
55 new MiniCssExtractPlugin({
56 // 对输出文件进行重命名并指定目录
57 filename: 'css/built.css'
58 })
59 ],
60 mode:'development'
61}
也可以将postcss.config.js的内容写入webpack配置
1 {
2 loader: 'postcss-loader',
3 options: {
4 postcssOptions: {
5 plugins: [
6 require('postcss-preset-env')()
7 ]
8 }
9 }
10 }
9.3. 压缩CSS
一般类似于兼容性处理,文件转换都是靠loader来完成,而压缩等都是靠插件plugin来实现的
主要使用optimize-css-assets-webpack-plugin这一插件
1plugins:[
2 new HtmlWebpackPlugin({
3 template: './src/index.html'
4 }),
5 // 提取css成单独文件
6 new MiniCssExtractPlugin({
7 // 对输出文件进行重命名并指定目录
8 filename: 'css/built.css'
9 }),
10 // 压缩css
11 new OptimizeCssAssetsWebpackPlugin()
12]
10. JS语法检查
下载eslint, eslint-webpack-plugin, eslint-config-airbnb-base, eslint-plugin-import四个包
eslint-webpack-plugin 用于替代eslint-loader(即将废弃)
在package.json中eslintConfig属性进行设置, eslint-plugin-import用于导入该设置, 利用airbnb-base用于规则设置
1"eslintConfig":{
2 "extends": "airbnb-base"
3}
webpack.config.js配置
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const ESLintWebpackPlugin = require('eslint-webpack-plugin');
4
5module.exports={
6 entry: './src/js/index.js',
7 output: {
8 filename: 'js/built.js',
9 path: resolve(__dirname, 'build')
10 },
11 module:{
12 rules:[
13 ]
14 },
15 plugins:[
16 new HtmlWebpackPlugin({
17 template: './src/index.html'
18 }),
19 new ESLintWebpackPlugin({
20 // 自动修复
21 fix: true,
22 // 检查文件
23 extensions: ['js'],
24 // 排除文件夹
25 exclude: '/node_modules/'
26 })
27 ],
28 mode: 'development'
29}
1// eslint-disable-next-line
2#上述注释可以用于忽略eslint检查
问题: eslint不认识window, navigator等浏览器全局变量, 需要修改配置
1 "eslintConfig": {
2 "extends": "airbnb-base",
3 "env":{
4 "browser": true
5 }
6 }
11. JS兼容性处理
JS兼容性问题,例如:下述代码在IE浏览器会报错
1const add = (x, y) => x + y;
2
3// eslint-disable-next-line
4console.log(add(2, 3));
利用babel-loader进js的兼容性处理
11.1. 选用**@babel/preset-env进行基本**js兼容性处理
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const ESLintWebpackPlugin = require('eslint-webpack-plugin');
4
5module.exports={
6 entry: './src/js/index.js',
7 output: {
8 filename: 'js/built.js',
9 path: resolve(__dirname, 'build')
10 },
11 module:{
12 rules:[
13 {
14 test: /\.js$/,
15 exclude: /node_modules/,
16 loader: 'babel-loader',
17 options: {
18 // 预设,指示babel做怎样的兼容性处理
19 presets: ['@babel/preset-env']
20 }
21 }
22 ]
23 },
24 plugins:[
25 new HtmlWebpackPlugin({
26 template: './src/index.html'
27 }),
28 new ESLintWebpackPlugin({
29 fix: true,
30 extensions: ['js'],
31 exclude: '/node_modules/'
32 })
33 ],
34 mode: 'development'
35}
11.2. 利用**@babel/polyfill进行全部**的js兼容性处理
在js文件内引入即可
1import '@babel/polyfill';
11.3. 按需加载兼容性处理core-js
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const ESLintWebpackPlugin = require('eslint-webpack-plugin');
4
5module.exports={
6 entry: './src/js/index.js',
7 output: {
8 filename: 'js/built.js',
9 path: resolve(__dirname, 'build')
10 },
11 module:{
12 rules:[
13 {
14 test: /\.js$/,
15 exclude: /node_modules/,
16 loader: 'babel-loader',
17 options: {
18 // 预设,指示babel做怎样的兼容性处理
19 presets: [
20 ['@babel/preset-env',
21 {
22 // 按需加载
23 useBuiltIns: 'usage',
24 //指定core-js版本
25 corejs: {
26 version: 3
27 },
28 // 指定兼容性做到哪个浏览器
29 targets: {
30 chrome: '60',
31 firefox: '60',
32 ie: '9',
33 safari: '10',
34 edge: '17'
35 }
36 }
37 ]
38 ]
39 }
40 }
41 ]
42 },
43 plugins:[
44 new HtmlWebpackPlugin({
45 template: './src/index.html'
46 }),
47 new ESLintWebpackPlugin({
48 fix: true,
49 extensions: ['js'],
50 exclude: '/node_modules/'
51 })
52 ],
53 mode: 'development'
54}
12. JS和html压缩
12.1. JS压缩
在生产环境下js自动压缩
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports={
5 entry: './src/js/index.js',
6 output: {
7 filename: 'js/built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module:{
11 rules:[
12
13 ]
14 },
15 plugins:[
16 new HtmlWebpackPlugin({
17 template: './src/index.html'
18 })
19 ],
20 // 生产环境下自动压缩js代码
21 mode: 'production'
22}
12.2. Html压缩
只需要配置HtmlWebpackPlugin
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports={
5 entry: './src/js/index.js',
6 output: {
7 filename: 'js/built.js',
8 path: resolve(__dirname, 'build')
9 },
10 module:{
11 rules:[
12
13 ]
14 },
15 plugins:[
16 new HtmlWebpackPlugin({
17 template: './src/index.html',
18 minify: {
19 // 移除空格
20 collapseWhitespace: true,
21 // 移除注释
22 removeComments: true
23 }
24 })
25 ],
26 // 生产环境下自动压缩js代码
27 mode: 'production'
28}
13. 生产环境配置
包含了:
- CSS代码处理
- CSS兼容性配置
- CSS代码压缩
- JS语法检查
- js兼容性处理
- 图片处理
- js和html压缩
**注意:**正常来讲,一个文件只能被一个loader处理,当一个文件要被多个loader处理时,那么一定要指定loader的执行先后顺序
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3const MiniCssExtractPlugin = require('mini-css-extract-plugin')
4const commonCssLoader = {
5 loader: 'postcss-loader',
6 options: {
7 postcssOptions: {
8 plugins: [
9 require('postcss-preset-env')()
10 ]
11 }
12 }
13}
14const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
15const ESLintWebpackPlugin = require('eslint-webpack-plugin');
16const { Generator } = require('webpack');
17
18module.exports = {
19 entry: './src/js/index.js',
20 output:{
21 filename: 'js/built.js',
22 path: resolve(__dirname, 'build')
23 },
24 module: {
25 rules:[
26 {
27 test: /\.css$/,
28 use: [
29 MiniCssExtractPlugin.loader,
30 'css-loader',
31 // css兼容性处理
32 commonCssLoader
33 ]
34 },
35 {
36 test: /\.less$/,
37 use: [
38 MiniCssExtractPlugin.loader,
39 'css-loader',
40 commonCssLoader,
41 'less-loader'
42 ]
43 },
44 {
45 test: /\.js$/,
46 exclude: /node_module/,
47 loader: 'babel-loader',
48 options:{
49 presets: [
50 [
51 '@babel/preset-env',
52 {
53 // 按需加载
54 useBuiltIns: 'usage',
55 //指定core-js版本
56 corejs: {
57 version: 3
58 },
59 // 指定兼容性做到哪个浏览器
60 targets: {
61 chrome: '60',
62 firefox: '60',
63 ie: '9',
64 safari: '10',
65 edge: '17'
66 }
67 }
68 ]
69 ]
70 }
71 },
72 {
73 test: /\.(jpg|png|gif)/,
74 type: 'asset/resource',
75 generator: {
76 filename: 'img/[hash:6][ext]'
77 }
78 },
79 // html中图片处理
80 {
81 test: /\.html/,
82 loader: 'html-loader'
83 },
84 // 其他文件处理
85 {
86 exclude: /\.(js|css|less|html|jpg|jpg|gif)/,
87 type: 'asset/resource',
88 generator: {
89 filename: 'asset/[hash:6][ext]'
90 }
91 }
92 ]
93 },
94 plugins:[
95 new HtmlWebpackPlugin({
96 template: './src/index.html',
97 minify: {
98 collapseWhitespace: true,
99 removeComments: true
100 }
101 }),
102 new MiniCssExtractPlugin({
103 filename: 'css/built.css'
104 }),
105 new OptimizeCssAssetsWebpackPlugin(), // CSS压缩
106 new ESLintWebpackPlugin({
107 fix: true,
108 extensions: ['js'],
109 exclude: '/node_modules/'
110 })
111 ],
112 mode: 'production'
113}
14. Webpack性能优化
14.1. 开发环境性能优化
-
优化Webpack打包构建速度 (HMR)
HMR: hot module replacement 热更新
作用: 一个模块变化, 只会重新打包这一个模块,而不是打包所有
Webpack5自动开启
Webpack4开启:
1devServer:{ 2 // Webpack4用法 3 // contentBase: resolve(__dirname, 'build'), 4 // Webpack5用法 5 static: resolve(__dirname, 'build'), 6 // 启动gzip压缩, 使代码体积更小,启动更快 7 compress: true, 8 // 端口号 9 port: 3000, 10 // 自动打开浏览器 11 open: true, 12 // HMR 13 hot: true 14 }
样式文件: 可以使用HMR功能,因为’style-loader’内部实现了~
js文件: js文件变化时devServer会重新编译所有文件,虽然也能实现更新,但是默认不能使用HMR功能,即无法实现局部打包– > 需要修改代码,添加支持 HMR功能
修改:
1 if(module.hot){ 2 // 为true说明开启了hmr功能 3 module.hot.accept('./print.js', function(){ 4 // 方法会监听print.js文件变化,一但发生变化,其它默认不会重新打包构建 5 // 会执行后面的回调函数 6 print(); 7 }) 8 }
html文件: 默认不能使用HMR功能,同时会导致问题:修改html文件后浏览器并未自动刷新热更新 (不用做HMR)
修改:
1// Entry更改为数组, 添加html文件 2entry: ['./src/js/index.js', './src/index.html']
-
优化代码调试(source-map)
source-map: 一种提供源代码到构建后代码映射的技术 (如果构建后代码出错, 通过构建关系可以追踪到源代码错误)
配置:
1module.exports = { 2 ... 3 devtool: 'source-map' // 能够提示错误代码准确信息 和 源代码错误位置 4 ... 5} 6 7// source-map 参数 8// [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map 9// inline-source-map // source-map内联嵌入到js文件中,内联速度更快 * 能够提示错误代码准确信息 和 源代码错误位置 10// hidden-source-map // 外部生成 * 能够提示错误代码原因 和 但是没有错误位置,只能提示到构建后代码错误位置 11// eval-source-map // 内联,每一个js文件后会追加一个sourceMapURL,在eval函数中 * 能够提示错误代码准确信息 和 源代码错误位置 12// nosources-source-map // 外部生成 * 能够提示错误代码准确信息 和 但是没有任何源代码信息 13// cheap-source-map // 外部生成 * 能够提示错误代码准确信息 和 源代码错误位置(只能精确到行) 14// module-source-map // 外部生成 * 能够提示错误代码准确信息 和 源代码错误位置 15 16/* --- 开发环境 --- 17 速度快(eval>inline>cheap...) 18 eval-cheap-source-map 19 eval-source-map 一般用这个性价比最好 20 调试更友好 21 source-map 22 cheap-module-source-map 23 module会将loader的source-map加入 24 cheap-source-map 25 26 --- 生产环境 --- 27 考虑代码隐藏 28 nosources-source-map 29 hidden-source-map 30 31 不考虑inline 会使文件体积变大 32*/
14.2. 生产环境性能优化
-
优化Webpack打包构建速度
oneOf
babel缓存
多进程打包
externals
dll
-
优化代码运行性能
缓存(hash - chunkhash - contenthash)
tree shaking
code split
懒加载/预加载
pwa
15. 缓存
当生产环境构建编译时,缓存文件,下次编译时不变的代码直接使用缓存即可,加快构建速度
15.1. babel缓存
babel构建代码时,避免重复构建,可以使用babel缓存, 下次构建时针对不变的部分直接使用缓存
cacheDirectory: true
1{
2 test: /\.js$/,
3 exclude: /node_module/,
4 loader: 'babel-loader',
5 options:{
6 presets: [
7 [
8 '@babel/preset-env',
9 {
10 // 按需加载
11 useBuiltIns: 'usage',
12 //指定core-js版本
13 corejs: {
14 version: 3
15 },
16 // 指定兼容性做到哪个浏览器
17 targets: {
18 chrome: '60',
19 firefox: '60',
20 ie: '9',
21 safari: '10',
22 edge: '17'
23 }
24 }
25 ]
26 ],
27 // 开启babel缓存
28 cacheDirectory: true
29 }
30}
15.2. 文件资源缓存
假如开启node server, 由于浏览器文件资源缓存, 重新打包后刷新浏览器并不会显示新打包的效果
解决办法:
-
hash: webpack配置内文件名添加hash值
1output:{ 2 filename: 'js/built.[hash:10].js', // chunkhash ; contenthash 3 path: resolve(__dirname, 'build') 4 }
问题: js和css同时使用一个hash值,如果重新打包, 所有缓存都将失效, 因为不管文件变没变, 都会重新生成hash值
-
chunkhash: 根据chunk生成hash值, 如果打包来源于同一个chunk, 那么hash值一样
存在问题: css和js文件hash值还是一样, 因为css被js引入, 属于同一个chunk, 因此两者hash值还是一样
-
contenthash: 根据文件内容生成hash值, 不同文件hash值一定不一样, 内容不变, hash值不变
16. Tree Shaking
含义: 去除应用程序中没有使用到的代码
前提:
- 必须使用ES6模块化
- 开启production模式
则 webpack自动启用
在package.json中配置:
代表所有代码都没有副作用(都可以进行tree shaking)
可能会把css文件去除
1"sideEffects": false
改进:
1"sideEffects":["*.css"]
17. code split 代码分割
将代码打包输出分割为多个js文件
demo1: 多入口: 一个入口输出一个bundle
1entry: {
2 main: './src/js/index.js',
3 test: './src/js/test.js'
4},
5output:{
6 filename: 'js/[name].[contenthash:10].js', // 输出名字为entry指定的key
7 path: resolve(__dirname, 'build')
8}
demo2: 引入的node_modules代码单独打包输出, 多个库会合并打包成一个文件
自动分析多入口文件中有没有公共依赖, 如果有, 则打包成单独chunk
1optimization:{
2 splitChunks:{
3 chunks:'all'
4 }
5}
demo3: 通过js代码让某个文件单独打包成一个chunk
import 动态导入语法
1// 导入test文件内两个函数
2// webpackChunkName 指定打包后文件名
3import(/* webpackChunkName: 'test' */'./test').then(({mul, count}) => {
4 console.log(mul(2,3));
5 console.log(count(6,2));
6})
小结:
一般多入口不常用,通常使用单入口 + optimization + import动态导入
18. JS文件懒加载
需要时才加载
懒加载必然需要将js拆分为单独bundle, 因此可以使用import动态导入
1import(/* webpackChunkName: 'test' */ './test').then(({mul, count})=>{
2 console.log(mul(2, 3));
3})
预加载:
会在使用前, 提前加载js文件
会等其他资源加载完毕, 再加载
有兼容性问题, 慎用!
1import(/* webpackChunkName: 'test' , webpackPrefetch: true */'./test').then(({mul, count})=>{
2 console.log(mul(2, 3));
3})
19. PWA
渐进式网页开发应用程序
加载的网页离线也能访问
利用workbox-webpack-plugin插件
**注意: **serviceWorker必须运行在服务器端, 可以通过nodejs启动
webpack.config.js配置
1new WorkboxWebpackPlugin.GenerateSW({
2 // 帮助serviceWorker快速启动
3 // 删除旧的 serviceWorker
4 // 生成一个serviceWorker的配置文件
5 clientsClaim: true,
6 skipWaiting: true
7})
js代码注册serviceWorker代码运行
1// 注册serviceWorker
2// 处理兼容性问题
3if ('serviceWorker' in navigator) {
4 window.addEventListener('load', () => {
5 navigator.serviceWorker.register('/service-worker.js').then(() => {
6 console.log('注册成功!');
7 }).catch(() => {
8 console.log('注册失败');
9 });
10 });
11}
20. 多进程打包
使用thread-loader进行, 需要给哪一类文件开启多进程打包, 就在对应loader下面使用
只有总体打包消耗工作时间比较长才需要使用多进程打包
一般可以在babel打包的时候使用
1{
2 test: /\.js$/,
3 exclude: /node_module/,
4 use: [
5 // 开启多进程打包
6 'thread-loader',
7 {
8 loader: 'babel-loader',
9 options:{
10 presets: [
11 [
12 '@babel/preset-env',
13 {
14 // 按需加载
15 useBuiltIns: 'usage',
16 //指定core-js版本
17 corejs: {
18 version: 3
19 },
20 // 指定兼容性做到哪个浏览器
21 targets: {
22 chrome: '60',
23 firefox: '60',
24 ie: '9',
25 safari: '10',
26 edge: '17'
27 }
28 }
29 ]
30 ],
31 cacheDirectory: true
32 }
33 }
34 ],
35}
指定进程数量:
1{
2 loader:'thread-loader',
3 options:{
4 workers: 2
5 }
6}
21. externals
阻止指定module打包进bundle中, 比如希望通过cdn引入时
例如: 防止js中引入的jquery被打包:
1externals: {
2 // 忽略库名 -- npm包名
3 jquery: 'jQuery'
4}
22. dll
将多个库分别打包为单独chunk
解决重复打包第三方库的问题
optimization虽然也分离第三方库, 但是仍需要重新打包
webpack.dll.js配置编写
作用: 将第三方库单独输出为js
1const {resolve} = require('path');
2const webpack = require('webpack');
3
4/*
5 使用dll技术对某些库(第三方库)进行单独打包
6 运行指令: webpack --config webpack.dll.js
7*/
8module.exports = {
9 entry: {
10 // 最终打包生成的[name] --> jquery
11 // ['jquery'] --> 要打包的库是jquery
12 jquery: ['jquery']
13 },
14 output:{
15 filename: '[name].js',
16 path: resolve(__dirname, 'dll'),
17 library: '[name]_[hash]', // 打包的库里面向外暴露出去的内容叫什么名字
18 },
19 plugins:[
20 // 打包生成一个manifest.json文件,提供和jquery的映射
21 new webpack.DllPlugin({
22 name: '[name]_[hash]', // 映射库的暴露的内容
23 path: resolve(__dirname, 'dll/manifest.json')
24 })
25 ],
26 mode: 'production'
27}
webpack.config.js配置
作用:
- 告诉webpack哪些库不需要打包(之前已经导出为dll的库)
- 将第三方库输出到build下, 并在html中自动引入该第三方资源
1// 告诉webpack哪些库不参与打包,同时使用时名称也需要修改,但是最终并未引入包
2new webpack.DllReferencePlugin({
3 manifest: resolve(__dirname, 'dll/manifest.json')
4}),
5// 将某个文件打包输出出去,并在html中自动引入该资源
6new AddAssetHtmlWebpackPlugin({
7 filepath: resolve(__dirname, 'dll/jquery.js'),
8 outputPath: 'auto'
9})
23. entry详细配置
-
string写法 –> ‘./src/index.js’
打包形成一个chunk,输出一个bundle文件
1const {resolve} = require('path'); 2const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 4module.exports = { 5 entry: './src/index.js', 6 output: { 7 filename: 'built.js', 8 path: resolve(__dirname, 'build') 9 }, 10 plugins:[ 11 new HtmlWebpackPlugin() 12 ], 13 mode: 'development' 14}
-
Array写法
多入口, 所有入口文件最终只会生成一个chunk, 输出只有一个bundle
–>通常来说, 只有在HMR功能中, 通过多入口让html热更新生效
1const {resolve} = require('path'); 2const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 4module.exports = { 5 entry: ['./src/index.js' , './src/add.js'], 6 output: { 7 filename: '[name].js', 8 path: resolve(__dirname, 'build') 9 }, 10 plugins:[ 11 new HtmlWebpackPlugin() 12 ], 13 mode: 'development' 14}
-
object写法
多入口, 有几个入口文件就形成几个chunk,输出几个bundle文件
此时[name]为key值
特殊用法: 与数组结合
1const {resolve} = require('path'); 2const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 4module.exports = { 5 entry: { 6 index: ['./src/index.js', './src/count.js'], 7 add: './src/add.js' 8 }, 9 output: { 10 filename: '[name].js', 11 path: resolve(__dirname, 'build') 12 }, 13 plugins:[ 14 new HtmlWebpackPlugin() 15 ], 16 mode: 'development' 17}
24. output详细配置
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports = {
5 entry: './src/index.js',
6 output: {
7 // 文件名称(可以指定名称 + 目录)
8 filename: 'js/[name].js',
9 // 指定输出文件目录(将来所有资源输出的公共目录)
10 path: resolve(__dirname, 'build'),
11 // 所有资源引入的公共路径前缀 --> 补充到path的前面 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
12 publicPath: '/',
13 // 非入口chunk的名称 如import动态导入或者optimization分割的chunk
14 chunkFilename: '[name]_chunk.js',
15 // js文件整个库向外暴露出去的变量名
16 library: '[name]',
17 libraryTarget: 'window' // 变量名添加到哪个上 browser
18 },
19 plugins:[
20 new HtmlWebpackPlugin()
21 ],
22 mode: 'development'
23}
25. module详细配置
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports = {
5 entry: './src/index.js',
6 output: {
7 // 文件名称(可以指定名称 + 目录)
8 filename: 'js/[name].js',
9 // 指定输出文件目录(将来所有资源输出的公共目录)
10 path: resolve(__dirname, 'build'),
11 },
12 module: {
13 rules: [
14 {
15 test:/\.css$/,
16 // 多个loader用use
17 use: ['style-loader', 'css-loader']
18 },
19 {
20 test: /\.js$/,
21 // 排除文件
22 exclude: /node_modules/,
23 // 只检查src下的js文件
24 include: resolve(__dirname, 'src'),
25 // 优先执行
26 enforce: 'pre',
27 // 延后执行
28 // enforce: 'post',
29 // 单个loader用loader
30 loader: 'eslint-loader'
31 },
32 {
33 // 通常每个不同类型的文件在loader转换时,都会被命中,遍历module中rules中所有loader
34 // oneOf 只要匹配即退出, 根据文件类型选择对应的loader
35 // 不能有两个loader处理同一种文件
36 oneOf:[]
37 }
38 ]
39 },
40 plugins:[
41 new HtmlWebpackPlugin()
42 ],
43 mode: 'development'
44}
26. resolve解析模块详细配置
解析模块的规则设置
1const {resolve} = require('path');
2const HtmlWebpackPlugin = require('html-webpack-plugin');
3
4module.exports = {
5 entry: './src/js/index.js',
6 output: {
7 // 文件名称(可以指定名称 + 目录)
8 filename: 'js/[name].js',
9 // 指定输出文件目录(将来所有资源输出的公共目录)
10 path: resolve(__dirname, 'build'),
11 },
12 module: {
13 rules: [
14 {
15 test:/\.css$/,
16 use:['style-loader', 'css-loader']
17 }
18 ]
19 },
20 plugins:[
21 new HtmlWebpackPlugin()
22 ],
23 mode: 'development',
24
25 // 解析模块的规则
26 resolve: {
27 // 配置解析模块路径别名
28 // 例如 将src/css 替换为 $css
29 // 优点: 简写路径
30 // 缺点: 没有提示
31 alias: {
32 $css: resolve(__dirname, 'src/css')
33 },
34
35 // 配置省略文件路径的后缀名 --> './css/index'
36 // 文件名一样则匹配第一个遇到的, 命名时需要注意
37 extensions: ['.js', '.json', '.css'],
38
39 // 告诉webpack解析模块时是去找哪个目录
40 modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
41 }
42}
27. devServer详细配置
1 // 开发服务器 devServer: (用来自动化编译,自动打开浏览器,自动刷新浏览器)
2 // 特点: 只会在内存中编译打包, 不会有任何输出
3 devServer:{
4 // --- 运行代码的目录 ---
5 // Webpack4用法
6 // contentBase: resolve(__dirname, 'build'),
7 // Webpack5用法
8 static: resolve(__dirname, 'build'),
9 // 监视运行目录下的所有文件
10 watchcontentBase: true,
11 watchOptions: {
12 // 忽略监视的文件
13 ignored: /node_modules/
14 },
15 // 启动gzip压缩, 使代码体积更小,启动更快
16 compress: true,
17 // 端口号
18 port: 3000,
19 // 域名
20 host: 'localhost',
21 // 自动打开浏览器
22 open: true,
23 // 开启HMR功能 webpack5自动开启
24 hot: true,
25 // 不需要显示启动服务器日志
26 clientLogLevel: 'none',
27 // 除了一些基本启动信息以外,其他内容都不要打印
28 quiet: 'true',
29 // 如果出现错误,不要全屏提示~
30 overlay: false,
31 // 服务器代理 --> 解决开发环境下跨域问题
32 proxy: {
33 // 一旦devServer(port:5000)服务器接收到/api/xxx的请求, 就会把请求转发到另外一个服务器(port:3000)
34 '/api': {
35 target: 'http://localhost:3000',
36 // 发送请求时,请求路径重写: 将api/xxx --> /xxx(去掉/api)
37 pathRewrite: {
38 '^/api' : ''
39 }
40 }
41 }
42 }
28. optimization详细配置
优化配置选项
1 optimization: {
2 splitChunks: {
3 chunks: 'all',
4 // 下面所有的都默认就行, 通常不需要配置
5 minSize: 30 * 1024, // 分割的chunk最小为30kb
6 maxSize: 0, // 最大没有限制
7 minChunks: 1, // 要提取的chunk最少要被引用1次
8 maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
9 maxInitialRequests: 3, // 入口js文件最大并行请求数量
10 automaticNameDelimiter: '~', // 名称连接符
11 name: true, // 可以使用命名规则
12 cacheGroup: { // 分割chunk的组
13 // node_modules中的文件会被打包到vendors组的chunk中 --> vecdors~0.js
14 // 满足上面的公共规则
15 vendors: {
16 test: /[\\/]node_modules[\\/]/,
17 priority: -10
18 },
19 default: {
20 // 被提取的chunk最少要被引用2次
21 minChunks: 2,
22 priority: -20,
23 // 如果当前要打包的模块和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
24 reuseExistingChunk: true
25 }
26 }
27 }
28 // 解决: 修改a文件导致b文件的contenthash变化
29 runtimeChunk: {
30 name: entrypoint => `runtime-$(entrypoint.name)`
31 },
32 minimizer: [
33 // 配置生产环境的压缩方案: js和css
34 new TerserWebpackPluging({
35 // 开启缓存
36 cache: true,
37 // 并行打包
38 parallel: true,
39 // 启用source-map 否则会被压缩
40 sourceMap: true,
41 })
42 ]
43 }
- 原文作者:小小呆呆没有脑袋
- 原文链接:https://www.gujin.store/post/2022-3-17-webpacklearn/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。