淺談webpack4.0 性能優化

前言:在現實項目中,咱們可能不多須要從頭開始去配置一個webpack 項目,特別是webpack4.0發佈之後,零配置啓動一個項目成爲一種標配。正由於零配置的webpack對項目自己提供的「打包」和「壓縮」功能已經作了優化,因此實際應用中,咱們能夠把精力更多專一在業務層面上,而無需分心於項目構建上的優化。然而從學習者的角度,咱們須要瞭解webpack在項目的構建和打包壓縮過程當中作了哪些的優化,以及在原有默認配置上,還能夠作哪些性能方面上的改進。
       最近在完成vue的單頁面應用後萌生了一個想法,拋棄掉vue-cli的構建配置,從零開始進行webpack優化,並將過程當中的思路和體會分享在這篇文章中。webpack的初始配置在我以前寫的另外一篇手把手教你從零認識webpack4.0文章中,如下內容也不對基本的webpack配置作過多闡述。css

一,優化的方向

1.1 項目開發

       對開發者而言,咱們但願webpack這個工具能夠給咱們帶來流暢的開發體驗。好比,當不斷修改代碼時,咱們但願代碼的變動能及時的通知瀏覽器刷新頁面,而不是手動去刷新頁面。更進一步的咱們但願,代碼的修改只會局部更換某個模塊,而不是整個頁面的刷新。這樣可使咱們不須要在等待刷新中浪費不少時間,大大提升了頁面的開發效率。html

1.2 項目部署

       項目部署上線時,性能優化是咱們考慮的重點,有兩個方向能夠做爲核心考慮的點,一個是減小HTTP請求,咱們知道在網速相同的條件下,下載一個100KB的圖片比下載兩個50KB的圖片要快,所以,咱們要求webpack將多個文件打包成一個或者少許個文件;另外一個優化的重點是減小單次請求的時間,也就是儘量減小請求文件的體積大小。        webpack中在性能優化所作的努力,也大抵圍繞着這兩個大方向展開。另外在構建項目中,咱們也但願能持續的提升構建效率。前端

二, 提高開發效率

2.1 減小體積

開發環境下,咱們依然對代碼的體積有必定的要求,更小的體積可讓加載速度更快,開發效率更高,固然配置也相對簡單。vue

// webpack.dev.js 開發環境webpack配置
module.exports = {
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        port: 9000,
        compress: true, // 代碼壓縮
      },
}
複製代碼

2.2 模塊熱更新(HMR)

開發過程當中,咱們但願修改代碼的過程當中,頁面能實時且不須要手動的刷新。所以使用HRM, HMR 既避免了頻繁手動刷新頁面,也減小了頁面刷新時的等待,大幅度提升了開發效率。node

// webpack.dev.js
module.exports = {
  devServer: {
    compress: true,
    hot: true // 開啓配置
  },
  plugins: [
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin(),
  ],
}
複製代碼

三,構建體積優化

3.1 生產中的sourcemap 模式

webpack 在構建中提供了很多於7種的sourcemap模式,其中eval模式雖然能夠提升構建效率,可是構建後的腳本較大,所以生產上並不適用。而source-map 模式能夠經過生成的 .map 文件來追蹤腳本文件的 具體位置,進而縮小腳本文件的體積,這是生產模式的首選,而且在生產中,咱們須要隱藏具體的腳本信息,所以可使用 cheap 和module 模式來達到目的。 綜上,在生產的webpack devtool選項中,咱們使用 cheap-module-source-map的配置react

// webpack.pro.js 生產webpack配置腳本
module.exports = {
  mode: 'production',
  devtool: 'cheap-module-source-map',  
}

複製代碼

3.2 獨立css 文件

以單入口文件而論,一般咱們會將頁面的全部靜態資源都打包成一個JS 文件,這已經實現了1.2 中的優化部分,將代碼合併成一個靜態資源,減小了HTTP 請求。jquery

分離前

在這裏插入圖片描述
可是接下來,咱們須要將css代碼獨立開來,爲何呢?最主要的一點是咱們但願更好的利用瀏覽器的緩存,當單獨修改了樣式時,獨立的css文件能夠不須要應用去加載整個的腳本文件,提升效率。而且,當遇到多頁面的應用時,能夠單獨將一些公共部分的樣式抽離開來,加載一個頁面後,接下來的頁面一樣能夠利用緩存來減小請求。

webpack4.0 中提供了抽離css文件的插件,mini-css-extract-plugin,只須要簡單的配置即可以將css文件分離開來webpack

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    ···
    plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].[contenthash].css",
            chunkFilename: "[name].[contenthash].css"
        })
    ],
    module: {
        rules: {
            test: /\.(css|scss)$/,
            use: [process.env.NODE_ENV == 'production' ? MiniCssExtractPlugin.loader : 'style-loader', {
              loader: 'css-loader',
              options: {
                sourceMap: true
              },
            }, "sass-loader"]
        }
    }
    ···
}
複製代碼
分離後

在這裏插入圖片描述

3.3 壓縮js, html, css 文件

要想優化構建後的體積,不斷減小靜態資源文件的大小,咱們但願webpack幫助咱們儘量壓縮文件的體積。對於js 腳本文件而言,webpack4.0 在mode 爲‘production’時,默認會啓動代碼的壓縮。除此以外,咱們須要手動對html和css 進行壓縮。
       針對html 的壓縮,只須要對html-webpack-plugin進行相關配置。git

// webpack.base.js 

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
          title: 'minHTML',
          filename: 'index.html',
          template: path.resolve(__dirname, '../index.html'),
          minify: { // 壓縮 HTML 的配置
            collapseWhitespace: true,
            removeComments: true,
            useShortDoctype: true
          }
        }),
    ]
}

複製代碼

       針對css 的壓縮, webpack4.0 使用optimize-css-assets-webpack-plugin來壓縮單獨的css 文件。github

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
    plugins: [
        new OptimizeCSSAssetsPlugin()
    ],
}
複製代碼

在這裏插入圖片描述
在這裏插入圖片描述
對比之下,咱們能夠看到明顯的效果,關於壓縮css 更多的配置能夠參考 optimize-css-assets-webpack-plugin

3.4. 合併壓縮圖片

處理完前端的三大塊js,html,css後, 接下來優化能想到的是處理圖片。前面提到,提高性能的一個重要的條件是下降http請求數,而應用中常常會有大大小小的圖片須要處理,對應用中的小圖標來講,css sprite 是首選,將各類圖標集合成一張大的圖片能夠很好的減小網絡請求數。而對於須要獨立開的圖片,且大小在合理範圍內時,咱們能夠將圖片轉換成 base64位編碼,內嵌到css 中,一樣能夠減小請求。

3.4.1 base64 轉換

處理圖片資源時,webpack 提供了 file-loader 和url-loader 兩個loaders供選擇,file-loader 和url-loader 的做用,能夠用來解析項目中圖片文件的url引入問題。二者的區別在於,url-loader 能夠將小於指定字節的文件轉爲DataURL, 大於指定字節 的依舊會使用file-loader 進行解析

// webpack.base.js
module.exports = {
    module: {
        rules: [{
            test: /\.(png|jpe?g|gif|svg|ttf|woff2|woff)(\?.*)?$/,
            use: [{
              loader: 'url-loader',
              options: {
                limit: 10000, // 限制大小
              }
            }, 
        ]
  },
}
複製代碼
3.4.2 壓縮圖片

處理完雪碧圖和小圖片的base64轉換後,對於大圖片來講,webpack還能夠作到對圖片進行壓縮,推薦使用image-webpack-loader,插件提供了多種形式的壓縮,詳細能夠參考官網文檔

// webpack.base.js
module.exports = {
    module: {
        rules: [
            {
              loader: 'image-webpack-loader',
              options: {
                optipng: { // 使用 imagemin-optipng 壓縮 png,enable: false 爲關閉
                  enabled: true,
                },
                pngquant: { // 使用 imagemin-pngquant 壓縮 png
                  quality: '65-90',
                  speed: 4
                },
              }
            }
        ]
    }
}
複製代碼

效果對好比下:

在這裏插入圖片描述
在這裏插入圖片描述

3.5 依賴庫分離

一箇中大型應用中,第三方的依賴,龐大得可怕,佔據了打包後文件的一半以上。然而,這些依賴模塊又是不多變動的資源,和css 代碼分離的邏輯類似,分離第三方依賴庫,能夠更好的利用瀏覽器緩存,提高應用性能。所以,將依賴模塊從業務代碼中分離是性能優化重要的一環。 webpack4.0 中,依賴庫的分離只須要經過 optimization.splitChunks 進行配置便可。

// webpack.pro.js
module.exports = {
    optimization: {
       splitChunks: {
          cacheGroups: {
            vendor: {
              chunks: "initial",
              test: path.resolve(__dirname, "../node_modules"),
              name: "vendor", // 使用 vendor 入口做爲公共部分
              enforce: true,
            },
          },
        },
      },
}

複製代碼

公共庫分離後的結果

在這裏插入圖片描述

3.6 依賴分析

正如前面所講,在優化分析中,實際影響體積最大的是 node_modules 的第三方庫,這一部分的優化能夠大大的減小打包後的體積。這裏咱們使用最新的webpack-bundle-analyzer插件來分析打包好後的模塊,它能夠將打包後的內容束展現位方便交互的直觀樹狀圖,經過它,能夠知道項目大體有哪些模塊組成,哪一個模塊佔據的體積較大,是不是可替代的。

咱們大體能夠從幾個方向考慮

  • 1.判斷依賴是否不可或缺,依賴在項目中使用率是否太低。在項目中,可能針對某個運算,某個功能,咱們使用了一個龐大的庫,這個庫在體積上的佔比較大,而功能使用卻較少。這個時候咱們能夠尋找體積更小,且功能知足的替換庫,或者手動實現某些依賴的功能來替換他。
  • 2.大型庫是否能夠經過定製功能的方式減小體積。明顯的一個例子是 echart, echart徹底版的依賴壓縮後也有幾百k 之多,這顯示是難以接受的。現實項目中,咱們可能只須要少許或者部分的echart 功能,這時咱們能夠經過制定圖表的形式,下載圖表用到的功能,達到體積最優化。
  • 3.某些不可優化的大型庫是否能夠經過外部引用的方式減小文件體積。例如像bootstrap,vue這類沒法優化的第三方庫,經過免費開源的cdn服務不但能夠減小文件體積,還能夠提升網站的加載速度,也是個優化性能的方法

3.7 按需加載

前面提到依賴分析的方向中,若是大型庫不可或缺,並且使用率也不算低的時候,咱們能夠經過按需加載的形式。這種方式其實是先把你的代碼在一些邏輯斷點處分離開,而後在一些代碼塊中完成某些操做後,當即引用或即將引用另一些新的代碼塊。這樣加快了應用的初始加載速度,減輕了它的整體體積,由於某些代碼塊可能永遠不會被加載。

webpack中利用require.ensure()實現按需加載,在不使用按需加載的狀況下,首屏加載時會把全部的腳本同時加載出來,這每每會拖累首屏顯示時間,帶來很差的用戶體驗。例子來講。當項目須要使用大型的圖表類庫,而首頁並不須要時,按需加載每每比同時加載在用戶體驗上好好得多。

當不須要按需加載的時候,咱們的代碼多是這樣的:

import test from './components/test.vue'
import test2 from './components/test2.vue'
複製代碼

開啓按需加載時,咱們的代碼修改成:

const test = r => require.ensure([], () => r(require('./components/test.vue')), 'chunk1')
const test2 = r => require.ensure([], () => r(require('./components/test2.vue')), 'chunk2')
複製代碼

webpack 配置修改成

output: {
    ···
    chunkFilename: '[name].[hash].js'
}
複製代碼

這時編譯出來的文件會從原來的一個,變成了多個小文件。每一個路由加載時會去加載不一樣的資源。特別在首屏的資源加載上進一步優化了應用的體驗。

儘管如此,實際中咱們須要根據項目的需求來衡量按需加載的可用性,儘管在首屏優化上取得較大的提高,但按需加載畢竟會將大的文件拆分紅多個小文件,增長了http 的請求數。這又違背了性能優化的基礎。因此實際中須要取捨,更須要權衡。

3.8 刪除冗餘代碼

代碼體積優化到這一步,基本能夠優化的地方已經優化完畢了。接下來能夠抓住一些細節作更細的優化。好比能夠刪除項目中上下文都未被引用的代碼。這就是所謂的 Tree shaking 優化。webpack 4.0中,mode 爲production 默認啓動這一優化。可是,若是在項目中使用到babel的 話,須要把babel解析語法的功能關掉。只須要

// .babelrc

{
  "presets": [["env", { "modules": false }]]
}
複製代碼

四,構建速度優化

說完如何減小項目構建後的大小後,接下來簡單的談一下如何提升構建的速度。實際上webpack的 構建速度,只須要簡單的修改配置便能大幅提升速度。常見的設置以下。

4.1 babel-loader構建時間過長

4.1.1 限定加載器做用範圍

因爲babel-loader須要將語法進行轉換,所耗費的時間較長,因此第一步須要限定babel-loader 做用的範圍,讓babel-loader 的搜索和轉換準確的定位到指定模塊。大幅提升構建速度。 例如:

// webpack.base.js
module.exports = {
    module:{
        rules: [
            {
                test: /\.js$/,
                include: [resolve('src')],// 限定範圍
                use: {
                  loader: 'babel-loader',
                },
            },]
    }
}
複製代碼
4.1.2 緩存加載器執行結果

正由於babel-loader在解析轉換上耗時太長,因此咱們但願能緩存每次執行的結果。webpack的loader中恰好有 cacheDirectory 的選項,默認爲false 開啓後將使用緩存的執行結果,打包速度明顯提高。

// webpack.base.js
module.exports = {
    module: {
        rules: [
            {
            test: /\.js$/,
            include: [resolve('src')],
            use: {
              loader: 'babel-loader?cacheDirectory',
            },
        },]
    }
}
複製代碼

4.2 resolve 解析優化

webpack 的resolve 作相關的配置後,也可讓項目的構建速度加快。具體看下文的配置:

  • 當項目中出現 import 'react' 既不是絕對路徑也不是相對路徑時,指定好搜索的路徑,能夠不用過多的查詢
  • 儘量少的使用 resolve.alias 來設置路徑別名,由於會影響到tree shaking 的優化
  • 後綴自動補全儘量的少。減小路徑查詢的工做
  • 當使用的第三方庫過大,而且不包含import require define 的調用。可使用noParse讓庫不被loaders 解析
// webpack.base.js
module.exports = {
    resolve: {
      modules: [
        path.resolve(__dirname, 'node_modules'),
      ],

      extensions: [".js"], 
    
      // 避免新增默認文件,編碼時使用詳細的文件路徑,代碼會更容易解讀,也有益於提升構建速度
      mainFiles: ['index'],
    },
    module: {
        noParse: function(content){
            return /jquery/.test(content)
        }
    }
}

複製代碼

五,結語

webpack性能優化的瓶頸仍是集中在構建時間和構建體積上,做爲構建工具業界霸主,webpack一直不停的優化構建打包流程。經過對舊有項目的優化也能夠對webpack這個工具以及性能優化的知識有更深的瞭解。

轉載請註明出處

相關文章
相關標籤/搜索