筆者最近在整理關於 webpack
相關的知識點,一方面是由於本身掌握的知識點比較零碎、不夠系統,有時候碰到問題不知從何下手,另一方面 webpack5.0
已經在路上了,這的確是一個讓人頭禿的消息。javascript
因此這就促使了我去系統性的回顧了一遍 webpack4.0
的全部知識點,包括 webpack
的由來,各類配置的使用、性能優化、Webpack
的底層原理、相關腳手架的配置分析,都回顧了一波,大體目錄以下圖:css
筆者把系列的文章都扔在了這個倉庫:webpack 學習整理文檔,有興趣的同窗能夠去看一波。html
今天這篇文章也是筆者就學習文檔中的性能優化這一塊內容作的整理與回顧。前端
文章中使用到的案例代碼連接放在了最底部,你們自取。
先來講說爲何要優化?固然若是你的項目很小,構建很快,其實不須要特別關注性能方面的問題。java
可是隨着項目涉及到的頁面愈來愈多,功能和業務代碼也會愈來愈多,相應的 webpack
的構建時間也會愈來愈久,這個時候咱們就不得不考慮性能優化的事情了。node
由於這個構建時間與咱們的平常開發是密切相關,當咱們本地開發啓動 devServer
或者 build
的時候,若是時間過長,會大大下降咱們的工做效率。react
試想一個場景,咱們忽然碰到一個緊急 bug
,項目啓動須要花費 3/4
分鐘,改完後項目 build
上線也要 3/4
分鐘,這個時候腦瓜是否是 duang
、duang
、duang
...webpack
那接下來咱們看一下如何優化 webpack
的性能,提高 webpack
的構建速度。git
在動手優化以前,咱們須要有一個量化的指標,得知道影響構建時間的問題究竟出在哪裏,是某個 chunk
文件太大了,仍是哪個 loader
或者 plugin
耗時過久了等等。github
咱們能夠對經過一些工具對項目進行相應的 體積 和 速度 分析, 而後對症下藥。
能夠經過官方提供的 stat.json
文件幫助咱們分析打包結果,stat.json
文件能夠經過下面語句快速生成:
webpack --profile --json > stats.json
接着咱們經過官網提供的 stats.json 分析工具 進行分析,上傳 stats.json
文件以後,就能夠獲得以下圖所示分析結果:
其中包括 webpack
的版本、打包時間、打包過程的 hash
值、模塊數量( modules
)、chunk
數量、打包生層的靜態文件 assets
以及打包的警告和錯誤數。
咱們能夠分析其提供的內容,進行大體問題的定位。
webpack-bundle-analyzer 是打包分析神器,它的界面我的以爲很好看,並且能很直觀的給出每個打包出來的文件的大小以及各自的依賴,可以更加方便的幫助咱們對項目進行分析。
使用以下:
// config/webpack.common.js const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const commonConfig = { // ... plugins: [ new BundleAnalyzerPlugin({ analyzerPort: 8889, // 指定端口號 openAnalyzer: false, }), ] // ... }
webpack-bundle-analyzer
其底層也是依賴stat.json
文件的,經過對stat.json
的分析,得出最後的分析頁面
經過分析工具的分析,咱們能夠知道哪些文件耗時比較多,打包出來的體積比較大,從而對有問題的文件進行優化。
咱們能夠經過 speed-measure-webpack-plugin 這個插件幫助咱們分析整個打包的總耗時,以及每個loader
和每個 plugins
構建所耗費的時間,從而幫助咱們快速定位到能夠優化 Webpack
的配置。
如上圖,耗時比較長的會以紅色標出。
引入此插件,建立一個 plugins
實例 smp
包裹 webpack
配置文件便可,咱們修改一下 webpack
的公共配置文件 webpack.common.js
:
// config/webpack.common.js const SpeedMeasurePlugin = require("speed-measure-webpack-plugin"); const smp = new SpeedMeasurePlugin(); // ... module.exports = (production) => { if (production) { const endProdConfig = merge(commonConfig, prodConfig); return smp.wrap(endProdConfig); } else { const endDevConfig = merge(commonConfig, devConfig); return smp.wrap(endDevConfig); } };
筆者文章演示的代碼配置文件分爲三個,分別爲 開發環境配置文件,生產環境配置文件,以及前兩者共用的公共配置文件,以下:
webpack.dev.js
:開發環境使用的配置文件webpack.prod.js
:生產環境使用的配置文件webpack.common.js
:公共配置文件
執行打包以後,能夠看到以下效果圖:
注意:speed-measure-webpack-plugin
對於webpack
的升級還不夠完善,暫時還沒法與你本身編寫的掛載在html-webpack-plugin
提供的hooks
上的自定義Plugin
(add-asset-html-webpack-plugin
就是此類)共存,有人已經在 github 上提了 issue 了,可是貌似仍是沒有解決。
通過相應的體積分析和速度分析以後,咱們即可以着手進行優化了。
這個是 webpack
性能優化的萬能膏藥,升級版本一定能帶來性能提高,並且提高很明顯。
咱們能夠看一張對比圖:
從上圖中咱們能夠看到,webpack4.0
的構建速度遠遠快於webpack3.0
,官方也說升級以後,升級版本以後,構建時間能夠下降60% - 98%
左右。
在每個版本的更新,webpack
內部確定會作不少優化,而 webpack
是依賴 Node
的 js
運行環境,升級他們對應的版本,webpack
的速度確定也可以得到提高。
說不定在webpack5.0
出來以後,咱們今天講到的大部分性能優化方法都會被集成到webpack
自身中去,咱們只須要經過幾個簡單的配置就能完成性能配置。
同時新版本的包管理工具(Npm
、Yarn
)也能夠更快的幫助咱們分析一些包的依賴和引入,從而提升打包速度。
v8
引擎帶來的優化(for of
替代 forEach
、Map
和 Set
替代 Object
、includes
替代 indexOf
)md4 hash
算法webpack AST
能夠直接從 loader
傳遞給 AST
,減小解析時間咱們能夠在 github
上的 webpack
庫的 releases 版本迭代 頁面中查看其帶來的性能優化:
v8
性能優化例子:咱們能夠來看一個例子,比較使用 includes
替代 indexOf
以後帶來的速度提高,建立 compare-includes-indexof.js
文件,在這個文件中建一個 10000000
長度的數組,記錄兩個函數分別消耗的時間:
const ARR_SIZE = 10000000; const hugeArr = new Array(ARR_SIZE).fill(1); // includes const includesTest = () => { const arrCopy = []; console.time('includes') let i = 0; while (i < hugeArr.length) { arrCopy.includes(i++); } console.timeEnd('includes'); } // indexOf const indexOfTest = () => { const arrCopy = []; console.time('indexOf'); for (let item of hugeArr) { arrCopy.indexOf(item); } console.timeEnd('indexOf'); } includesTest(); indexOfTest();
能夠發現 includes
的速度遠遠快於 indexOf
:
includes
:12.224ms
indexOf
:147.638ms
因此在項目上儘量使用比較新的 webpack
、Node
、Npm
、Yarn
版本,是咱們提高打包速度的第一步。
webpack
是個項目打包工具,通常項目打完包之後,須要發佈到服務器上供用戶使用,爲了用戶體驗,咱們的項目體積須要越小越好,因此 webpack
中打包的體積是 webpack
中重要的一環。
webpack4.0
默認在生產環境的時候是支持代碼壓縮的,即 mode=production
模式下。
實際上 webpack4.0
默認是使用 terser-webpack-plugin 這個壓縮插件,在此以前是使用 uglifyjs-webpack-plugin,二者的區別是後者對 ES6 的壓縮不是很好,同時咱們能夠開啓 parallel
參數,使用多進程壓縮,加快壓縮。
// config/webpack.common.js const TerserPlugin = require('terser-webpack-plugin'); // ... const commonConfig = { // ... optimization: { minimize: true, minimizer: [ new TerserPlugin({ parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1 }), ], }, // ... }
咱們能夠藉助 optimize-css-assets-webpack-plugin
插件來壓縮 css
,其默認使用的壓縮引擎是 cssnano
。 具體使用以下:
// config/webpack.prod.js const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // ... const prodConfig = { // ... optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({ assetNameRegExp: /\.optimize\.css$/g, cssProcessor: require('cssnano'), cssProcessorPluginOptions: { preset: ['default', { discardComments: { removeAll: true } }], }, canPrint: true, }) ] }, }
CSS
使用 PurgeCSS
來完成對無用 css
的擦除,它須要和 mini-css-extract-plugin
配合使用。
// config/webpack.common.js const PurgecssPlugin = require('purgecss-webpack-plugin'); // ... const PATHS = { src: path.join(__dirname, './src') }; const commonConfig = { // ... plugins: [ // ... new PurgecssPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), }), ] // ... }
在未使用此插件以前,好比咱們只用到了 navcontact
這個類,其餘的都沒有用到,咱們在未引入以前打包一下,發現未用到的 css
仍是會被打包進去:
引入插件後,從新進行打包,發現沒有用到的 css
都被擦除了:
更多使用你們可參考 PurgeCSS 文檔。
通常來講在打包以後,一些圖片文件的大小是遠遠要比 js
或者 css
文件要來的大,因此咱們首先要作的就是對於圖片的優化,咱們能夠手動的去經過線上的圖片壓縮工具,如 tiny png 幫咱們來壓縮圖片。
可是這個比較繁瑣,在項目中咱們但願可以更加自動化一點,自動幫咱們作好圖片壓縮,這個時候咱們就能夠藉助 image-webpack-loader 幫助咱們來實現。它是基於 imagemin 這個 Node 庫來實現圖片壓縮的。
使用很簡單,咱們只要在 file-loader
以後加入 image-webpack-loader
便可:
// config/webpack.common.js // ... module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { name: '[name]_[hash].[ext]', outputPath: 'images/', } }, { loader: 'image-webpack-loader', options: { // 壓縮 jpeg 的配置 mozjpeg: { progressive: true, quality: 65 }, // 使用 imagemin**-optipng 壓縮 png,enable: false 爲關閉 optipng: { enabled: false, }, // 使用 imagemin-pngquant 壓縮 png pngquant: { quality: '65-90', speed: 4 }, // 壓縮 gif 的配置 gifsicle: { interlaced: false, }, // 開啓 webp,會把 jpg 和 png 圖片壓縮爲 webp 格式 webp: { quality: 75 } } } ] }, ] } // ...
咱們先不使用這個 loader
打包一下,圖片大小是 2.1MB
:
使用 image-webpack-loader
以後,圖片大小是 666KB
:
壓縮的效果仍是很明顯的。
有時候咱們寫的某些模塊根本沒有使用,可是仍是被打包了,這樣實際上會拖累 webpack
的打包速度,並且也會增長打包文件的體積,因此咱們可使用 tree-shaking
將這些代碼剔除掉。
或者也可使用 splitChunksPlugin
把一個大的文件分割成幾個小的文件,這樣也能夠有效的提高 webpack
的打包速度,詳細的配置介紹你們能夠看筆者寫的 配置 SplitChunksPlugin,裏面詳細介紹了怎麼配置 splitChunks
,以及各參數的用法與意義,在這裏就不展開講了。
講完打包體積的優化,咱們來看一下在速度方面的優化。
通常來講在項目開發中,咱們會區分開發和生產環境兩套配置,各司其職。
在開發階段:咱們須要 webpack-dev-server
來幫咱們進行快速的開發,同時須要 HMR 熱更新 幫咱們進行頁面的無刷新改動,而這些在 生產環境 中都是不須要的。
在生產階段:咱們須要進行 代碼壓縮、目錄清理、計算 hash、提取 CSS 等等;
實現起來很簡單,咱們前面也提到過,就新建三個 webpack
的配置文件就行:
webpack.dev.js
:開發環境的配置文件webpack.prod.js
:生產環境的配置文件webpack.common.js
:公共配置文件經過 webpack-merge
來整合兩個配置文件共同的配置 webpack.common.js
,具體能夠參照源碼。
對 webpack
的 resolve
參數進行合理配置,使用 resolve
字段告訴 webpack
怎麼去搜索文件。
resolve.extensions
在導入語句沒帶文件後綴時,webpack
會自動帶上後綴後去嘗試詢問文件是否存在,查詢的順序是按照咱們配置 的 resolve.extensions
順序從前到後查找,webpack
默認支持的後綴是 js
與 json
。
舉個🌰:若是咱們配置 resolve.extensions= ['js', 'json']
,那麼 webpack
會先找 xxx.js
若是沒有則再查找 xxx.json
,因此咱們應該把經常使用到的文件後綴寫在前面,或者 咱們導入模塊時,儘可能帶上文件後綴名。
雖然
extensions
會優先查找數組內的值,可是咱們不要一古腦兒的把全部後綴都往裏面塞,這會調用屢次文件的查找,這樣就會減慢打包速度。
resolve.modules
這個屬性告訴 webpack
解析模塊時應該搜索的目錄,絕對路徑和相對路徑都能使用。使用絕對路徑以後,將只在給定目錄中搜索,從而減小模塊的搜索層級:
// config/webpack.common.js // ... const commonConfig = { // ... resolve: { extensions: ['.js', '.jsx'], mainFiles: ['index', 'list'], alias: { alias: path.resolve(__dirname, '../src/alias'), }, modules: [ path.resolve(__dirname, 'node_modules'), // 指定當前目錄下的 node_modules 優先查找 'node_modules', // 將默認寫法放在後面 ] }, // ... } // ...
resolve.alias
減小查找過程alias
的意思爲 別名,能把原導入路徑映射成一個新的導入路徑。
好比咱們項目中可能會有一些相對路徑的寫法,就可使用 alias
配置來減小查找過程;
還好比咱們常用的 react
庫,其實咱們能夠直接使用其 dist
目錄下打包好的 react.min.js
,這樣就能跳過耗時的模塊解析,具體示例配置以下:
// config/webpack.common.js // ... const commonConfig = { // ... resolve: { // ... alias: { react: path.resolve(__dirname, './node_modules/react/dist/react.min.js'), @alias: path.resolve(__dirname, '../src/alias'), }, }, // ... } // ...
這個筆者在實際項目中沒有嘗試過,不過也是一個思路,你們有機會能夠嘗試一波。
排除 Webpack
不須要解析的模塊,即便用 loader
的時候,在儘可能少的模塊中去使用。
咱們能夠藉助 include
和 exclude
這兩個參數,規定 loader
只在那些模塊應用和在哪些模塊不該用。
咱們修改公共配置文件 webpack.common.js
:
// config/webpack.common.js // ... const commonConfig = { // ... module: { rules: [ { test: /\.js|jsx$/, exclude: /node_modules/, include: path.resolve(__dirname, '../src'), use: ['babel-loader'] }, // ... ] }, } // ...
首先咱們不加 exclude
和 include
兩個參數,打包一下 npm run build
,打包時間 3350ms
左右:
接着咱們加上這兩個參數,意思分別是:
exclude: /node_modules/
:排除 node_modules
下面的文件include: path.resolve(__dirname, '../src')
:只對 src
下面的文件使用從新打包一下,打包時間變成了 1400ms
左右:
因爲運行在 Node.js
之上的 webpack
是單線程模型的,因此 webpack
須要處理的事情須要一件一件的作,不能多件事一塊兒作。
若是 webpack
能同一時間處理多個任務,發揮多核 CPU
電腦的威力,那麼對其打包速度的提高確定是有很大的做用的。
HappyPack
原理:每次 webapck
解析一個模塊,HappyPack
會將它及它的依賴分配給 worker
線程中。處理完成以後,再將處理好的資源返回給 HappyPack
的主進程,從而加快打包速度。
在webpack4.0
中使用happypack
須要使用其5.0
版本。
咱們將 HappyPack
引入公共配置文件,他的用法就是將相應的 loader
替換成 happypack/loader
,同時將替換的 loader
放入其插件的 loaders
選項,咱們暫且替換一下 babel-loader
:
// config/webpack.common.js // ... const makePlugins = (configs) => { const plugins = [ // ... new HappyPack({ loaders: ['babel-loader'] }), ]; // ... return plugins; } // ... const commonConfig = { entry: { main: "./src/index.js", entry2: "./src/entry2.js", entry3: "./src/entry3.js", entry4: "./src/entry4.js", entry5: "./src/entry5.js", entry6: "./src/entry6.js", }, // ... module: { rules: [{ test: /\.jsx?$/, // exclude: /node_modules/, // include: path.resolve(__dirname, '../src'), use: [ 'happypack/loader' // 'babel-loader' ] }] }, // ... } // ...
爲了讓效果更加明顯一點,咱們在項目下多增長几個入口文件,在不使用 happypack
的狀況下,進行一次打包,時間差很少是 8s
多:
開啓 happypack
以後,咱們能夠從控制檯中看到,happypack
默認幫咱們開啓了 3
個進程,打包時間變成了6.5s
左右:
注意:HappyPack
的做者如今基本上也不維護這個插件了,由於做者對此項目的興趣正在減弱。他也推薦咱們使用webpack
官方 thread-loader。
更多參數你們能夠參考 HappyPack 官網
thread-loader
webpack
官方推出的一個多進程方案,用來替代 HappyPack
。
原理和 HappyPack
相似,webpack
每次解析一個模塊,thread-loader
會將它及它的依賴分配給 worker
線程中,從而達到多進程打包的目的。
使用很簡單,直接在咱們使用的 loader
以前加上 thread-loader
就行,咱們須要先註釋掉 HappyPack
代碼:
// config/webpack.common.js // ... const commonConfig = { // ... module: { rules: [{ test: /\.jsx?$/, // exclude: /node_modules/, // include: path.resolve(__dirname, '../src'), use: [ { loader: 'thread-loader', options: { workers: 3, // 開啓幾個 worker 進程來處理打包,默認是 os.cpus().length - 1 } }, 'babel-loader' ] }] }, // ... } // ...
咱們從新運行一下,也是差很少 6.5s
左右:
咱們在打包的時候,通常來講第三方模塊是不會變化的,因此咱們想只要在第一次打包的時候去打包一下第三方模塊,並將第三方模塊打包到一個特定的文件中,當第二次 webpack
進行打包的時候,就不須要去 node_modules
中去引入第三方模塊,而是直接使用咱們第一次打包的第三方模塊的文件就行。
webpack.DllPlugin
就是來解決這個問題的插件,使用它能夠在第一次編譯打包後就生成一份不變的代碼供其餘模塊引用,這樣下一次構建的時候就能夠節省開發時編譯打包的時間。
咱們在配置文件目錄 config
下新建一個 webpack.dll.js
,此文件用於將咱們的第三方包文件打包到 dll
文件夾中去:
// config/webpack.dll.js const path = require('path'); const webpack = require('webpack'); module.exports = { mode: 'production', // 環境 entry: { vendors: ['lodash'], // 將 lodash 打包到 vendors.js 下 react: ['react', 'react-dom'], // 將 react 和 react-dom 打包到 react.js 下 }, output: { filename: '[name].dll.js', // 輸出的名字 path: path.resolve(__dirname, '../dll'), // 輸出的文件目錄 library: '[name]' // 將咱們打包出來的文件以所有變量的形式暴露,能夠在瀏覽器變量的名字進行訪問 }, plugins: [ // 對生成的庫文件進行分析,生成庫文件與業務文件的映射關係,將結果放在 mainfest.json 文件中 new webpack.DllPlugin({ name: '[name]', // 和上面的 library 輸出的名字要相同 path: path.resolve(__dirname, '../dll/[name].manifest.json'), }) ] }
library
的意思其實就是將 dll
文件以一個全局變量的形式導出出去,便於接下來引用,以下圖:mainfest.json
文件是一個映射關係,它的做用就是幫助 webpack
使用咱們以前打包好的 ***.dll.js
文件,而不是從新再去 node_modules
中去尋找。咱們在命令行中打包一下 dll
文件,能夠看到根目錄生成了一個 dll
文件夾,而且在下面生成了相應的文件,而且 loader
打包到了 vendor.dll.js
中,react
和 react-dom
打包到了 react.dll.js
中了:
接着咱們須要去修改公共配置文件 webpack.common.js
,將咱們以前生成的 dll
文件導入到 html
中去,若是咱們不想本身手動向 html
文件去添加 dll
文件的時候,咱們能夠藉助一個插件 add-asset-html-webpack-plugin
,此插件顧名思義,就是將一些文件加到 html
中去。
同時咱們須要使用 webpack
自帶的 DllReferencePlugin
插件對 mainfest.json
映射文件進行分析。
// config/webpack.common.js const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); // ... const commonConfig = { // ... plugins: [ // ... new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, '../dll/vendors.dll.js') }), new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, '../dll/react.dll.js') }), new webpack.DllReferencePlugin({ manifest: require(path.resolve(__dirname, '../dll/vendors.dll.mainfest.json')) }), new webpack.DllReferencePlugin({ manifest: require(path.resolve(__dirname, '../dll/react.dll.mainfest.json')) }), ], // ... } // ...
這裏的代碼還能夠優化,具體你們能夠參考筆者整理的筆記中 dll優化 這一節。
咱們進行一次打包,能夠看到打包耗時爲 1450ms
左右,同時能夠看到庫文件打包到的 vendors.chunk.js
爲 1.22MB
。
咱們註釋掉對 dll
的引用分析以後,從新打包,打包耗時爲 1950ms
左右,同時能夠看到 vendors.chunk.js
爲 5.28MB
。
咱們能夠開啓相應 loader
或者 plugin
的緩存,來提高二次構建的速度。通常咱們能夠經過下面幾項來完成:
babel-loader
開啓緩存terser-webpack-plugin
開啓緩存cache-loader
或者 hard-source-webpack-plugin 若是項目中有緩存的話,在 node_modules
下會有相應的 .cache
目錄來存放相應的緩存。
babel-loader
首先咱們開啓 babel-loader
的緩存,咱們修改 babel-loader
的參數,將參數 cacheDirectory
設置爲 true
:
// config/webpack.common.js // ... module: { rules: [ { test: /\.jsx?$/, // exclude: /node_modules/, // include: path.resolve(__dirname, '../src'), use: [ { loader: 'babel-loader', options: { cacheDirectory: true, } }, ] }, ] } // ...
首次打包時間爲 8.5s
左右,打包完成以後,咱們能夠發如今 node_modules
下生成了一個 .cache
目錄,裏面存放了 babel
的緩存文件:
咱們從新打包一次,會發現時間變成了 6s
左右:
TerserPlugin
咱們經過將 TerserPlugin
中的 cache
設爲 true
,就能夠開啓緩存:
// config/webpack.common.js const TerserPlugin = require('terser-webpack-plugin'); // ... const commonConfig = { // ... optimization: { minimize: true, minimizer: [ new TerserPlugin({ parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1 cache: true, }), ], }, // ... }
首次打包時間爲 8-9s
左右,同時在 .cache
目錄下生成了 terser-webpack-plugin
緩存目錄:
咱們從新打包一次,會發現時間變成了 5s
左右:
HardSourceWebpackPlugin
這個插件其實就是用於給模塊提供一箇中間的緩存。
使用以下,咱們直接在插件中引入就 ok 了:
// config/webpack.common.js const HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); // ... const plugins = [ // ... new HardSourceWebpackPlugin(), ]; // ...
咱們打包一下,能夠看到在第一次打包的時候 HardSourceWebpackPlugin
就幫咱們開始生成打包文件了,同時在 .cache
目錄生成了 hard-source
目錄,第一次打包耗時 6.6s
左右:
咱們從新打包一次,會發現時間變成了 2.7s
左右:
以前咱們有講過,以前咱們打包生成 sourceMap
的時候,若是信息越詳細,打包速度就會越慢
因此咱們要在代碼打包過程當中的時候,在對應的環境使用對應的 sourceMap
很重要。
除了上述咱們提到的一些經常使用方法,還有其餘的一些方法,好比:
ES6 Modules
語法,以保證 Tree-Shaking
起做用由於tree-shaking
只對ES6 Modules
靜態引入生效,對於相似於CommonJs
的動態引入方式是無效的
Ployfill
若是咱們對於引入的polyfill
不作處理的話,Webpack
會把全部的Polyfill
都加載進來,致使產出文件過大。推薦使用@babel/preset-env
的useBuiltIns='usage'
方案,此配置項會根據瀏覽器的兼容性幫助咱們按需引入所需的墊片;此外咱們也可使用動態polyfill
服務,每次根據瀏覽器的User Agent
,下發不一樣的Polyfill
,具體能夠參考polyfill.io
。
webpackPrefetch
使用
webpackPrefetch
來提早預加載一些資源,意思就是
未來可能須要一些模塊資源,在覈心代碼加載完成以後帶寬空閒的時候再去加載須要用到的模塊代碼。
icon
類圖片使用 css Sprite
來合併圖片
若是
icon
類圖片太多的話,就使用雪碧圖合成一張圖片,減小網絡請求,或者使用字體文件。
html-webpack-externals-plugin
此插件能夠將一些公用包提取出來使用cdn
引入,不打入bundle
中,從而減小打包文件大小,加快打包速度。
chunk
的哈希值在生產環境打包,必定要配置文件的hash
,這樣有助於瀏覽器緩存咱們的文件,當咱們的代碼文件沒變化的時候,用戶就只須要讀取瀏覽器緩存的文件便可。 通常來講javascript
文件使用[chunkhash]
、css
文件使用[contenthash]
、其餘資源(例如圖片、字體等)使用[hash]
。
更多性能優化方法筆者就再也不一一列舉了,由於關於 webpack
性能優化的方法實在是太多了,你們能夠根據實際遇到的問題制定相關的優化方案。
今天這篇文章介紹了 webpack
打包的一些優化方案,從項目體積再到對項目速度,咱們提出了一些優化方案,你們能夠在具體的項目中去進行實踐。
固然我還要提一嘴,若是你的項目自己構建就比較快,那麼你其實就不須要使用文章中提到的方法去對項目進行優化,可能效果會拔苗助長。
對於文章中有些一筆帶過的內容,你們能夠在個人 webpack 學習整理文檔 找到相應的介紹。
實不相瞞,想要個贊!
示例代碼能夠看這裏: