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篇)

            

 

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