Webpack實戰(八):教你搞懂webpack若是實現代碼分片(code splitting)

2020年春節已過,原本打算回鄭州,卻由於新型冠狀病毒感染肺炎的疫情公司推遲了上班的時間,我也推遲了去鄭州的時間,在家多陪娃幾天。之前都是在書房學習寫博客,今天比較特殊,抱着電腦,在樓頂曬着太陽,陪着家人,寫着博客。javascript

前面的幾篇文章主要告訴你們如何安裝、配置webpack、webpack實現樣式分離等,今天這篇文章主要跟你們分享若是webpack如何實現代碼分片。css

如今工程項目中,實現高性能應用的其中重要的一點就是讓用戶每次只加載必要的資源,優先級別不過高的資源採用延遲加載等技術漸進地進行加載獲取。html

Webpack 做爲打包工具所特有的一項技術就是代碼分片技術,經過這項技術咱們能夠把代碼按照特定的形式進行拆分,使用按需加載資源,沒必要要所有加載下來。 代碼分片能夠有效下降首屏加載資源的大小,可是咱們同時又面臨着其餘問題,好比如何對項目模塊進行分片,分片後的資源如何進行管理等等。今天咱們須要對這些問題進行分析解決。java

利用入口劃分代碼

在Webpack中,配置參數中每一個入口都將生成一個對應的資源文件,經過入口的配置咱們能夠進行一些簡單有效的代碼拆分。node

對於項目中經常會引入一些第三方庫和工具,這些通常不會改動的,能夠把它們單獨放在一個入口中,由該入口的資源不會常常更新,所以能夠有效利用客戶端緩存這些資源,讓用戶沒必要在每次請求頁面時候都從新加載。react

//webpack.config.js
entry: {
	index: './index.js',
	lib: ['lib-1', 'lib-2']
}

//index.html
<script src="dist/lib.js"></script>
<script src="dist/index.js"></script>
複製代碼

這種拆分方法主要適合於那些將接口綁定在全局對象上的庫,由於業務代碼中的模塊沒法直接引用庫中的模塊,兩者屬於不一樣的依賴樹。webpack

對於多頁面應用來講,咱們能夠利用入口劃分的方式拆分代碼。好比,爲每個頁面建立一個入口,並放入只涉及該頁面的代碼,同時再建立一個入口來包含全部公共模塊,並使每一個頁面都進行加載。可是這樣仍會帶來公共模塊與業務模塊處於不一樣依賴樹的問題。另外,不少時候不是全部的頁面都須要這些公共模塊。這就須要咱們利用webpack專用的插件來解決這種問題了。web

CommonsChunkPlugin

CommonsChunkPlugin是webpack4以前內部自帶的插件,webpack4以後用的是SplitChunks。CommonsChunkPlugin主要是用來提取第三方庫和公共模塊,避免首屏加載的bundle文件或者按需加載的bundle文件體積過大,從而致使加載時間過長,是一把優化項目的利器。瀏覽器

優勢:緩存

  • 開發過程當中減小了重複模塊打包,能夠提高開發速度;
  • 減少總體資源體積;
  • 合理分片後的代碼能夠更有效地利用客戶端緩存。 首頁經過一個簡單的例子也說明,假設咱們當前項目中有a.js 和b.js 兩個入口文件,而且都引入了react,下面是未使用CommonsChunkPlugin的配置
//webpack.config.js
module.exports = {
	entry: {
		a: './a.js',
		b: './b.js'
	},
	output: {
		filename: '[name].js'
	}
}

//a.js
import React from 'React'
... //省略

// b.js 
import React from 'React'
... //省略
複製代碼

若是打包,從打包的資源體積能夠看出,react被分別打包到a.js 和b.js中。

更改webpack.config.js,添加CommonsChunkPlugin配置

const webpack = require('webpack');
module.exports = {
	entry: {
		a: './a.js',
		b: './b.js'
	},
	output: {
		filename: '[name].js'
	},
	plugins: [
	new webpack.optimize.CommonsChunkPlugin({
	name: 'commons',
	filename: 'commons.js'
})
]
}

複製代碼

在配置文件的頭部引入Webpack,接着使用其內部CommonsChunkPlugin函數建立了一個插件實例,並傳入配置對象,配置參數能夠理解爲

  • name:用於指定公共chunk的名字
  • filename: 提取後的資源文件名 打包後能夠看到,產出的資源中多了一個commons.js,而a.js 和b.js文件的體積也減小了,這是因爲把react及依賴的模塊提到commons.js的緣由。 不過咱們須要注意的是,咱們須要在頁面引入其餘j s以前,先引入公用的commons.js文件。

在提取公共模塊方面,CommonsChunkPlugin能夠知足不少場景的需求,可是它也有一些欠缺的地方。 1)一個CommonsChunkPlugin只能提取一個vendor,假如咱們想提取多個vendor則須要配置多個插件,這會增長不少重複的配置代碼。

2)前面咱們提到的manifest實際上會使瀏覽器多加載一個資源,這對於頁面渲染速度是不友好的。

3)因爲內部設計上的一些缺陷,CommonsChunkPlugin在提取公共模塊的時候會破壞掉原有Chunk中模塊的依賴關係,致使難以進行更多的優化。好比在異步Chunk的場景下CommonsChunkPlugin並不會按照咱們的預期正常工做。

optimization.SplitChunks

optimization.SplitChunks(簡稱SplitChunks)是Webpack 4爲了改進CommonsChunk-Plugin而從新設計和實現的代碼分片特性。它不只比CommonsChunkPlugin功能更增強大,還更簡單易用。

配置文件web pack.config.js爲:

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  context: path.join(__dirname, './src'),
  entry: {
    index: './index.js'
  },
  output: {
    // path: path.join(__dirname, 'dist'),
    filename: 'index.js',
    publicPath: '/dist/'
  },
  mode: 'development',
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path][name]__[local]--[hash:base64:5]',
            }
          }
        }]
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: [
              [
                'env', {
                  modules: false
                }
              ]
            ]
          }
        }
      }
    ],
  }
}
複製代碼

須要引入的兩個index.js文件和index2.js文件

// index
import index2 from  './index2.js';
import React from 'react'
document.write('index.js', React.version);

//index2
import React from 'react'
document.write('index2.js', React.version);


複製代碼

使用optimization.splitChunks替代了CommonsChunkPlugin,並指定了chunks的值爲all,這個配置項的含義是,SplitChunks將會對全部的chunks生效(默認狀況下,SplitChunks只對異步chunks生效,而且不須要配置) 打包結果以下圖:

在這裏插入圖片描述

本來咱們打包的結果應該是index.js,可是因爲SplitChunks的存在,又生成了一個vendors~index.index.js,而且把react提取到了裏面。 運行的效果以下圖:

在這裏插入圖片描述
在使用CommonsChunkPlugin的時候,咱們大多數時候是經過配置項將特定入口中的特定模塊提取出來,也就是更貼近命令式的方式。而SplitChunks的不一樣之處在於咱們只須要設置一些提取條件,如提取的模式、提取模塊的體積等,當某些模塊達到這些條件後就會自動被提取出來。SplitChunks的使用更像是聲明式的。

SplitChunks默認情形下的提取條件:

  • 提取後的chunk可被共享或者來自node_modules目錄。這一條很容易理解,被屢次引用或處於node_modules中的模塊更傾向因而通用模塊,比較適合被提取出來。
  • 提取後的Javascript chunk體積大於30kB(壓縮和gzip以前),CSS chunk體積大於50kB。這個也比較容易理解,若是提取後的資源體積過小,那麼帶來的優化效果也比較通常。
  • 在按需加載過程當中,並行請求的資源最大值小於等於5。按需加載指的是,經過動態插入script標籤的方式加載腳本。咱們通常不但願同時加載過多的資源,由於每個請求都要花費創建連接和釋放連接的成本,所以提取的規則只在並行請求很少的時候生效。
  • 在首次加載時,並行請求的資源數最大值小於等於3。和上一條相似,只不過在頁面首次加載時每每對性能的要求更高,所以這裏的默認閾值也更低。

SplitChunks提取方式

SplitChunks 默認的提取方式是異步提取,當咱們在chunks上配置參數爲all的時候,不是異步資源也能夠提取。

SplitChunks 配置

optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      minRemainingSize: 0,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: '~',
      name: true,
      automaticNameMaxLength: 30,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
複製代碼

(1)匹配模式經過chunks咱們能夠配置SplitChunks的工做模式。它有3個可選值,分別爲async(默認)、initial和all。async即只提取異步chunk,initial則只對入口chunk生效(若是配置了initial則上面異步的例子將失效),all則是兩種模式同時開啓。

(2)匹配條件minSize、minChunks、maxAsyncRequests、maxInitialRequests都屬於匹配條件,

(3)命名配置項name默認爲true,它意味着SplitChunks能夠根據cacheGroups和做用範圍自動爲新生成的chunk命名,並以automaticNameDelimiter分隔。如vendors~a~b~c.js意思是cacheGroups爲vendors,而且該chunk是由a、b、c三個入口chunk所產生的。

(4)cacheGroups能夠理解成分離chunks時的規則。默認狀況下有兩種規則——defaultVendors和default。defaultVendors用於提取全部node_modules中符合條件的模塊,default則做用於被屢次引用的模塊。咱們能夠對這些規則進行增長或者修改,若是想要禁用某種規則,也能夠直接將其置爲false。當一個模塊同時符合多個cacheGroups時,則根據其中的priority配置項肯定優先級。

總結

有關webpack實現代碼分片的幾種方法:合理地規劃入口,使用Commons-ChunkPlugin或SplitChunks就暫時分享到這裏,這僅表明我的的觀點,如想了解更多請掃描二維碼

在這裏插入圖片描述
最後友情提醒你們要戴口罩,勤洗手,儘可能減小外出,作好預防 措施,遠離新型冠狀病毒。
相關文章
相關標籤/搜索