webpack 是一個優秀的打包工具,其自己爲咱們作了大量優化,同時也爲咱們提供了大量的配置項讓咱們能夠自定義,從而有優化空間。javascript
在講 webpack 優化篇以前,因爲樓主主要以 vue 腳手架開始的,並且是已經升級爲 webpack4 以後的優化,若是對 vue腳手架配置不太瞭解的同窗。能夠看我上一篇文章 如何優雅的升級到webpack4,或者直接看 webpack3 vue腳手架註解
下面我先講講vue腳手架爲咱們作的一些優化,不喜歡看的請跳過,而後會講如何在優化的基礎上升華一下,內容從淺到深,可是全部的方法都通過樓主考證,內容較長,請自帶板凳瓜子。css
Babel 是一個 JavaScript 編譯器,能將 ES6 代碼轉爲 ES5 代碼,讓你使用最新的語言特性而不用擔憂兼容性問題,而且能夠經過插件機制根據需求靈活的擴展。這裏我不講babel ,而是講官方用的插件 transform-runtime,對應的插件全名叫作 babel-plugin-transform-runtime,其做用是減小冗餘代碼,究竟是怎麼減小的呢?html
例如在轉換 class extent 語法時會在轉換後的 ES5 代碼裏注入 _extent 輔助函數用於實現繼承:前端
function _extent(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }
這會致使每一個使用了 class extent 語法的文件都被注入重複的_extent 輔助函數代碼,babel-plugin-transform-runtime 的做用在於不把輔助函數內容注入到文件裏,而是注入一條導入語句:vue
var _extent = require('babel-runtime/helpers/_extent');
這樣能減少 Babel 編譯出來的代碼的文件大小。
注意:babel-plugin-transform-runtime 必須和 babel-runtime 須要配套使用java
說來慚愧,樓主試了一下,把這個插件去掉,生成文件的hash和大小並無變化(汗,別砸,翻資料webpack 標準入門、前端工程化-webpack篇之babel-polyfill與babel-runtime(三)上有寫,並且腳手架上有)。後來發現,樓主的代碼中並無es6。後來換了一個大項目,作了對比
能夠發現,圖右邊是去掉插件的。體積明顯大了一點。使用此插件能夠減小重複代碼,縮小項目體積。node
使用 Loader 時能夠經過 test 、 include 、 exclude 三個配置項來命中,對於咱們的項目大部分都是 js,下面看看官方腳手架 js 的 babel-loader:react
module.exports = { // ... module: { rules: [ // ... { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] }, ] } }
因爲經過 npm 安裝的第三方的庫,都是通過 webpack 打包 es5 化了,因此這裏就能夠只對 include 包括的文件使用 babel-loader 解析webpack
注意了。因爲 css、less 的引入是要插入到js中的,因此並不適用於這個(把 node_modules 排除在外)方法。說到這裏,多說一句,也是曾經很困擾個人 css 的 loader 解析順序,use 的 loader 解析順序跟數組的位置是反着的,以 less 爲例,具體來說git
module.exports = { // ... module: { rules: [ // ... { test: /\.less$/, // less 文件的處理順序爲先 less-loader 再 css-loader 再 vue-style-loader use: [ // style-loader 會把 CSS 代碼轉換成字符串後,注入到 JavaScript 代碼中去, 'vue-style-loader', // css-loader 會找出 CSS 代碼中的 @import 和 url() 這樣的導入語句,告訴 Webpack 依賴這些資源。同時還支持 CSS Modules、壓縮 CSS 等功能。處理完後再把結果交給 vue-style-loader 去處理。 { loader: 'css-loader', options: { sourceMap: config.dev.cssSourceMap } }, //經過 less-loader 把 less 源碼轉換爲 CSS 代碼,再把 CSS 代碼交給 css-loader 去處理。 { loader: 'less-loader' } ] }, ] } }
關於縮小範圍增長命中這個思想,還能夠作不少事情,這裏只講了vue腳手架優化作的事情,更多配置請日後看,看我如何自定義的
webpack 的官方腳手架裏面的node選項能夠防止node包,還有 setImmediate 的 profill注入到代碼中
node: { // prevent webpack from injecting useless setImmediate polyfill because Vue // source contains it (although only uses it if it's native). setImmediate: false, // prevent webpack from injecting mocks to Node native modules // that does not make sense for the client dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', child_process: 'empty' }
好很差,看療效。那麼具體的療效怎麼樣呢,樓主一樣的代碼,作了對比,效果以下:
經過對比能夠看到,兩次打包css的hash值所有變了,js部分hash發生改變(這個打包沒看出js變化,可是另外一個項目的部分js的hash變了)。整體打出來的包的體積相差不大。去掉node選項打包時間差異不明顯,因此用不用,見仁見智吧。我看create-react-app中也使用了,因此仍是建議使用吧,知道更多的,能夠留言區討論。
css 壓縮這個就很少說了,你們都懂,
值得一提的是因爲 UglifyJsPlugin 插件升級到1.0以後有了 parallel選項,開啓了多線程壓縮
new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false } }, sourceMap: config.build.productionSourceMap, parallel: true // 開啓多線程壓縮 })
這兩個插件都有配置項,合理配置能夠優化項目。後面會講。
代碼分割就是將動態引入的代碼分割成一個一個的代碼塊(chunk),根據需求加載到html上。注意:要使用代碼分割功能,在項目中要配合使用組件、路由懶加載的方式(能夠經過import實現)
webpack4 的 mode 爲 production 時,默認會對代碼進行分割。樓主看了 webpack3 的代碼分割方式是使用 CommonsChunkPlugin 插件,目的就是分割出幾類代碼:
嗯 webpack 生成 js 的 hash 是如何計算我並不清楚,可是若是不用這個插件的話,全部生成 js 的 hash 是同樣的,並且只要有一點點改動,全部文件的 hash 值都會變化。那形成什麼樣的結果呢?
好比你只改了 b 頁面的 js 裏的一行代碼,若是不用此插件的話,全部頁面的 js 的 hash 所有會變化,瀏覽器要從新請求所有的js。性能浪費到使人髮指。而使用了 HashedModuleIdsPlugin 這個插件,只有你改動的那個 chunk 的 hash會發生變化,其餘不變,因爲瀏覽器的緩存機制,瀏覽器只從新請求改動的js。是否是很棒。並且上一小節對代碼分割那裏的分割方式,也是爲了把不常常變更的文件單獨打包,hash 能夠保持不變。
使用方法也很簡單
new webpack.HashedModuleIdsPlugin(),
什麼?爲何就算去掉 HashedModuleIdsPlugin 插件 用腳手架第一次打包項目生成的 js 的 hash 不所有同樣,並且改動以後,也不是所有發生變化啊。這個也是樓主遇到的問題。樓主不用腳手架搭建的項目,js 的 hash 是同樣的,知道爲何出現初始打包的 js hash 值爲何不所有同樣的同窗,歡迎評論區討論。
過去 webpack 打包時的一個取捨是將 bundle 中各個模塊單獨打包成閉包。這些打包函數使你的 JavaScript 在瀏覽器中處理的更慢。相比之下,一些工具像 Closure Compiler 和 RollupJS 能夠提高(hoist)或者預編譯全部模塊到一個閉包中,提高你的代碼在瀏覽器中的執行速度。
個插件會在 webpack 中實現以上的預編譯功能。
new webpack.optimize.ModuleConcatenationPlugin()
這種連結行爲被稱爲「做用域提高(scope hoisting)
記住,此插件僅適用於由 webpack 直接處理的 ES6 模塊。在使用轉譯器(transpiler)時,你須要禁用對模塊的處理(例如 Babel 中的 modules 選項)。
因爲css加載不會阻塞dom的解析,因此把css抽取出來。不佔用js的大小是一個明智的選擇 OptimizeCSSPlugin 插件作的就是這個,而且代碼複用,會減少css體積
new OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: true, map: { inline: false } } : { safe: true } }),
整體來說 webpack 爲咱們作的優化有
大體就這樣了,有沒有講到的也請評論區提出,那麼如何在此基礎上作優化呢,這個也許是你們都很關心的問題。接下來我會在 《關於webpack優化,你須要知道的事(下篇)》講到。