1. 程式人生 > 前端設計 >webpack效能優化面面觀

webpack效能優化面面觀

前言

開發中,webpack檔案一般分為3個:

  1. webpack.base.conf.js (基礎檔案)
  2. webpack.dev.conf.js (開發環境使用的webpack,需要與webpack.base.conf.js結合使用)
  3. webpack.prod.conf.js(上線環境使用的webpack,需要與webpack.base.conf.js結合使用)

一.優化構建速度

webpack在啟動後,會根據Entry配置的入口,遞迴解析所依賴的檔案。這個過程分為搜尋檔案把匹配的檔案進行分析、轉化的兩個過程,因此可以從這兩個角度來進行優化配置。

1.1 縮小檔案的搜尋範圍

搜尋過程優化方式包括:

1. resolve欄位告訴webpack怎麼去搜索檔案,所以首先要重視resolve欄位的配置:

參考文件:webpack.docschina.org/configurati…

resolve用來配置模組如何解析。例如,當在 ES2015 中呼叫 import 'lodash'resolve 選項能夠對webpack 查詢'lodash' 的方式去做修改(檢視模組)。

// webpack.config.js

module.exports = {
  //...
  resolve: {
    // configuration options
  }
};
複製程式碼
module.export = {
  resolve: {
    modules:[path.resolve(__dirname,'node_modules'
)] extensions: ['.js','.jsx'],mainFiles: ['index','child'], alias: { '@/src': path.resolve(__dirname,`../src`),// 當看到@/src這個路徑或字串的時候,實際上指向的是../src目錄 } } } 複製程式碼

(1). resolve.modules參考文件:www.webpackjs.com/configurati…

resolve.modules告訴webpack解析時應該搜尋的目錄

絕對路徑和相對路徑都能使用,但是要知道他們之間有一點差異。通過檢視當前目錄以及祖先路徑(即 ./node_modules,../node_modules

等等),相對路徑將類似於 Node 查詢 'node_modules' 的方式進行查詢。使用絕對路徑,將只在給定目錄中搜索

// webpack.config.js

module.exports = {
  //...
  resolve: {
    modules: ['node_modules'] // 相對路徑寫法,會按./node_modules,../node_modules的方式查詢
  }
};
複製程式碼

如果你想要新增一個目錄到模組搜尋目錄,此目錄優先於 node_modules/ 搜尋:

// webpack.config.js

module.exports = {
  //...
  resolve: {
    modules: [path.resolve(__dirname,'src'),'node_modules'] // 絕對路徑寫法
  }
};
複製程式碼

因此:設定resolve.modules:[path.resolve(__dirname,'node_modules')]避免層層查詢。 (2). resolve.mainFields參考文件: www.webpackjs.com/configurati…

當從 npm包中匯入模組時(例如,import * as D3 from "d3"),此選項將決定在 package.json 中使用哪個欄位匯入模組。根據 webpack 配置中指定的 target 不同,預設值也會有所不同。

target 屬性設定為webworker,web 或者沒有指定,預設值為:

module.exports = {
  //...
  resolve: {
    mainFields: ['browser','module','main']
  }
};
複製程式碼

對於其他任意的 target(包括 node),預設值為:

module.exports = {
  //...
  resolve: {
    mainFields: ['module','main']
  }
};
複製程式碼

例如,考慮任意一個名為 upstreamlibrary,其 package.json包含以下欄位

{
  "browser": "build/upstream.js","module": "index"
}
複製程式碼

在我們 import * as Upstream from 'upstream' 時,這實際上會從browser 屬性解析檔案。在這裡 browser屬性是最優先選擇的,因為它是 mainFields 的第一項。同時,由 webpack 打包的Node.js 應用程式首先會嘗試從 module 欄位中解析檔案。

(3).resolve.alias參考文件:www.webpackjs.com/configurati…

建立 importrequire 的別名,來確保模組引入變得更簡單。例如,一些位於 src/ 資料夾下的常用模組:

alias: {
  Utilities: path.resolve(__dirname,'src/utilities/'),Templates: path.resolve(__dirname,'src/templates/')
}
複製程式碼

現在,替換「在匯入時使用相對路徑」這種方式,就像這樣:

import Utility from '../../utilities/utility';
複製程式碼

你可以這樣使用別名:

import Utility from 'Utilities/utility';
複製程式碼

也可以在給定物件的鍵後的末尾新增 $,以表示精準匹配:

module.exports = {
  //...
  resolve: {
    alias: {
      xyz$: path.resolve(__dirname,'path/to/file.js')
    }
  }
};
複製程式碼

這將產生以下結果:

import Test1 from 'xyz'; // 精確匹配,所以 path/to/file.js 被解析和匯入
import Test2 from 'xyz/file.js'; // 非精確匹配,觸發普通解析
複製程式碼

PS: 如果你使用了TS,在webpack中使用了resolve.alias,一般需要在tsconfig.json檔案中對其進行配置,否則使用alias會導致無法找到響應目錄而報錯:

// tsconfig.json

"compilerOptions": {
    "paths": {
      "@/src/*": ["./src/*"],'Templates': ["./src/templates/"],},}
複製程式碼

對龐大的第三方模組設定resolve.alias,使webpack直接使用庫的min檔案,避免庫內解析

(4). resolve.extensions參考文件:www.webpackjs.com/configurati…

配置resolve.extensions可以自動解析確定的擴充套件。合理配置resolve.extensions,以減少檔案查詢

resolve.extensions預設值:extensions:['.wasm','.mjs','.js','.json'],當匯入語句沒帶檔案字尾時,Webpack會根據extensions定義的字尾列表進行檔案查詢,所以:

  • 列表值儘量少
  • 頻率高的檔案型別的字尾寫在前面
  • 原始碼中的匯入語句儘可能的寫上檔案字尾,如require(./data)要寫成require(./data.json)

常用寫法:

extensions: ['.js','.json','.ts','.tsx','.scss']
複製程式碼

2. module.noParse欄位告訴Webpack不必解析哪些檔案,可以用來排除對非模組化庫檔案的解析

參考文件:webpack.docschina.org/configurati…

jQuery、ChartJS,另外如果使用resolve.alias配置了react.min.js,則也應該排除解析,因為react.min.js經過構建,已經是可以直接執行在瀏覽器的、非模組化的檔案了。noParse值可以是RegExp、[RegExp]、function

module:{ noParse:[/jquery|chartjs/,/react\.min\.js$/]}
複製程式碼

3. 配置loader時,通過test、exclude、include等縮小搜尋範圍

{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,loader: 'url-loader',options: {
      limit: 10000,name: `/fonts/[name].[hash:8].[ext]`
    }
}
複製程式碼

二.提升開發效率

開發過程中修改程式碼後,需要自動構建和重新整理瀏覽器,以檢視效果。這個過程可以使用Webpack實現自動化,Webpack負責監聽檔案的變化,DevServer負責重新整理瀏覽器。

2.1 使用自動重新整理

2.1.1 Webpack監聽檔案

Webpack可以開啟監聽: 啟動webpack時加上--watch引數

webpack.js.org/configurati…

// package.json

"scripts": {
    "dev": "webpack --watch" // --watch監聽打包檔案,只要發生變化,就會重新打包。只要有這個引數就生效。
}
複製程式碼

但我們想要更豐富的功能:執行npm run dev就會自動打包,並自動開啟瀏覽器,同時可以模擬一些伺服器上的特性,此時就要藉助WebpackDevServer來實現。

devServer:{
    contentBase: './dist' // 伺服器起在哪個資料夾下。WebpackDevServer會幫助我們在這個資料夾下起一個伺服器
}
複製程式碼

配置

devServer:{
    port: 8080,// 預設8080
    contentBase: './dist',open: true,// 自動開啟瀏覽器,並訪問伺服器地址。 file協議不行,不能傳送ajax請求
    proxy: {
        './api': 'http://localhost:3000' // 使用者訪問 /api 這個路徑會被轉發到 http://localhost:3000,支援跨域代理
    }
}
複製程式碼

2.1.2 DevServer重新整理瀏覽器

devServer: {
    contentBase: config.build.assetsRoot,host: config.dev.host,port: config.dev.port,inline: true,hot: true,overlay: {
      warnings: true,errors: true
    },historyApiFallback: {
      rewrites: [
        { from: /^\/index\//,to: `http://${config.dev.host}:${config.dev.port}/index.html` },]
    },noInfo: true,disableHostCheck: true,proxy: {
      // '/user/message': {
      //   target: `http://go.buy.test.mi.com`,//   changeOrigin: true,//   secure: false
      // },}
  },複製程式碼

DevServer重新整理瀏覽器有兩種方式:

  1. 向網頁中注入代理客戶端程式碼,通過客戶端發起重新整理
  2. 向網頁裝入一個iframe,通過重新整理iframe實現重新整理效果

預設情況下,以及 devserver: {inline:true} 都是採用第一種方式重新整理頁面。第一種方式DevServer因為不知道網頁依賴哪些Chunk,所以會向每個chunk中都注入客戶端程式碼,當要輸出很多chunk時,會導致構建變慢。而一個頁面只需要一個客戶端,所以關閉inline模式可以減少構建時間,chunk越多提升越明顯。關閉方式:

  1. 啟動時使用webpack-dev-server --inline false
  2. 配置devserver:{inline:false}

關閉inline後入口網址變為http://localhost:8080/webpack-dev-server/ 另外devServer.compress引數可配置是否採用Gzip壓縮,預設為false

2.2 開啟模組熱替換HMR

模組熱替換不重新整理整個網頁而只重新編譯發生變化的模組,並用新模組替換老模組,所以預覽反應更快,等待時間更少,同時不重新整理頁面能保留當前網頁的執行狀態。原理也是向每一個chunk中注入代理客戶端來連線DevServer和網頁。開啟方式:

webpack-dev-server --hot 使用HotModuleReplacementPlugin,比較麻煩

// package.json
"scripts": {
    "start": "webpack-dev-server",}
複製程式碼

webpack-dev-server打包後的dist中的內容放到了記憶體中,加快訪問速度

const webpack = require('webpack')

module.exports =  {
    devServer:{
        port: 8080,// 預設8080
        contentBase: './dist',// 讓webpack-dev-server開啟Hot Module Replacement功能
        hotOnly: true,// 即使HMR功能沒有生效,也不讓瀏覽器自動重新整理,
    },module: {
        rules: [
        {
            test: /\.css$/,use:  [
                 'style-loader','css-loader','postcss-loader',]
        },plugins: [
      new HtmlWebpackPlugin({
        template: 'src/index.html',}),new CleanWebpackPlugin(['dist']),// 開發環境不需要此配置
      new webpack.HotModuleReplacementPlugin() // 使用webpack外掛,可用於開發環境
   ],}
複製程式碼

開啟後如果修改子模組就可以實現區域性重新整理,但如果修改的是根JS檔案,會整頁重新整理,原因在於,子模組更新時,事件一層層向上傳遞,直到某層的檔案接收了當前變化的模組,然後執行回撥函式。如果一層層向外拋直到最外層都沒有檔案接收,就會重新整理整頁。 使用 NamedModulesPlugin 可以使控制檯打印出被替換的模組的名稱而非數字ID,另外同webpack監聽,忽略node_modules目錄的檔案可以提升效能。

三、優化輸出質量-壓縮檔案體積

3.1 區分環境--減小生產環境程式碼體積

程式碼執行環境分為開發環境生產環境,程式碼需要根據不同環境做不同的操作,許多第三方庫中也有大量的根據開發環境判斷的if else程式碼,構建也需要根據不同環境輸出不同的程式碼,所以需要一套機制可以在原始碼中區分環境,區分環境之後可以使輸出的生產環境的程式碼體積減小。Webpack中使用DefinePlugin外掛來定義配置檔案適用的環境。

3.2 壓縮程式碼-JS、CSS

1. 壓縮JS:Webpack內建UglifyJS外掛、ParallelUglifyPlugin

使用terser-webpack-plugin外掛壓縮JS程式碼: 參考文件: webpack.js.org/plugins/ter…

optimization: {
 minimizer: [
      new TerserPlugin({
        terserOptions: {
          safari10: true
        }
      })
    ],}
複製程式碼

取代了 UglifyJsPlugin

// 取代 new UglifyJsPlugin(/* ... */)
複製程式碼

2. 壓縮CSS

2.1 mini-css-extract-plugin:webpack.js.org/plugins/min… 。該外掛將CSS提取到單獨的檔案中。它為每個包含CSSJS檔案建立一個CSS檔案。它支援CSSSourceMap的按需載入。它基於新的webpack v4功能(模組型別)構建,並且需要webpack 4才能正常工作。

2.2 optimize-css-assets-webpack-plugin: www.npmjs.com/package/opt… 。主要是用來壓縮css檔案

plugins: [
    new MiniCssExtractPlugin({
      filename: path.join('css/[name].css?[contenthash:8]'),chunkFilename: path.join('css/[name].chunk.css?[contenthash:8]')
    }),new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.css\?\w*$/
    })
],複製程式碼

2.3 cssnano基於PostCSS,不僅是刪掉空格,還能理解程式碼含義,例如把color:#ff0000 轉換成 color:redcss-loader內建了cssnano,只需要使用 css-loader?minimize 就可以開啟cssnano壓縮。 另外一種壓縮CSS的方式是使用PurifyCSSPlugin,需要配合 extract-text-webpack-plugin 使用,它主要的作用是可以去除沒有用到的CSS程式碼,類似JSTree Shaking

3.3 使用Tree Shaking剔除JS死程式碼

參考文件:webpack.docschina.org/guides/tree…

Tree Shaking可以剔除用不上的死程式碼,它依賴ES6import、export的模組化語法,最先在Rollup中出現,Webpack 2.0將其引入。適合用於Lodash、utils.js等工具類較分散的檔案。它正常工作的前提是程式碼必須採用ES6的模組化語法,因為ES6模組化語法是靜態的(在匯入、匯出語句中的路徑必須是靜態字串,且不能放入其他程式碼塊中)。如果採用了ES5中的模組化,例如module.export = {...}、require( x+y )、if (x) { require( './util' ) },則Webpack無法分析出可以剔除哪些程式碼。

tree shaking 是一個術語,通常用於描述移除 JavaScript 上下文中的未引用程式碼(dead-code)。它依賴於 ES2015模組語法的 靜態結構 特性,例如importexport。這個術語和概念實際上是由 ES2015 模組打包工具 rollup 普及起來的。

webpack 4正式版本擴充套件了此檢測能力,通過package.json"sideEffects" 屬性作為標記,向 compiler 提供提示,表明專案中的哪些檔案是 "pure(純的 ES2015 模組)",由此可以安全地刪除檔案中未使用的部分。

參考文件:webpack.docschina.org/guides/tree…

注意,所有匯入檔案都會受到tree shaking 的影響。這意味著,如果在專案中使用類似css-loaderimport 一個 CSS 檔案,則需要將其新增到side effect列表中,以免在生產模式中無意中將它刪除:

{
  "name": "your-project","sideEffects": [
    "./src/some-side-effectful-file.js","*.css"
  ]
}
複製程式碼

參考文件:webpack.docschina.org/guides/tree…

通過 importexport語法,我們已經找出需要刪除的“未引用程式碼(dead code)”,然而,不僅僅是要找出,還要在 bundle 中刪除它們。為此,我們需要將 mode配置選項設定為 production

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',output: {
    filename: 'bundle.js',path: path.resolve(__dirname,'dist')
  },- mode: 'development',- optimization: {
-   usedExports: true
- }
+ mode: 'production'
};
複製程式碼
注意,也可以在命令列介面中使用 --optimize-minimize 標記,來啟用 TerserPlugin。
複製程式碼

準備就緒後,然後執行另一個 npm script npm run build,就會看到輸出結果發生了改變。

dist/bundle.js 中,現在整個 bundle 都已經被 minify(壓縮) 和 mangle(混淆破壞),但是如果仔細觀察,則不會看到引入 square 函式,但能看到 cube函式的混淆破壞版本(function r(e){return e*e*e}n.a=r)。現在,隨著 minification(程式碼壓縮) 和tree shaking,我們的bundle 減小几個位元組!雖然,在這個特定示例中,可能看起來沒有減少很多,但是,在有著複雜依賴樹的大型應用程式上執行 tree shaking時,會對 bundle 產生顯著的體積優化。

執行 tree shaking 需要 ModuleConcatenationPlugin。通過 mode: "production" 可以新增此外掛。如果你沒有使用 mode 設定,記得手動新增 ModuleConcatenationPlugin。
複製程式碼

參考文件:webpack.docschina.org/guides/tree…

結論: 我們已經知道,想要使用 tree shaking 必須注意以下幾點:

  • 使用 ES2015模組語法(即 importexport)。
  • 確保沒有 compilerES2015 模組語法轉換為 CommonJS模組(這也是流行的 Babel preset@babel/preset-env 的預設行為 - 更多詳細資訊請檢視 文件)。
  • 在專案package.json 檔案中,新增一個"sideEffects" 屬性。
  • 通過將 mode選項設定為 production,啟用 minification(程式碼壓縮) 和tree shaking

你可以將應用程式想象成一棵樹。綠色表示實際用到的 source code(原始碼)library(庫),是樹上活的樹葉。灰色表示未引用程式碼,是秋天樹上枯萎的樹葉。為了除去死去的樹葉,你必須搖動這棵樹,使它們落下。

四、優化輸出質量--加速網路請求

4.1 使用CDN加速靜態資源載入

1. CND加速的原理

CDN通過將資源部署到世界各地,使得使用者可以就近訪問資源,加快訪問速度。要接入CDN,需要把網頁的靜態資源上傳到CDN服務上,在訪問這些資源時,使用CDN服務提供的URL

由於CDN會為資源開啟長時間的快取,例如使用者從CDN上獲取了index.html,即使之後替換了CDN上的index.html,使用者那邊仍會在使用之前的版本直到快取時間過期。業界做法:

  • HTML檔案:放在自己的伺服器上且關閉快取,不接入CDN
  • 靜態的JS、CSS、圖片等資源:開啟CDN和快取,同時檔名帶上由內容計算出的Hash值,這樣只要內容變化hash就會變化,檔名就會變化,就會被重新下載而不論快取時間多長。

另外,HTTP1.x版本的協議下,瀏覽器會對於向同一域名並行發起的請求數限制在4~8個。那麼把所有靜態資源放在同一域名下的CDN服務上就會遇到這種限制,所以可以把他們分散放在不同的CDN服務上,例如JS檔案放在js.cdn.com下,將CSS檔案放在css.cdn.com下等。這樣又會帶來一個新的問題:增加了域名解析時間,這個可以通過dns-prefetch來解決 <link rel='dns-prefetch' href='//js.cdn.com'> 來縮減域名解析的時間。形如**//xx.com 這樣的URL省略了協議**,這樣做的好處是,瀏覽器在訪問資源時會自動根據當前URL採用的模式來決定使用HTTP還是HTTPS協議。

當瀏覽器從第三方服務跨域請求資源的時候,在瀏覽器發起請求之前,這個第三方的跨域域名需要被解析為一個IP地址,這個過程就是DNS解析,DNS快取可以用來減少這個過程的耗時,DNS解析可能會增加請求的延遲,對於那些需要請求許多第三方的資源的網站而言,DNS解析的耗時延遲可能會大大降低網頁載入效能。

參考文章: developer.mozilla.org/zh-CN/docs/…

2. 總之,構建需要滿足以下幾點:

  • 靜態資源匯入的URL要變成指向CDN服務的絕對路徑的URL
  • 靜態資源的檔名需要帶上根據內容計算出的Hash
  • 不同型別資源放在不同域名的CDN

3. 最終配置:

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {WebPlugin} = require('web-webpack-plugin');
//...
output:{
 filename: '[name]_[chunkhash:8].js','dist'),publicPatch: '//js.cdn.com/id/',//指定存放JS檔案的CDN地址
},module:{
 rules:[{
     test: /\.css/,use: ExtractTextPlugin.extract({
         use: ['css-loader?minimize'],publicPatch: '//img.cdn.com/id/',//指定css檔案中匯入的圖片等資源存放的cdn地址
     }),{
    test: /\.png/,use: ['file-loader?name=[name]_[hash:8].[ext]'],//為輸出的PNG檔名加上Hash值 
 }]
},plugins:[
  new WebPlugin({
     template: './template.html',filename: 'index.html',stylePublicPath: '//css.cdn.com/id/',//指定存放CSS檔案的CDN地址
  }),new ExtractTextPlugin({
     filename:`[name]_[contenthash:8].css`,//為輸出的CSS檔案加上Hash
 })
]

複製程式碼

4.2 多頁面應用提取頁面間公共程式碼,以利用快取

  1. 原理

大型網站通常由多個頁面組成,每個頁面都是一個獨立的單頁應用,多個頁面間肯定會依賴同樣的樣式檔案、技術棧等。如果不把這些公共檔案提取出來,那麼每個單頁打包出來的chunk中都會包含公共程式碼,相當於要傳輸n份重複程式碼。如果把公共檔案提取出一個檔案,那麼當用戶訪問了一個網頁,載入了這個公共檔案,再訪問其他依賴公共檔案的網頁時,就直接使用檔案在瀏覽器的快取,這樣公共檔案就只用被傳輸一次。

  1. 應用方法

把多個頁面依賴的公共程式碼提取到common.js中,此時common.js包含基礎庫的程式碼

把多個頁面依賴的公共程式碼提取到common.js中,此時common.js包含基礎庫的程式碼
複製程式碼

找出依賴的基礎庫,寫一個base.js檔案,再與common.js提取公共程式碼到base中,common.js就剔除了基礎庫程式碼,而base.js保持不變

//base.js
import 'react';
import 'react-dom';
import './base.css';
//webpack.config.json
entry:{
    base: './base.js'
},plugins:[
    new CommonsChunkPlugin({
        chunks:['base','common'],name:'base',//minChunks:2,表示檔案要被提取出來需要在指定的chunks中出現的最小次數,防止common.js中沒有程式碼的情況
    })        
]
複製程式碼
  1. 得到基礎庫程式碼base.js,不含基礎庫的公共程式碼common.js,和頁面各自的程式碼檔案xx.js

頁面引用順序如下:base.js--> common.js--> xx.js

4.3 分割程式碼以按需載入

  1. 原理

單頁應用的一個問題在於使用一個頁面承載複雜的功能,要載入的檔案體積很大,不進行優化的話會導致首屏載入時間過長,影響使用者體驗。做按需載入可以解決這個問題。具體方法如下:

  • 將網站功能按照相關程度劃分成幾類
  • 每一類合併成一個Chunk,按需載入對應的Chunk
  • 例如,只把首屏相關的功能放入執行入口所在的Chunk,這樣首次載入少量的程式碼,其他程式碼要用到的時候再去載入。最好提前預估使用者接下來的操作,提前載入對應程式碼,讓使用者感知不到網路載入
  1. 做法

一個最簡單的例子:網頁首次只加載main.js,網頁展示一個按鈕,點選按鈕時載入分割出去的show.js,載入成功後執行show.js裡的函式

//main.js
document.getElementById('btn').addEventListener('click',function(){
    import(/* webpackChunkName:"show" */ './show').then((show)=>{
        show('Webpack');
    })
})
//show.js
module.exports = function (content) {
    window.alert('Hello ' + content);
}

複製程式碼

import(/* webpackChunkName:show */ './show').then() 是實現按需載入的關鍵Webpack內建對import( *)語句的支援,Webpack會以./show.js為入口重新生成一個Chunk。程式碼在瀏覽器上執行時只有點選了按鈕才會開始載入show.js,且import語句會返回一個Promise,載入成功後可以在then方法中獲取載入的內容。這要求瀏覽器支援Promise API,對於不支援的瀏覽器,需要注入Promise polyfill/* webpackChunkName:show */ 是定義動態生成的Chunk的名稱,預設名稱是[id].js,定義名稱方便除錯程式碼。為了正確輸出這個配置的ChunkName,還需要配置Webpack

//...
output:{
    filename:'[name].js',chunkFilename:'[name].js',// 指定動態生成的Chunk在輸出時的檔名稱
}
複製程式碼

五、優化輸出質量--提升程式碼執行時的效率

5.1 使用Prepack提前求值

  1. 原理:

Prepack是一個部分求值器,編譯程式碼時提前將計算結果放到編譯後的程式碼中,而不是在程式碼執行時才去求值。通過在便一階段預先執行原始碼來得到執行結果,再直接將執行結果輸出以提升效能。但是現在Prepack還不夠成熟,用於線上環境還為時過早。

參考文件:github.com/facebook/pr…

  1. 使用方法
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default;
module.exports = {
    plugins:[
        new PrepackWebpackPlugin()
    ]
}
複製程式碼

5.2 使用Scope Hoisting

  1. 原理

譯作“作用域提升”,是在Webpack3中推出的功能,它分析模組間的依賴關係,儘可能將被打散的模組合併到一個函式中,但不能造成程式碼冗餘,所以只有被引用一次的模組才能被合併。由於需要分析模組間的依賴關係,所以原始碼必須是採用了ES6模組化的,否則Webpack會降級處理不採用Scope Hoisting

  1. 使用方法
const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
//...
plugins:[
    new ModuleConcatenationPlugin();
],resolve:{
	mainFields:['jsnext:main','browser','main']
}
複製程式碼

webpack --display-optimization-bailout 輸出日誌中會提示哪個檔案導致了降級處理

六、使用輸出分析工具

啟動Webpack時帶上這兩個引數可以生成一個json檔案,輸出分析工具大多依賴該檔案進行分析: webpack --profile --json > stats.json 其中 --profile 記錄構建過程中的耗時資訊,--jsonJSON的格式輸出構建結果,>stats.jsonUNIX / Linux系統中的管道命令,含義是將內容通過管道輸出到stats.json檔案中。

  1. 官方工具Webpack Analyse

開啟該工具的官網http://webpack.github.io/analyse/上傳stats.json,就可以得到分析結果

  1. webpack-bundle-analyzer

視覺化分析工具,比Webapck Analyse更直觀。使用也很簡單:

npm i -g webpack-bundle-analyzer安裝到全域性 按照上面方法生成stats.json檔案 在專案根目錄執行webpack-bundle-analyzer,瀏覽器會自動開啟結果分析頁面。