webpack效能優化面面觀
前言
開發中,webpack檔案一般分為3個:
-
webpack.base.conf.js
(基礎檔案) -
webpack.dev.conf.js
(開發環境使用的webpack
,需要與webpack.base.conf.js
結合使用) -
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']
}
};
複製程式碼
例如,考慮任意一個名為 upstream
的 library
,其 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…
建立 import
或 require
的別名,來確保模組引入變得更簡單。例如,一些位於 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
引數
// 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
重新整理瀏覽器有兩種方式:
- 向網頁中注入代理客戶端程式碼,通過客戶端發起重新整理
- 向網頁裝入一個
iframe
,通過重新整理iframe
實現重新整理效果
預設情況下,以及 devserver: {inline:true}
都是採用第一種方式重新整理頁面。第一種方式DevServer
因為不知道網頁依賴哪些Chunk
,所以會向每個chunk
中都注入客戶端程式碼,當要輸出很多chunk
時,會導致構建變慢。而一個頁面只需要一個客戶端,所以關閉inline
模式可以減少構建時間,chunk
越多提升越明顯。關閉方式:
- 啟動時使用
webpack-dev-server --inline false
- 配置
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
提取到單獨的檔案中。它為每個包含CSS
的JS
檔案建立一個CSS
檔案。它支援CSS
和SourceMap
的按需載入。它基於新的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:red
,css-loader
內建了cssnano
,只需要使用 css-loader?minimize
就可以開啟cssnano
壓縮。
另外一種壓縮CSS
的方式是使用PurifyCSSPlugin,需要配合 extract-text-webpack-plugin
使用,它主要的作用是可以去除沒有用到的CSS
程式碼,類似JS
的Tree Shaking
。
3.3 使用Tree Shaking
剔除JS
死程式碼
參考文件:webpack.docschina.org/guides/tree…
Tree Shaking
可以剔除用不上的死程式碼,它依賴ES6
的import、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
模組語法的 靜態結構 特性,例如import
和 export
。這個術語和概念實際上是由 ES2015
模組打包工具 rollup
普及起來的。
webpack 4
正式版本擴充套件了此檢測能力,通過package.json
的 "sideEffects"
屬性作為標記,向 compiler
提供提示,表明專案中的哪些檔案是 "pure(純的 ES2015 模組)
",由此可以安全地刪除檔案中未使用的部分。
參考文件:webpack.docschina.org/guides/tree…
注意,所有匯入檔案都會受到tree shaking
的影響。這意味著,如果在專案中使用類似css-loader
並import
一個 CSS
檔案,則需要將其新增到side effect
列表中,以免在生產模式中無意中將它刪除:
{
"name": "your-project","sideEffects": [
"./src/some-side-effectful-file.js","*.css"
]
}
複製程式碼
參考文件:webpack.docschina.org/guides/tree…
通過 import
和 export
語法,我們已經找出需要刪除的“未引用程式碼(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
模組語法(即import
和export
)。 - 確保沒有
compiler
將ES2015
模組語法轉換為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 多頁面應用提取頁面間公共程式碼,以利用快取
- 原理
大型網站通常由多個頁面組成,每個頁面都是一個獨立的單頁應用,多個頁面間肯定會依賴同樣的樣式檔案、技術棧等。如果不把這些公共檔案提取出來,那麼每個單頁打包出來的chunk
中都會包含公共程式碼,相當於要傳輸n
份重複程式碼。如果把公共檔案提取出一個檔案,那麼當用戶訪問了一個網頁,載入了這個公共檔案,再訪問其他依賴公共檔案的網頁時,就直接使用檔案在瀏覽器的快取,這樣公共檔案就只用被傳輸一次。
- 應用方法
把多個頁面依賴的公共程式碼提取到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中沒有程式碼的情況
})
]
複製程式碼
- 得到基礎庫程式碼
base.js
,不含基礎庫的公共程式碼common.js
,和頁面各自的程式碼檔案xx.js
。
頁面引用順序如下:base.js
--> common.js
--> xx.js
4.3 分割程式碼以按需載入
- 原理
單頁應用的一個問題在於使用一個頁面承載複雜的功能,要載入的檔案體積很大,不進行優化的話會導致首屏載入時間過長,影響使用者體驗。做按需載入可以解決這個問題。具體方法如下:
- 將網站功能按照相關程度劃分成幾類
- 每一類合併成一個
Chunk
,按需載入對應的Chunk
- 例如,只把首屏相關的功能放入執行入口所在的
Chunk
,這樣首次載入少量的程式碼,其他程式碼要用到的時候再去載入。最好提前預估使用者接下來的操作,提前載入對應程式碼,讓使用者感知不到網路載入
- 做法
一個最簡單的例子:網頁首次只加載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
提前求值
- 原理:
Prepack
是一個部分求值器,編譯程式碼時提前將計算結果放到編譯後的程式碼中,而不是在程式碼執行時才去求值。通過在便一階段預先執行原始碼來得到執行結果,再直接將執行結果輸出以提升效能。但是現在Prepack
還不夠成熟,用於線上環境還為時過早。
- 使用方法
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default;
module.exports = {
plugins:[
new PrepackWebpackPlugin()
]
}
複製程式碼
5.2 使用Scope Hoisting
- 原理
譯作“作用域提升”,是在Webpack3
中推出的功能,它分析模組間的依賴關係,儘可能將被打散的模組合併到一個函式中,但不能造成程式碼冗餘,所以只有被引用一次的模組才能被合併。由於需要分析模組間的依賴關係,所以原始碼必須是採用了ES6
模組化的,否則Webpack
會降級處理不採用Scope Hoisting
。
- 使用方法
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
記錄構建過程中的耗時資訊,--json
以JSON
的格式輸出構建結果,>stats.json
是UNIX / Linux
系統中的管道命令,含義是將內容通過管道輸出到stats.json
檔案中。
- 官方工具
Webpack Analyse
開啟該工具的官網http://webpack.github.io/analyse/
上傳stats.json
,就可以得到分析結果
webpack-bundle-analyzer
視覺化分析工具,比Webapck Analyse
更直觀。使用也很簡單:
npm i -g webpack-bundle-analyzer
安裝到全域性
按照上面方法生成stats.json
檔案
在專案根目錄執行webpack-bundle-analyzer
,瀏覽器會自動開啟結果分析頁面。