webpack 入门笔记(1)

第 1 章:webpack 简介

1.1 webpack 是什么

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)

webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。

它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)

 

 

1.2 webpack 五个核心概念

1.2.1 Entry

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

1.2.2 Output

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

1.2.3 Loader

Loader webpack 能 够去处理 那些非 JavaScript 文件 (webpack 自身只理解

JavaScript)

1.2.4 Plugins

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

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

1.2.5 Mode

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

 

 

第 2 章:webpack 的初体验

2.1 初始化配置

1. 初始化 package.json

输入指令:

npm init

2. 下载并安装 webpack

输入指令:

npm install webpack webpack-cli -g

npm install webpack webpack-cli -D

 

2.2 编译打包应用

1. 创建文件

2. 运行指令

开发环境指令:webpack src/js/index.js -o build/js/built.js --mode=development

功能:webpack 能够编译打包 js json 文件,并且能将 es6 的模块化语法转换成

浏览器能识别的语法。

生产环境指令:webpack src/js/index.js -o build/js/built.js --mode=production

功能:在开发配置功能上多一个功能,压缩代码。

3. 结论

webpack 能够编译打包 js json 文件。

能将 es6 的模块化语法转换成浏览器能识别的语法。

生产环境和开发环境将 ES6 模块化编译成浏览器能识别的模块化,但是不能处理 ES6 的基本语法转化为 ES5(需要借助 loader)

生产环境比开发环境多一个压缩 js 代码的功能

4. 问题

不能编译打包 cssimg 等文件。

不能将 js es6 基本语法转化为 es5 以下语法。

第 3 章: Webpack 的基本配置

3.1 基本结构

1. 创建文件 webpack.config.js

2. 配置内容如下

 1 module.exports = { 
 2   entry: './src/js/index.js', // 入口文件 
 3   output: { // 输出配置 
 4     filename: './built.js', // 输出文件名 
 5     path: resolve(__dirname, 'build/js') // 输出文件路径配置 
 6    },
 7   module:{ //匹配对应的文件,并将文件内容通过对应的loader转变为webpack能够识别的内容(相当于翻译官)
 8     rules:[ 
 9        //详细loader配置
10        //不同文件必须配置不同的loader处理
11       ...
12     ]
13   },
14   plugins:[ //通过插件处理相应的功能
15     ...
16   ],
17   mode: 'development' //开发环境 
18  };

 

3. 运行指令: webpack

第 4 章:Webpack 开发环境的基本配置

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

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

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

开发环境配置主要是为了能让代码运行。主要考虑以下几个方面:

  • 打包样式资源
  • 打包 html 资源
  • 打包图片资源
  • 打包其他资源
  • devServer

下面是一个简单的开发环境webpack.config.js配置文件

  1 // resolve用来拼接绝对路径的方法
  2 const { resolve } = require('path') // node 内置核心模块,用来处理路径问题。
  3 const HtmlWebpackPlugin = require('html-webpack-plugin') // 引用plugin
  4 
  5 module.exports = {
  6   // webpack配置
  7   entry: './src/js/index.js', // 入口起点
  8   output: {
  9     // 输出
 10     // 输出文件名 输出文件为js 目录下的build.js
 11     filename: 'js/built.js',
 12     // __dirname是nodejs的变量,代表当前文件的目录绝对路径
 13     path: resolve(__dirname, 'build'), // 输出路径,所有资源打包都会输出到这个文件夹下
 14   },
 15   // loader配置
 16   module: {
 17     rules: [
 18       // 详细的loader配置
 19       // 不同文件必须配置不同loader处理
 20       {
 21         // 匹配哪些文件
 22         test: /\.less$/,
 23         // 使用哪些loader进行处理
 24         use: [
 25           // use数组中loader执行顺序:从右到左,从下到上,依次执行(先执行less-loader)
 26           // style-loader:创建style标签,将js中的样式资源插入进去,添加到head中生效
 27           'style-loader',
 28           // css-loader:将css文件变成commonjs模块加载到js中,里面内容是样式字符串
 29           'css-loader',
 30           // less-loader:将less文件编译成css文件,需要下载less-loader和less
 31           'less-loader'
 32         ],
 33       },
 34       {
 35         test: /\.css$/,
 36         use: ['style-loader', 'css-loader'],
 37       },
 38       {
 39         // url-loader:处理图片资源,问题:默认处理不了html中的img图片
 40         test: /\.(jpg|png|gif)$/,
 41         // 需要下载 url-loader file-loader
 42         loader: 'url-loader',
 43         options: {
 44           // 图片大小小于8kb,就会被base64处理,优点:减少请求数量(减轻服务器压力),
 45           // 缺点:图片体积会更大(文件请求速度更慢)
 46           // base64在客户端本地解码所以会减少服务器压力,如果图片过大还采用base64编码会导致cpu调用率上升,网页加载时变卡
 47           limit: 8 * 1024,
 48           // 给图片重命名,[hash:10]:取图片的hash的前10位,[ext]:取文件原来扩展名
 49           name: '[hash:10].[ext]',
 50           // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是conmonjs,解析时会出问题:[object Module]
 51           // 解决:关闭url-loader的es6模块化,使用commonjs解析
 52           esModule: false,
 53           outputPath: 'imgs',  //将打包后的图片资源放到imgs文件中(为了让打包文件目录与src目录一致)
 54         },
 55       },
 56       {
 57         test: /\.html$/,
 58         // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
 59         loader: 'html-loader',
 60       },
 61       // 打包其他资源(除了html/js/css资源以外的资源)
 62       {
 63         // 排除html|js|css|less|jpg|png|gif文件
 64         exclude: /\.(html|js|css|less|jpg|png|gif)/,
 65         // file-loader:处理其他文件
 66         loader: 'file-loader',
 67         options: {
 68           name: '[hash:10].[ext]',
 69           outputPath: 'media', //将打包后的图片资源放到media文件中
 70         },
 71       },
 72     ],
 73   },
 74   // plugin的配置
 75   plugins: [
 76     // 下载html-webpack-plugin
 77     // html-webpack-plugin:默认会创建一个空的html文件,自动引入打包输出的所有资源(JS/CSS)
 78     // 需要有结构的HTML文件可以加一个template
 79     new HtmlWebpackPlugin({
 80       // 复制这个./src/index.html文件,并自动引入打包输出的所有资源(JS/CSS)
 81       template: './src/index.html',
 82     }),
 83   ],
 84   // 模式
 85   mode: 'development', // 开发模式 生产模式值为: 'production'
 86   // 开发服务器 devServer:用来自动化,不用每次修改后都重新输入webpack打包一遍(自动编译,自动打开浏览器,自动刷新浏览器)
 87   // 特点:只会在内存中编译打包,不会有任何输出(不会像之前那样在外面看到打包输出的build包,而是在内存中,关闭后会自动删除)
 88   // 安装: npm i webpack-dev-server -D
 89   // 启动devServer指令为:npx webpack-dev-server
 90   devServer: {
 91     // 项目构建后路径
 92     contentBase: resolve(__dirname, 'build'),
 93     // 启动gzip压缩
 94     compress: true,
 95     // 端口号
 96     port: 3000,
 97     // 自动打开浏览器
 98     open: true,
 99     //是否项目编译后自动打开浏览器
100     autoOpenBrowser: false, //true为自动打开 false不自动打开
101   },
102 }

 

第 5 章:Webpack 生产环境的基本配置

而生产环境的配置需要考虑以下几个方面:

  • 提取 css 成单独文件
  • css 兼容性处理
  • 压缩 css
  • js 语法检查
  • js 兼容性处理
  • js 压缩
  • html 压缩

下面是一个基本的生产环境下的webpack.config.js配置

5.1 提取css成单独文件

1.下载插件:npm install --save-dev mini-css-extract-plugin

2.修改配置文件

 1 const { resolve } = require('path'); 
 2 const HtmlWebpackPlugin = require('html-webpack-plugin'); 
 3 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
 4 module.exports = { 
 5   entry: './src/js/index.js', 
 6   output: { 
 7     filename: 'js/built.js', 
 8     path: resolve(__dirname, 'build') },
 9     module: { 
10       rules: [ 
11         { 
12           test: /\.css$/, 
13           use: [ 
14             // 创建 style 标签,将样式放入head中 
15             //'style-loader', 
16             // 这个 loader 取代 style-loader。作用:提取 js 中的 css 成单独文件 
17             MiniCssExtractPlugin.loader, // 将 css 文件整合到 js 文件中 
18             'css-loader' 
19           ] 
20         } 
21       ] 
22     },
23   plugins: [ 
24     new HtmlWebpackPlugin({ template: './src/index.html' }), 
25     new MiniCssExtractPlugin({ 
26          // 对输出的 css 文件进行重命名 filename: 'css/built.css' })
27       ],
28   mode: 'development' 
29  };

 

5.2 css 兼容性处理

1.下载 loader

npm install --save-dev postcss-loader postcss-preset-env

2.修改配置文件webpack.config.js

 1 const { resolve } = require('path'); 
 2 const HtmlWebpackPlugin = require('html-webpack-plugin'); 
 3 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
 4 
 5 // 设置 nodejs 环境变量 
 6 // process.env.NODE_ENV = 'development'; 
 7 module.exports = { 
 8   entry: './src/js/index.js', 
 9   output: { 
10     filename: 'js/built.js', 
11     path: resolve(__dirname, 'build') 
12   },
13   module: { 
14     rules: [ 
15       { 
16         test: /\.css$/, 
17         use: [ 
18           MiniCssExtractPlugin.loader, 
19           'css-loader', 
20                     /*
21                css兼容性处理: postcss --> postcss-loader  postcss-preset-env  
22                注意:安装postcss-loader 3.0.0 版本,否则会报错
23 
24                帮postcss找到package.json 中browserlist里面的配置 通过配置加载指定的css兼容性样式
25                        
26                  "browserslist": {
27                  //开发环境 ---》设置环境变量:process.env.NODE_ENV = 'development'
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               "not dead",
37               "not op_mini all"
38              ]
39             }
40            */
41          //使用loader的默认配置
42          // postcss-loader
43          //修改loader的配置
44           { 
45             loader: 'postcss-loader', 
46             options: { 
47               ident: 'postcss',  //固定写法
48                       plugins: () => [ // postcss 的插件 
49                         require('postcss-preset-env')() 
50                       ] 
51             } 
52           } 
53         ] 
54       } 
55     ] 
56   },
57   plugins: [ 
58     new HtmlWebpackPlugin({ 
59       template: './src/index.html' 
60     }), 
61     new MiniCssExtractPlugin({ 
62       filename: 'css/built.css' 
63     }) 
64   ],
65   mode: 'development' 
66 };

 

3.修改 package.json

 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     "not dead", 
10     "not op_mini all" 
11   ] 
12 }

 

5.3 压缩 css

1. 下载安装包

npm install --save-dev optimize-css-assets-webpack-plugin

2.修改配置文件

 1 const { resolve } = require('path'); 
 2 const HtmlWebpackPlugin = require('html-webpack-plugin'); 
 3 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
 4 const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin' )
 5 
 6 module.exports = { 
 7   entry: './src/js/index.js', 
 8   output: { 
 9     filename: 'js/built.js', 
10     path: resolve(__dirname, 'build') 
11   },
12   module: { 
13     rules: [ 
14       { 
15         test: /\.css$/, 
16         use: [ 
17           MiniCssExtractPlugin.loader, 
18           'css-loader', 
19           { 
20             loader: 'postcss-loader', 
21             options: { 
22               ident: 'postcss',  //固定写法
23                       plugins: () => [ // postcss 的插件 
24                         require('postcss-preset-env')() 
25                       ] 
26             } 
27           } 
28         ] 
29       } 
30     ] 
31   },
32   plugins: [ 
33     new HtmlWebpackPlugin({ 
34       template: './src/index.html' 
35     }), 
36     new MiniCssExtractPlugin({ 
37       filename: 'css/built.css' 
38     }),
39     // 压缩 css 
40     new OptimizeCssAssetsWebpackPlugin()
41   ],
42   mode: 'development' 
43 };

 

5.4 js 语法检查

1.下载安装包

npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

2.修改配置文件

 1 const { resolve } = require('path'); 
 2 const HtmlWebpackPlugin = require('html-webpack-plugin'); 
 3 const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 
 4 const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin' )
 5 
 6 // 设置 nodejs 环境变量 
 7 // process.env.NODE_ENV = 'development'; 
 8 
 9 module.exports = { 
10   entry: './src/js/index.js', 
11   output: { 
12     filename: 'js/built.js', 
13     path: resolve(__dirname, 'build') },
14   module: { 
15     rules: [ 
16       /*
17       语法检查: eslint-loader eslint 
18       注意:只检查自己写的源代码,第三方的库是不用检查的 
19       设置检查规则: package.json 中 eslintConfig 中设置~
20       "eslintConfig": { 
21       "extends": "airbnb-base" 
22        } 
23        对于console.log的检测,eslint会爆出警告
24        可以在console.log前面一行以单行注释的方式忽略  console.log的检测
25        // eslint-disable-next-line
26       airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint 
27       */
28       { 
29         test: /\.js$/, 
30         exclude: /node_modules/, 
31         loader: 'eslint-loader', 
32         options: {
33           // 自动修复 eslint 的错误 
34           fix: true 
35         } 
36       } 
37     ] 
38   },
39   plugins: [ 
40     new HtmlWebpackPlugin({ 
41       template: './src/index.html' 
42     }), 
43     new MiniCssExtractPlugin({ filename: 'css/built.css' }), 
44     // 压缩 css 
45     new OptimizeCssAssetsWebpackPlugin() 
46   ],
47   mode: 'development' 
48 };

 

3.配置package.json

1 "eslintConfig": { 
2   "extends": "airbnb-base", 
3     "env": { 
4       "browser": true 
5     } 
6 }

 

5.5 js 兼容性处理

1.下载安装包

npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill

2.修改配置文件

 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        /* 
13             js兼容性处理: babel-loader @babel/preset-env @babel/core
14              下载 babel-loader @babel/preset-env 
15              1. 基本js兼容性处理 --》  @babel/preset-env
16                问题: 只能转换基本语法 如promise高级语法不能转换
17             2. 全部js兼容性处理 --》 @babel/polyfill
18               问题: 我只要解决部分兼容性问题 但是将所有兼容性代码全部引入 体积太大了
19             3.需要做兼容性处理的就做 按需加载 --》 core-js  下载core-js
20 
21             1 3结合 完成所有的兼容性处理方案 2因为处理完包的体积(built.js)太大了,所以不考虑
22      */
23       { 
24         test: /\.js$/, 
25         exclude: /node_modules/, 
26         loader: 'babel-loader', 
27         options: { // 预设:指示 babel 做怎么样的兼容性处理 
28           presets: [ 
29             [ 
30               '@babel/preset-env', 
31               {
32                 // 按需加载 
33                 useBuiltIns: 'usage', // 指定 core-js 版本 
34                 corejs: { 
35                   version: 3 
36                 },
37                 // 指定兼容性做到哪个版本浏览器 
38                 targets: { 
39                   chrome: '60', 
40                   firefox: '60', 
41                   ie: '9', 
42                   safari: '10', 
43                   edge: '17' 
44                 } 
45               } 
46             ] 
47           ] 
48         } 
49       } 
50     ] 
51   },
52   plugins: [ 
53     new HtmlWebpackPlugin({ 
54       template: './src/index.html' 
55     }) 
56   ],
57   mode: 'development'
58 }

 

5.6 js 压缩

生产环境下会自动压缩 js 代码

5.7 HTML 压缩

修改配置文件

 1 const { resolve } = require('path'); 
 2 const HtmlWebpackPlugin = require('html-webpack-plugin'); 
 3 module.exports = { 
 4   entry: './src/js/index.js', 
 5   output: { 
 6     filename: 'js/built.js', 
 7     path: resolve(__dirname, 'build') 
 8   },
 9   plugins: [ 
10     new HtmlWebpackPlugin({ 
11       template: './src/index.html', 
12       // 压缩 html 代码 
13       minify: { 
14         // 移除空格 
15         collapseWhitespace: true, 
16         // 移除注释 
17         removeComments: true 
18       } 
19     }) 
20   ],
21   mode: 'production' 
22 };

 

5.8生产环境基本配置

配置文件参考代码:

  1 const { resolve } = require('path')
  2 const MiniCssExtractorPlugin = require('mini-css-extract-plugin')
  3 const OptimiziCssAssetsWebpackPlugin = require('optimizi-css-assets-webpack-plugin')
  4 const HtmlWebpackPlugin = require('html-webpack-plugin')
  5 
  6 // 定义node.js的环境变量,决定使用browserslist的哪个环境
  7 process.env.NODE_ENV = 'production'
  8 
  9 // 复用loader的写法
 10 const commonCssLoader = [
 11   // 这个loader取代style-loader。作用:提取js中的css成单独文件然后通过link加载
 12   MiniCssExtractPlugin.loader,
 13   // css-loader:将css文件整合到js文件中
 14   // 经过css-loader处理后,样式文件是在js文件中的
 15   // 问题:1.js文件体积会很大2.需要先加载js再动态创建style标签,样式渲染速度就慢,会出现闪屏现象
 16   // 解决:用MiniCssExtractPlugin.loader替代style-loader
 17   'css-loader',
 18   /*
 19     postcss-loader:css兼容性处理:postcss --> 需要安装:postcss-loader postcss-preset-env
 20     postcss需要通过package.json中browserslist里面的配置加载指定的css兼容性样式
 21     在package.json中定义browserslist:
 22     "browserslist": {
 23       // 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
 24       "development": [ // 只需要可以运行即可
 25         "last 1 chrome version",
 26         "last 1 firefox version",
 27         "last 1 safari version"
 28       ],
 29       // 生产环境。默认是生产环境
 30       "production": [ // 需要满足绝大多数浏览器的兼容
 31         ">0.2%",
 32         "not dead",
 33         "not op_mini all"
 34       ]
 35     },
 36   */
 37   {
 38     loader: 'postcss-loader',
 39     options: {
 40       ident: 'postcss', // 基本写法
 41       plugins: () => [
 42         // postcss的插件
 43         require('postcss-preset-env')(),
 44       ],
 45     },
 46   },
 47 ]
 48 
 49 module.exports = {
 50   entry: './src/js/index.js',
 51   output: {
 52     filename: 'js/built.js',
 53     path: resolve(__dirname, 'build'),
 54   },
 55   module: {
 56     rules: [
 57       {
 58         test: /\.css$/,
 59         use: [...commonCssLoader],
 60       },
 61       {
 62         test: /\.less$/,
 63         use: [...commonCssLoader, 'less-loader'],
 64       },
 65       /*
 66         正常来讲,一个文件只能被一个loader处理
 67         当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
 68         先执行eslint再执行babel(用enforce)
 69       */
 70       {
 71         /*
 72           js的语法检查: 需要下载 eslint-loader eslint
 73           注意:只检查自己写的源代码,第三方的库是不用检查的
 74           airbnb(一个流行的js风格) --> 需要下载 eslint-config-airbnb-base eslint-plugin-import
 75           设置检查规则:
 76             package.json中eslintConfig中设置
 77               "eslintConfig": {
 78                 "extends": "airbnb-base", // 继承airbnb的风格规范
 79                 "env": {
 80                   "browser": true // 可以使用浏览器中的全局变量(使用window不会报错)
 81                 }
 82               }
 83         */
 84         test: /\.js$/,
 85         exclude: /node_modules/, // 忽略node_modules
 86         enforce: 'pre', // 优先执行
 87         loader: 'eslint-loader',
 88         options: {
 89           // 自动修复
 90           fix: true,
 91         },
 92       },
 93       /*
 94         js兼容性处理:需要下载 babel-loader @babel/core
 95           1. 基本js兼容性处理 --> @babel/preset-env
 96             问题:只能转换基本语法,如promise高级语法不能转换
 97           2. 全部js兼容性处理 --> @babel/polyfill
 98             问题:只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了
 99           3. 需要做兼容性处理的就做:按需加载  --> core-js
100       */
101       {
102         // 第三种方式:按需加载
103         test: /\.js$/,
104         exclude: /node_modules/,
105         loader: 'babel-loader',
106         options: {
107           // 预设:指示babel做怎样的兼容性处理
108           presets: [
109             '@babel/preset-env', // 基本预设
110             {
111               useBuiltIns: 'usage', //按需加载
112               corejs: { version: 3 }, // 指定core-js版本
113               targets: { // 指定兼容到什么版本的浏览器
114                 chrome: '60',
115                 firefox: '50',
116                 ie: '9',
117 safari: '10',
118 edge: '17'
119               },
120             },
121           ],
122         },
123       },
124       {
125 // 图片处理
126 test: /\.(jpg|png|gif)/,
127 loader: 'url-loader',
128 options: {
129 limit: 8 * 1024,
130 name: '[hash:10].[ext]',
131 outputPath: 'imgs',
132 esModule: false, // 关闭url-loader默认使用的es6模块化解析
133         },
134       },
135 // html中的图片处理
136       {
137 test: /\.html$/,
138 loader: 'html-loader',
139       },
140 // 处理其他文件
141       {
142 exclude: /\.(js|css|less|html|jpg|png|gif)/,
143 loader: 'file-loader',
144 options: {
145 outputPath: 'media',
146         },
147       },
148     ],
149   },
150 plugins: [
151 new MiniCssExtractPlugin({
152 // 对输出的css文件进行重命名
153 filename: 'css/built.css',
154     }),
155 // 压缩css
156 new OptimiziCssAssetsWebpackPlugin(),
157 // HtmlWebpackPlugin:html文件的打包和压缩处理
158 // 通过这个插件会自动将单独打包的样式文件通过link标签引入
159 new HtmlWebpackPlugin({
160 template: './src/index.html',
161 // 压缩html代码
162 minify: {
163 // 移除空格
164 collapseWhitespace: true,
165 // 移除注释
166 removeComments: true,
167       },
168     }),
169   ],
170 // 生产环境下会自动压缩js代码
171 mode: 'production',
172 }

 

第 6 章:webpack 优化配置

6.1 开发环境性能优化

6.1.1 HMR(模块热替换)

HMR: hot module replacement 热模块替换 / 模块热替换

作用:一个模块发生变化,只会重新打包构建这一个模块(而不是打包所有模块) ,极大提升构建速度

代码:只需要在 devServer 中设置 hot 为 true,就会自动开启HMR功能(只能在开发模式下使用)

 1 /* 
 2   HRM: hot module replacement 热模块替换/ 模块热替换
 3   作用: 一个模块发生变化,只会重新打包这一个模块(而不是打包所有)
 4       极大地提升构建速度
 5 
 6       样式文件: 可以使用HMR功能 因为style-loader内部实现了
 7       js文件: 默认不能使用HMR 功能--->需要修改js代码 添加支持HRM功能的代码
 8       注意:HRM功能对js的处理 只能处理非入口js文件的其他文件
 9       CSS文件: 默认不能使用HMR 功能 同时会导致问题:html文件不能热更新了
10       解决: 修改entry入口 将html文件引入
11 */
12 devServer: {
13   contentBase: resolve(__dirname, 'build'),
14   compress: true,
15   port: 3000,
16   open: true,
17   // 开启HMR功能
18   // 当修改了webpack配置,新配置要想生效,必须重启webpack服务
19   hot: true
20 }

 

每种文件实现热模块替换的情况:

  • 样式文件:可以使用HMR功能,因为开发环境下使用的 style-loader 内部默认实现了热模块替换功能
  • js 文件:默认不能使用HMR功能(修改一个 js 模块所有 js 模块都会刷新)
    --> 实现 HMR 需要修改 js 代码(添加支持 HMR 功能的代码)

在入口文件中index.js中

1 // 绑定
2 if (module.hot) {
3   // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
4   module.hot.accept('./print.js', function() {
5     // 方法会监听 print.js 文件的变化,一旦发生变化,只有这个模块会重新打包构建,其他模块不会。
6     // 会执行后面的回调函数
7     print();
8   });
9 }

 

 

   注意:HMR 功能对 js 的处理,只能处理非入口 js 文件的其他文件。

  • html 文件: 默认不能使用 HMR 功能(html 不用做 HMR 功能,因为只有一个 html 文件,不需要再优化)
    使用 HMR 会导致问题:html 文件不能热更新了(不会自动打包构建)
    解决:修改 entry 入口,将 html 文件引入(这样 html 修改整体刷新)

 

entry: ['./src/js/index.js', './src/index.html']

  

 

6.1.2 source-map

source-map:一种提供源代码到构建后代码的映射的技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)

参数:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

代码:

devtool: 'eval-source-map'

  

 

可选方案:[生成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 >... )
    1. eval-cheap-souce-map
    2. eval-source-map
  • 调试更友好
    1. souce-map
    2. cheap-module-souce-map
    3. cheap-souce-map

最终得出最好的两种方案 --> eval-source-map(完整度高,内联速度快) / eval-cheap-module-souce-map(错误提示忽略列但是包含其他信息,内联速度快)

生产环境:需要考虑源代码要不要隐藏,调试要不要更友好

  • 内联会让代码体积变大,所以在生产环境不用内联
  • 隐藏源代码
    1. nosources-source-map 全部隐藏
    2. hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

最终得出最好的两种方案 --> source-map(最完整) / cheap-module-souce-map(错误提示一整行忽略列)

 

6.2 生产环境性能优化

6.2.1 优化打包构建速度

6.2.1.1 oneOf

oneOf:匹配到 loader 后就不再向后进行匹配,优化生产环境的打包构建速度

代码:

 1 module: {
 2   rules: [
 3     {
 4       // js 语法检查
 5       test: /\.js$/,
 6       exclude: /node_modules/,
 7       // 优先执行
 8       enforce: 'pre',
 9       loader: 'eslint-loader',
10       options: {
11         fix: true
12       }
13     },
14     {
15       // oneOf 优化生产环境的打包构建速度
16       // 以下loader只会匹配一个(匹配到了后就不会再往下匹配了)
17       // 注意:不能有两个配置处理同一种类型文件(所以把eslint-loader提取出去放外面)
18       oneOf: [
19         {
20           test: /\.css$/,
21           use: [...commonCssLoader]
22         },
23         {
24           test: /\.less$/,
25           use: [...commonCssLoader, 'less-loader']
26         },
27         {
28           // js 兼容性处理
29           test: /\.js$/,
30           exclude: /node_modules/,
31           loader: 'babel-loader',
32           options: {
33             presets: [
34               [
35                 '@babel/preset-env',
36                 {
37                   useBuiltIns: 'usage',
38                   corejs: {
39                     version: 3
40                   },
41                   targets: {
42                     chrome: '60',
43                     firefox: '50'
44                   }
45                 }
46               ]
47             ]
48           }
49         },
50         {
51           test: /\.(jpg|png|gif)/,
52           loader: 'url-loader',
53           options: {
54             limit: 8 * 1024,
55             name: '[hash:10].[ext]',
56             outputPath: 'imgs',
57             esModule: false
58           }
59         },
60         {
61           test: /\.html$/,
62           loader: 'html-loader'
63         },
64         {
65           exclude: /\.(js|css|less|html|jpg|png|gif)/,
66           loader: 'file-loader',
67           options: {
68             outputPath: 'media'
69           }
70         }
71       ]
72     }
73   ]
74 }

 

6.2.1.2 babel 缓存

babel 缓存:类似 HMR,将 babel 处理后的资源缓存起来(哪里的 js 改变就更新哪里,其他 js 还是用之前缓存的资源),让第二次打包构建速度更快

代码:

 1 {
 2   test: /\.js$/,
 3   exclude: /node_modules/,
 4   loader: 'babel-loader',
 5   options: {
 6     presets: [
 7       [
 8         '@babel/preset-env',
 9         {
10           useBuiltIns: 'usage',
11           corejs: { version: 3 },
12           targets: {
13             chrome: '60',
14             firefox: '50'
15           }
16         }
17       ]
18     ],
19     // 开启babel缓存
20     // 第二次构建时,会读取之前的缓存
21     cacheDirectory: true
22   }
23 },

 

 

文件资源缓存

开启bable缓存:

设置babel-loader的cacheDirectory为true

文件名不变,就不会重新请求,而是再次用之前缓存的资源

1.hash: 每次 wepack 打包时会生成一个唯一的 hash 值。

问题:重新打包,所有文件的 hsah 值都改变,会导致所有缓存失效。(可能只改动了一个文件)

2.chunkhash:根据 chunk 生成的 hash 值。来源于同一个 chunk的 hash 值一样

问题:js 和 css 来自同一个chunk,hash 值是一样的(因为 css-loader 会将 css 文件加载到 js 中,所以同属于一个chunk)

3.contenthash: 根据文件的内容生成 hash 值。不同文件 hash 值一定不一样(文件内容修改,文件名里的 hash 才会改变)

修改 css 文件内容,打包后的 css 文件名 hash 值就改变,而 js 文件没有改变 hash 值就不变,这样 css 和 js 缓存就会分开判断要不要重新请求资源 --> 让代码上线运行缓存更好使用

 1 const {resolve } = require("path")
 2 const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 
 3 
 4 /* 
 5   缓存:
 6   babel缓存
 7      cacheDirectory: true
 8 ----》 让第二次打包构建速度更快
 9   文件资源缓存
10   hash:每次webpack构建时会生成一个唯一的hash值。
11    问题:因为js和css同时使用一个hash值
12     如果重新打包 会导致所有缓存失效(可能我只改动了一个文件)
13   chunkhash: 根据chunk生成的hash值 如果打包来源同一个chunk 那么hash值就一样
14    问题: js和css的hash值还是一样的
15      因为css是在js中被引入的 所以同属于一个chunk
16   contenthash: 根据文件的内容生成hash值 不同文件hash值一定不一样
17   ----》 让代码上线运行缓存更好
18 */
19 
20 //定义nodejs环境变量 决定使用browserslist的那个环境
21 process.env.NODE_ENV = 'production'
22 
23 module.exports = {
24     entry: './src/js/index.js',
25     output: {
26         filename: 'js/built.[contenthash:10].js', //文件缓存处理
27         path:resolve(__dirname,'build')
28     },
29     module:{
30        ......
31          {
32                     test: /\.js$/,
33                     exclude: /node_modules/,
34                     loader: 'babel-loader',
35                     options: {
36                         presets: [
37                             [
38                                 '@babel/preset-env',
39                                 {
40                                     useBuiltIns: 'usage',
41                                     corejs:{version: 3},
42                                     targets: {
43                                     chrome: '60',
44                                     firefox: '50'
45                                     }
46                                 }
47                             ]
48                         ],
49                         //开启bable缓存
50                         //第二次构建是 会读取之前的缓存
51                         cacheDirectory: true  //设置
52                     },
53                 },
54       ......
55     },
56     plugins:[
57         new MiniCssExtractPlugin({
58             filename: 'css/built.[contenthash:10].css' //文件缓存处理
59         }),
60         ......
61     ],
62     mode:'production',
63 }

 

6.2.1.3 多进程打包

多进程打包:某个任务消耗时间较长会卡顿,多进程可以同一时间干多件事,效率更高。

优点是提升打包速度,缺点是每个进程的开启和交流都会有开销(babel-loader消耗时间最久,所以使用thread-loader针对其进行优化)

 1 {
 2   test: /\.js$/,
 3   exclude: /node_modules/,
 4   use: [
 5     /* 
 6       thread-loader会对其后面的loader(这里是babel-loader)开启多进程打包。 
 7       进程启动大概为600ms,进程通信也有开销。(启动的开销比较昂贵,不要滥用)
 8       只有工作消耗时间比较长,才需要多进程打包
 9     */
10     {
11       loader: 'thread-loader',
12       options: {
13         workers: 2 // 进程2个
14       }
15     },
16     {
17       loader: 'babel-loader',
18       options: {
19         presets: [
20           [
21             '@babel/preset-env',
22             {
23               useBuiltIns: 'usage',
24               corejs: { version: 3 },
25               targets: {
26                 chrome: '60',
27                 firefox: '50'
28               }
29             }
30           ]
31         ],
32         // 开启babel缓存
33         // 第二次构建时,会读取之前的缓存
34         cacheDirectory: true
35       }
36     }
37   ]
38 },

 

 

6.2.1.4 externals

externals:让某些库不打包,通过 cdn 引入

webpack.config.js 中配置:

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

 

需要在 index.html 中通过 cdn 引入:

<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>

 

6.2.1.5 dll

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

1.下载插件

npm i add-asset-html-webpack-plugin -D

2.webpack.dll.js 配置:(将 jquery 单独打包) ----webpack.dll.js文件也可以是其他js文件名,只是运行的时候徐亚运行对应的配置文件(webpack --config xxx.js)

 1 /*
 2   node_modules的库会打包到一起,但是很多库的时候打包输出的js文件就太大了
 3   使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
 4   当运行webpack时,默认查找webpack.config.js配置文件
 5   需求:需要运行webpack.dll.js文件
 6     --> webpack --config webpack.dll.js(运行这个指令表示以这个配置文件打包)
 7 */
 8 const { resolve } = require('path');
 9 const webpack = require('webpack');
10 
11 module.exports = {
12   entry: {
13     // 最终打包生成的[name] --> jquery
14     // ['jquery] --> 要打包的库是jquery
15     jquery: ['jquery']
16   },
17   output: {
18     // 输出出口指定
19     filename: '[name].js', // name就是jquery
20     path: resolve(__dirname, 'dll'), // 打包到dll目录下
21     library: '[name]_[hash]', // 打包的库里面向外暴露出去的内容叫什么名字
22   },
23   plugins: [
24     // 打包生成一个manifest.json --> 提供jquery的映射关系(告诉webpack:jquery之后不需要再打包和暴露内容的名称)
25     new webpack.DllPlugin({
26       name: '[name]_[hash]', // 映射库的暴露的内容名称
27       path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
28     })
29   ],
30   mode: 'production'
31 };

 

 

3.webpack.config.js 配置:

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

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

 

6.2.2 优化代码运行的性能

6.2.2.1 缓存

6.2.2.2 tree shaking(树摇)

tree shaking:去除无用代码

前提:1. 必须使用 ES6 模块化 2. 开启 production 环境 (这样就自动会把无用代码去掉)

作用:减少代码体积

在 package.json 中配置:

"sideEffects": false 表示所有代码都没有副作用(都可以进行 tree shaking)

这样会导致的问题:可能会把 css / @babel/polyfill 文件干掉(副作用)

所以可以配置:"sideEffects": ["*.css", "*.less"] 不会对css/less文件tree shaking处理

6.2.2.3 code split(代码分割)

代码分割。将打包输出的一个大的 built.js 文件拆分成多个小文件,这样可以并行加载多个文件,比加载一个文件更快。

1.多入口拆分

 1 entry: {
 2     // 多入口:有一个入口,最终输出就有一个built
 3     index: './src/js/index.js',
 4     test: './src/js/test.js'
 5   },
 6   output: {
 7     // [name]:取文件名 (和entry中的属性对应)
 8     filename: 'js/[name].[contenthash:10].js',
 9     path: resolve(__dirname, 'build')
10   },

 

2.optimization:

1 optimization: {
2     splitChunks: {
3       chunks: 'all'
4     }
5   },

 

optimization的功能:

  • 将 node_modules 中的代码单独打包(大小超过30kb)
  • 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk(比如两个模块中都引入了jquery会被打包成单独的文件)(大小超过30kb)

注意:单入口只能做第一件事,多入口能做以上两件事

3.import 动态导入语法:

 1 /*
 2   通过js代码,让某个文件被单独打包成一个chunk
 3   import动态导入语法:能将某个文件单独打包(test文件不会和index打包在同一个文件而是单独打包)
 4   webpackChunkName:指定test单独打包后文件的名字
 5 */
 6 import(/* webpackChunkName: 'test' */'./test')
 7   .then(({ mul, count }) => {
 8     // 文件加载成功~
 9     // eslint-disable-next-line
10     console.log(mul(2, 5));
11   })
12   .catch(() => {
13     // eslint-disable-next-line
14     console.log('文件加载失败~');
15   });

 

 

6.2.2.4 lazy loading(懒加载/预加载)

1.懒加载:当文件需要使用时才加载(需要代码分割)。但是如果资源较大,加载时间就会较长,有延迟。--图片懒加载

2.正常加载:可以认为是并行加载(同一时间加载多个文件)没有先后顺序,先加载了不需要的资源就会浪费时间。

3.预加载 prefetch(兼容性很差):会在使用之前,提前加载。等其他资源加载完毕,浏览器空闲了,再偷偷加载这个资源。这样在使用时已经加载好了,速度很快。所以在懒加载的基础上加上预加载会更好。

代码:

 1 document.getElementById('btn').onclick = function() {
 2   // 将import的内容放在异步回调函数中使用,点击按钮,test.js才会被加载(不会重复加载)
 3   // webpackPrefetch: true表示开启预加载
 4   import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
 5     console.log(mul(4, 5));
 6   });
 7   import('./test').then(({ mul }) => {
 8     console.log(mul(2, 5))
 9   })
10 };

 

6.2.2.5 pwa(离线可访问技术)

pwa:离线可访问技术(渐进式网络开发应用程序),使用 serviceworker 和 workbox 技术。优点是离线也能访问,缺点是兼容性差。

webpack.config.js 中配置:

 1 const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); // 引入插件
 2 // plugins中加入:
 3 new WorkboxWebpackPlugin.GenerateSW({
 4   /*
 5     1. 帮助serviceworker快速启动
 6     2. 删除旧的 serviceworker
 7     生成一个 serviceworker 配置文件
 8   */
 9   clientsClaim: true,
10   skipWaiting: true
11 })

 

index.js 中还需要写一段代码来激活它的使用:

 1 /*
 2   1. eslint不认识 window、navigator全局变量
 3     解决:需要修改package.json中eslintConfig配置
 4     "env": {
 5       "browser": true // 支持浏览器端全局变量
 6     }
 7   2. sw代码必须运行在服务器上
 8     --> nodejs
 9     或-->
10       npm i serve -g
11       serve -s build 启动服务器,将打包输出的build目录下所有资源作为静态资源暴露出去
12 */
13 if ('serviceWorker' in navigator) { // 处理兼容性问题
14   window.addEventListener('load', () => {
15     navigator.serviceWorker
16       .register('/service-worker.js') // 注册serviceWorker
17       .then(() => {
18         console.log('sw注册成功了~');
19       })
20       .catch(() => {
21         console.log('sw注册失败了~');
22       });
23   });
24 }

 

 

 

 

 

 

webpack官网:https://www.webpackjs.com/

其他优秀的webpack推荐:Webpack4不求人系列(共5篇)

            

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章