webpack4.X 實戰(四):企業SPA 24點總結(下)

13. 配置 打包輸出路徑

  • webpack 配置文件輸出路徑時,經常使用三種配置webpack

    • 配置一:出口 entry.path,配置全部文件的輸出路徑git

      // webpack 配置
      const path = require('path');
      const rootPath = path.resolve(__dirname, '');
      
      
      module.exports = {
          output: {
              path: path.resolve(rootPath, './dist')
          }
      }
      
      // 打包後的文件,全部資源 統一輸出到 項目根目錄下的 dist 文件夾下
      複製代碼
    • 配置二:loaderoptions.outputPath,配置 當前 loader 匹配到文件 的輸出路徑github

      這項配置的文件輸出 以 entry.path 路徑爲標準web

      // webpack 配置
      
      module.exports = {
          output: {
              path: path.resolve(rootPath, './dist')
          },
                 
          module: {
              rules: [{
                  test: /\.(png|jpg|gif|ico)$/,
                  use: [{
                      loader: 'url-loader',
                      options: {
                          limit: 8192,
                          name: '[name].[ext]',
                          outputPath: 'static/assets/'
                      }
                  }]
              }]
          }
      };
      
      // 打包後的文件,圖片資源 統一輸出到 項目根目錄下的 `dist/static/assets` 文件夾下
      複製代碼
    • 配置三:name 選項的值,可改變當前包的輸出路徑vue-router

      出口 entry.chunkFilename,改變全部 chunk 包的輸出位置(runtimeChunk 除外)

      // webpack 配置
             
      module.exports = {
          output: {
              path: path.resolve(rootPath, './dist'),
              chunkFilename: 'static/js/[name].js'
          }
      };
         
      // 打包後的 chunk 包,統一輸出到 dist/static/js 文件夾下
      複製代碼

      optimization.splitChunksname 值,以 output.chunkFilename 路徑爲標準

      // webpack 配置
      
      
      module.exports = {
          output: {
              path: path.resolve(rootPath, './dist'),
              chunkFilename: 'static/js/[name].js'
          },
                 
          optimization: {
              splitChunks: {
                  cacheGroups: {
                      libs: {
                          test: /[\\/]node_modules[\\/]/,
                          priority: 20,
                          name: '../libs/index',
                          chunks: 'all'
                      }
                  }
              }
          }
      };
      
      // 打包後的 splitChunks 包,統一輸出到 dist/static/js 文件夾下
      複製代碼

      【特殊】optimization.runtimeChunkname 值,以 output.path 路徑爲標準

      // webpack 配置
                     
      module.exports = {
          output: {
              path: path.resolve(rootPath, './dist'),
              chunkFilename: 'static/js/[name].js'
          },
         
          optimization: {
              runtimeChunk: {
                  name: 'static/runtime/index'
              }
          }
      };
         
      // 打包後的 runtime 包,統一輸出到 dist/static/runtime 文件夾下
      複製代碼

14. 配置 靜態資源發佈路徑(方便CDN請求資源)

  • 做用:配置 發佈路徑,讓頁面請求 CDN 上的靜態資源,速度更快

  • 配置 公共發佈路徑: output.publicPath

    // webpack 配置
    
    module.exports = {
        output : {
            publicPath : 'http://aaa.com/'
        }
    };
    複製代碼
  • 單獨配置靜態資源: loader options

    module.exports = {
        module: {
            rules: [{
                test: /\.(png|jpg|gif|ico)$/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 8192,
                        name: '[name].[hash:8].[ext]',
                        outputPath: 'static/assets/',
                        publicPath: 'http://aaa.com/assets/'
                    }
                }]
            }]
        }
    };
    複製代碼

15. 配置 打包文件緩存 hash

  • webpack 打包後的模塊 默認命名規則:

    • webpack 默認爲給各個模塊分配一個 id,做爲模塊的名稱

      默認 id 是根據模塊引入的順序,賦予一個整數(0、一、二、3……)

      默認 id 用來處理模塊之間的依賴關係

    • 經過配置 不一樣的 hash,緩存文件

  • webpack 內置了多種可以使用 hash,官網解釋分別以下:

    • hash: the hash of the module identifier

    • chunkHash: the hash of the chunk content

    • contentHash: the hash of extracted content

  • 設置 哈希 的長度

    // 全局設置 hash 長度
    
    output.hashDigestLength
    複製代碼
    // 局部設置 hash 長度
    
    [hash:16] 等方式
    複製代碼
  • 配置 什麼時候生成 hash

    // output.hashDigest
    複製代碼
  • 如何選擇正確的 hash

    • JS、分離後的CSS、資源文件(圖片、字體圖標)都使用 hash

      • 每次打包 JS、分離後的CSS 文件 哈希值 都同樣

      • 項目代碼沒有變化,再次打包;全部文件哈希值不變

      • 只要JS、CSS 有一處變化,全部 JS、分離後的CSS 文件 哈希值 都變化

      • 資源文件(圖片、字體圖標)沒更新,哈希值不變;使用 hash,是個不錯的選擇

    • JS、分離後的CSS、資源文件(圖片、字體圖標)都使用 chunkHash

      • 每次打包 同一個頁面的 JS、分離後的CSS 哈希值同樣;不一樣頁面 JS、CSS文件 哈希值不同

      • 項目代碼沒有變化,再次打包;全部文件哈希值不變

      • 項目代碼有變化,只是有更改的JS、對應的CSS文件 哈希值變化;其餘的文件 哈希值不變

      • 資源文件(圖片、字體圖標)不能使用 chunkHash,會報錯

    • 最佳實踐:

      • JS 文件:[name].[chunkHash:8].js

      • 分離後的CSS 文件:[name].[contentHash:8].css

        不會 隨着JS的改變,而更改 hash

      • 資源文件(圖片、字體圖標):[name].[hash:8].[ext]

16. 打包前 自動清除文件 clean-webpack-plugin

  • 使用 clean-webpack-plugin 插件,在打包時先清空上一次打包的文件

    若是不清空,打包後的文件體積會愈來愈大

  • 安裝

    npm i clean-webpack-plugin@1.0.0 -D
    複製代碼
  • 配置

    詳細配置見

    // webpack 中配置
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    
    module.exports = {
        plugins: [
            new CleanWebpackPlugin(['dist'])    // 值爲根目錄下 清空的文件夾
        ]
    }
    複製代碼

17. 根據查找規則 精簡打包

  • webpack 提供內置插件:ContextReplacementPlugin

    官方配置

    // 以下:只打包 moment 的中文包
    
    ...
    
    plugins: [
      new webpack.ContextReplacementPlugin(
        /moment[/\\]locale$/,
        /zh-cn/,
      ),
    ]
    ...
    複製代碼

18. 打包時 代碼分割

  • 新舊版本 API更新

    • webpack3 使用 內置插件 optimize.CommonsChunkPlugin 來分割代碼

    • webpack4 移除內置插件,新增 optimization.splitChunksoptimization.runtimeChunk 來分割代碼

      Webpack 4 的 Code Splitting 最大的特色就是配置簡單(0配置起步),和基於內置規則自動拆分

  • 爲何要分割代碼?

    • 編譯:減小編譯的總體大小,以提升構建性能

    • 運行:減少文件體積,提升加載速度

      這是相對的,在代碼分割減少文件體積的同時,也應該考慮 合理的HTTP請求次數

  • 一般分割哪些代碼?

    • 提取公有代碼

    • 提取經常使用庫代碼

    • 提取 webpack 的 runtime (運行時) 代碼

  • webpack4默認的分割代碼機制,知足以下條件的默認都會被分割

    • 新 bundle 被兩個及以上模塊引用,或者來自 node_modules

    • 新 bundle 大於 30kb (壓縮以前)

    • 異步加載併發加載的 bundle 數不能大於 5 個

    • 初始加載的 bundle 數不能大於 3 個

  • 默認配置以下:

    詳細配置 見官網

    // 默認配置以下:
    module.exports = {
        // ...
        optimization: {
            splitChunks: {
                chunks: 'async',    //默認只做用於異步模塊,爲`all`時對全部模塊生效,`initial`對同步模塊有效
                minSize: 30000,     //合併前模塊文件的體積
                maxSize: 0,
                minChunks: 1,       //最少被引用次數
                maxAsyncRequests: 5,
                maxInitialRequests: 3,
                automaticNameDelimiter: '~',    //自動命名鏈接符
                name: true,
                cacheGroups: {
                    vendors: {
                        test: /[\\/]node_modules[\\/]/,
                        priority: -10           // 同時知足條件的,優先級更高的生效
                    },
                    default: {
                        minChunks: 2,
                        priority: -20,
                        reuseExistingChunk: true
                    }
                }
            }
        }
    };
    複製代碼
  • 自定義代碼分割 optimization.splitChunks.cacheGroups

    optimization.splitChunks.cacheGroups 下每個 chunk 的配置 選項 同 optimization.splitChunks 的配置選項

    若是設置了將 覆蓋 optimization.splitChunks 下的配置選項;若是沒設置 就繼承

    // 將代碼庫 單獨打包
    
    module.exports = {
        optimization: {
            splitChunks: {
                cacheGroups: {
                    libs: {
                        test: /[\\/]node_modules[\\/]/,
                        priority: 20,
                        name: '../libs/index',
                        chunks: 'all'
                    }
                }
            }
        }
    };
    複製代碼
  • 將 webpack runtime 代碼單獨打包

    // 將代碼庫 單獨打包
    
    module.exports = {
        optimization: {
            runtimeChunk: {
                name: 'static/runtime/index'
            }
        }
    };
    複製代碼
  • 特殊場景下,須要咱們 手動代碼分割,可參考以下案例

19. 使用 HappyPack 多線程打包(提高編譯速度)

  • 默認狀況下,webpack 單線程處理任務

    因爲運行在 Node.js 之上的 Webpack 是單線程模型的

    因此Webpack 須要處理的事情須要一件一件的作,不能多件事一塊兒作

  • HappyPack 可讓 webpack 同時處理多個任務

    它將任務分解給多個子進程去併發執行,子進程處理完成後再將結果發送給主進程中

    從而減小總的構建時間,提高構建效率

  • HappyPack 處理 JS,配置以下

    // 安裝
    
    npm i happypack@5.0.1 -D
    複製代碼
    // webpack 配置以下
    
    const os = require('os');
    const HappyPack = require('happypack');
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
    
    
    module.exports = {
        module: {
            rules: [{
                test: /\.js$/,
               
                // 把對.js 的文件處理交給id爲 happyBabel 的 HappyPack 的實例執行
                loader: 'happypack/loader?id=happyBabel'
            }]
        },
        plugins: [
            new HappyPack({
                id: 'happyBabel',                   // 用id來標識 happypack處理那裏類文件
    
                // 如何處理  用法和loader 的配置同樣
                loaders: [{
                    loader: 'babel-loader?cacheDirectory=true'
                }],
                threadPool: happyThreadPool,        // 共享進程池
                verbose: true                       // 容許 HappyPack 輸出日誌
            })
        ]
    };
    複製代碼
  • 說明 module.rules.loader

    • 在 Loader 配置中,全部文件的處理都交給了 happypack/loader 去處理

    • 使用緊跟其後的 ?id=happyBabel 去告訴 happypack/loader 去選擇哪一個 HappyPack 實例去處理文件

  • 說明 HappyPack 參數

    • id: String 用惟一的標識符 id 來表明當前的 HappyPack 是用來處理一類特定的文件

    • loaders: Array 用法和 webpack Loader 配置中同樣

    • threads: Number 表明開啓幾個子進程去處理這一類型的文件,默認是3個,類型必須是整數

    • verbose: Boolean 是否容許 HappyPack 輸出日誌,默認是 true

    • threadPool: HappyThreadPool 表明共享進程池(即多個 HappyPack 實例使用同一個共享進程池中的子進程去處理任務,以防止資源佔用過多)

    • verboseWhenProfiling: Boolean 開啓 webpack --profile ,仍然但願HappyPack產生輸出

    • debug: Boolean 啓用debug 用於故障排查;默認 false

  • 注意:

    • HappyPack 在處理 CSS / SCSS 時,須要單首創建一個 postcss.config.js 文件;不然會報錯

    • HappyPack 對 url-loader 和 file-loader 的支持度有限

  • 存疑

    • 問題:項目中只配置了子線程編譯 JS,編譯速度卻更慢了(項目沒有分離CSS;項目代碼很少,生產環境打包後體積 1.13M)

    • 緣由猜想:HappyPack 多線程的原理是,先開啓子線程處理,完成後再講結果傳遞給主線程,這個時間 超過了 多線程打包節省的時間(因爲須要編譯的JS很少)

20. DLL動態連接庫(提高編譯速度)

  • 認識 DLL

    能夠包含給其餘模塊調用的函數和數據

    用過 Windows 系統的人應該會常常看到以 .dll 爲後綴的文件

  • web 項目構建接入動態連接庫,須要完成如下事情:

    • 把網頁依賴的基礎模塊抽離出來,打包到一個個單獨的動態連接庫中(一個動態連接庫中能夠包含多個模塊)

    • 當須要導入的模塊存在於某個動態連接庫中時,這個模塊不能被再次被打包,而是去動態連接庫中獲取

    • 頁面依賴的全部動態連接庫須要被加載

  • web 項目構建接入動態連接庫,好處:提高構建速度(每次構建 不用再重複打包)

  • webpack 已經內置了對動態連接庫的支持

    • DllPlugin 插件:打包出一個個單獨的動態連接庫文件

    • DllReferencePlugin 插件:在主要配置文件中去引入 DllPlugin 插件打包好的動態連接庫文件

  • 實踐 DLL

    // webpack/dll.config.js 配置文件
    
    const path = require("path");
    const webpack = require("webpack");
    const libs = require('../configs/dll.config');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    
    
    
    module.exports = {
        mode: 'production',
    
        entry: {
            // 把 vue 相關模塊的放到一個單獨的動態連接庫
            vue: ['vue', 'vue-router'],
       
            // 把項目須要全部的 lib 放到一個單獨的動態連接庫
            lib: ['jquery', 'moment'],
        },
    
        output: {
            path: path.resolve(__dirname, './dll'),
            filename: "[name].js",
            library: "_dll_[name]"
        },
    
        plugins: [
            new webpack.DllPlugin({
                name: "_dll_[name]",
                path: path.join(__dirname, 'dll', 'manifest.json'),
            }),
    
            // 清空 dll 文件夾
            new CleanWebpackPlugin(['dll']),
        ]
    }
    
    
    複製代碼
    配置 npm script
    
    {
        "scripts": {
            "dll": "webpack --config webpack/dll.config.js",
        }
    }
    複製代碼
    安裝 依賴包
    
    npm i clean-webpack-plugin@1.0.0 -D
    複製代碼
    // webpack 配置文件
    
    const webpack = require('webpack');
    
    
    module.exports = {
       plugins: [
            // 啓用 dll
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./dll/manifest.json')
            })
        ]
    };
    複製代碼
    // 新dowload下來的項目,須要先打 DLL 包,再運行項目
    複製代碼

21. 全局引入 第三方庫

  • webpack 內置插件,支持全局引入第三方庫

    // webapck.config.js 文件中配置以下:
    
    const webpack = require('webpack');
    
    plugins:[
        new webpack.ProvidePlugin({
            $:"jquery"
        })
    ],
    複製代碼
  • 對比:

    • 全局引入:

      全局引入後,項目代碼中直接使用便可

      若是沒有地方用,第三方庫將不會被打包

    • 局部引入:

      每一個文件中須要的話,都單獨引入

      只要引入了,即便沒用,第三方庫也會打包

22. 可視化 分析打包體積

  • 插件:webpack-bundle-analyzer

    • 效果

    • 生成的報告中有三種尺寸大小:

      • stat: 壓縮等轉換以前的輸入文件大小(從webpack的stat對象裏獲得的

      • parsed: webpack 打包壓縮後 輸出的JS文件大小(不含資源文件、分離後的CSS等)

      • gzip: 通過 gzip 壓縮後的大小

    • 使用

      // 安裝
      
      npm i webpack-bundle-analyzer@3.0.3 -D
      複製代碼
      // webpack 配置
      
      const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
      
      
      module.exports = {
          plugins: [
              new BundleAnalyzerPlugin()
          ]
      };
      
      // 打包完成後 自動在瀏覽器中顯示分析結果
      複製代碼
  • 插件:webpack-chart

  • 插件:webpack-analyse

23. 精簡 終端輸出

  • 部署打包 配置

    // webpack 配置文件
    
    module.exports = {
        // 精簡 終端輸出(打包部署)
        stats: {
            modules: false,
            children: false,
            chunks: false,
            chunkModules: false
        }
    };
    
    複製代碼
  • 本地運行 配置 webpack-dev-server

    // webpack 配置文件
    
    module.exports = {
       devServer: {
            // 精簡 終端輸出(本地運行)
            stats: {
                modules: false,
                children: false,
                chunks: false,
                chunkModules: false
            }
        }
    };
    複製代碼
  • 詳細配置 見 webpack state

24. 顯示打包進度

  • 以下插件,可顯示打包進度,具體配置可查閱 GitHub

    • progress-bar-webpack-plugin

    • nyan-progress-webpack-plugin

    • progress-bar-webpack-plugin

    • progress-webpack-plugin

相關文章
相關標籤/搜索