關於webpack優化,你須要知道的事(上篇)

前言

webpack 是一個優秀的打包工具,其自己爲咱們作了大量優化,同時也爲咱們提供了大量的配置項讓咱們能夠自定義,從而有優化空間。javascript

在講 webpack 優化篇以前,因爲樓主主要以 vue 腳手架開始的,並且是已經升級爲 webpack4 以後的優化,若是對 vue腳手架配置不太瞭解的同窗。能夠看我上一篇文章 如何優雅的升級到webpack4,或者直接看 webpack3 vue腳手架註解

下面我先講講vue腳手架爲咱們作的一些優化,不喜歡看的請跳過,而後會講如何在優化的基礎上升華一下,內容從淺到深,可是全部的方法都通過樓主考證,內容較長,請自帶板凳瓜子。css

vue-cli 腳手架自帶優化

babel

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。後來換了一個大項目,作了對比
babel對比
能夠發現,圖右邊是去掉插件的。體積明顯大了一點。使用此插件能夠減小重複代碼,縮小項目體積。node

縮小文件搜索範圍

loader

使用 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腳手架優化作的事情,更多配置請日後看,看我如何自定義的

node 選項

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'
}

好很差,看療效。那麼具體的療效怎麼樣呢,樓主一樣的代碼,作了對比,效果以下:
node選項對比

經過對比能夠看到,兩次打包css的hash值所有變了,js部分hash發生改變(這個打包沒看出js變化,可是另外一個項目的部分js的hash變了)。整體打出來的包的體積相差不大。去掉node選項打包時間差異不明顯,因此用不用,見仁見智吧。我看create-react-app中也使用了,因此仍是建議使用吧,知道更多的,能夠留言區討論。

js、css 壓縮

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 插件,目的就是分割出幾類代碼:

  1. vendor 也就是第三方庫打包這裏。
  2. manifest 當編譯器開始執行、解析和映射應用程序時,它會保留全部模塊的詳細要點。這個數據集合稱爲 "Manifest"
  3. app 這個是代碼中的公共部分

HashedModuleIdsPlugin

嗯 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 值爲何不所有同樣的同窗,歡迎評論區討論。

做用域提高(scope hoisting)

過去 webpack 打包時的一個取捨是將 bundle 中各個模塊單獨打包成閉包。這些打包函數使你的 JavaScript 在瀏覽器中處理的更慢。相比之下,一些工具像 Closure Compiler 和 RollupJS 能夠提高(hoist)或者預編譯全部模塊到一個閉包中,提高你的代碼在瀏覽器中的執行速度。
個插件會在 webpack 中實現以上的預編譯功能。

new webpack.optimize.ModuleConcatenationPlugin()

這種連結行爲被稱爲「做用域提高(scope hoisting)

記住,此插件僅適用於由 webpack 直接處理的 ES6 模塊。在使用轉譯器(transpiler)時,你須要禁用對模塊的處理(例如 Babel 中的 modules 選項)。

css 優化

因爲css加載不會阻塞dom的解析,因此把css抽取出來。不佔用js的大小是一個明智的選擇 OptimizeCSSPlugin 插件作的就是這個,而且代碼複用,會減少css體積

new OptimizeCSSPlugin({
  cssProcessorOptions: config.build.productionSourceMap
    ? { safe: true, map: { inline: false } }
    : { safe: true }
}),

總結

整體來說 webpack 爲咱們作的優化有

  1. babel-plugin-transform-runtime 插件去除重複墊片代碼
  2. module.rules 的 js 解析,使用 include 提升命中
  3. node 選項,防止 node 的自帶包(dgram、fs、net、tls、child_process)注入到咱們的代碼中
  4. js、css 壓縮,代碼分割,公共部分抽離
  5. 維持打包後不變chunk的hash值不變
  6. 做用域提高(scope hoisting)
  7. css 抽離。公共部分抽離

大體就這樣了,有沒有講到的也請評論區提出,那麼如何在此基礎上作優化呢,這個也許是你們都很關心的問題。接下來我會在 《關於webpack優化,你須要知道的事(下篇)》講到。

相關文章
相關標籤/搜索