開發中,webpack文件通常分爲3個:css
webpack.base.conf.js
(基礎文件)webpack.dev.conf.js
(開發環境使用的webpack
,須要與webpack.base.conf.js
結合使用)webpack.prod.conf.js
(上線環境使用的webpack
,須要與webpack.base.conf.js
結合使用)webpack
在啓動後,會根據Entry
配置的入口,遞歸解析所依賴的文件。這個過程分爲搜索文件和把匹配的文件進行分析、轉化的兩個過程,所以能夠從這兩個角度來進行優化配置。html
resolve
字段告訴webpack
怎麼去搜索文件,因此首先要重視resolve
字段的配置:參考文檔:webpack.docschina.org/configurati…node
resolve
用來配置模塊如何解析。例如,當在 ES2015
中調用 import 'lodash'
,resolve
選項可以對webpack
查找'lodash'
的方式去作修改(查看模塊)。react
// 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…jquery
resolve.modules
告訴webpack
解析時應該搜索的目錄。webpack
絕對路徑和相對路徑都能使用,可是要知道他們之間有一點差別。經過查看當前目錄以及祖先路徑(即 ./node_modules, ../node_modules
等等),相對路徑將相似於 Node
查找 'node_modules
' 的方式進行查找。使用絕對路徑,將只在給定目錄中搜索。git
// webpack.config.js
module.exports = {
//...
resolve: {
modules: ['node_modules'] // 相對路徑寫法,會按./node_modules, ../node_modules的方式查找
}
};
複製代碼
若是你想要添加一個目錄到模塊搜索目錄,此目錄優先於 node_modules/
搜索:github
// 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…web
當從 npm
包中導入模塊時(例如,import * as D3 from "d3"
),此選項將決定在 package.json
中使用哪一個字段導入模塊。根據 webpack
配置中指定的 target
不一樣,默認值也會有所不一樣。ajax
當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']
複製代碼
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$/]}
複製代碼
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: `/fonts/[name].[hash:8].[ext]`
}
}
複製代碼
開發過程當中修改代碼後,須要自動構建和刷新瀏覽器,以查看效果。這個過程可使用Webpack
實現自動化,Webpack
負責監聽文件的變化,DevServer
負責刷新瀏覽器。
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,支持跨域代理
}
}
複製代碼
devServer: {
contentBase: config.build.assetsRoot,
host: config.dev.host,
port: config.dev.port,
open: true,
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
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',
open: true,
hot: true, // 讓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
目錄的文件能夠提高性能。
代碼運行環境分爲開發環境和生產環境,代碼須要根據不一樣環境作不一樣的操做,許多第三方庫中也有大量的根據開發環境判斷的if else
代碼,構建也須要根據不一樣環境輸出不一樣的代碼,因此須要一套機制能夠在源碼中區分環境,區分環境以後可使輸出的生產環境的代碼體積減少。Webpack
中使用DefinePlugin
插件來定義配置文件適用的環境。
Webpack
內置UglifyJS
插件、ParallelUglifyPlugin
使用terser-webpack-plugin
插件壓縮JS
代碼: 參考文檔: webpack.js.org/plugins/ter…
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
safari10: true
}
})
],
}
複製代碼
取代了 UglifyJsPlugin
// 取代 new UglifyJsPlugin(/* ... */)
複製代碼
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
。
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(庫)
,是樹上活的樹葉。灰色表示未引用代碼,是秋天樹上枯萎的樹葉。爲了除去死去的樹葉,你必須搖動這棵樹,使它們落下。
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/…
URL
要變成指向CDN
服務的絕對路徑的URL
Hash
值CDN
上const ExtractTextPlugin = require('extract-text-webpack-plugin');
const {WebPlugin} = require('web-webpack-plugin');
//...
output:{
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, '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
})
]
複製代碼
大型網站一般由多個頁面組成,每一個頁面都是一個獨立的單頁應用,多個頁面間確定會依賴一樣的樣式文件、技術棧等。若是不把這些公共文件提取出來,那麼每一個單頁打包出來的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
單頁應用的一個問題在於使用一個頁面承載複雜的功能,要加載的文件體積很大,不進行優化的話會致使首屏加載時間過長,影響用戶體驗。作按需加載能夠解決這個問題。具體方法以下:
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在輸出時的文件名稱
}
複製代碼
Prepack
提早求值Prepack
是一個部分求值器,編譯代碼時提早將計算結果放到編譯後的代碼中,而不是在代碼運行時纔去求值。經過在便一階段預先執行源碼來獲得執行結果,再直接將運行結果輸出以提高性能。可是如今Prepack
還不夠成熟,用於線上環境還爲時過早。
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default;
module.exports = {
plugins:[
new PrepackWebpackPlugin()
]
}
複製代碼
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
,瀏覽器會自動打開結果分析頁面。