vuecli3項目中webpack4配置(三)代碼分割

爲了不一次加載過大的文件和充分利用瀏覽器緩存,咱們須要將不常常被修改的文件單獨打包。即對項目中的代碼進行分割。webpack支持兩種代碼分割的方式。html

1.同步引入文件vue

index.js代碼:node

import _ from 'lodash';
console.log(_.join(['Hello', 'webpack'], ' '));
複製代碼

若是直接打包index.js會發現業務代碼和loadsh庫打包到了一個bundle.js文件中。可見webpack默認不會處理同步引入的文件。此時修改webpack的配置,增長optimization.splitChunks的配置項:webpack

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
	index: './src/index.js'
  },
  output: {
	filename: 'bundle.js',
	chunkFilename: '[name].bundle.js',
	path: path.resolve(__dirname, 'dist')
  },
  plugins: [
	new CleanWebpackPlugin(),
	new HtmlWebpackPlugin()
  ],
  optimization: {
	splitChunks: {
	  chunks: 'all'
	}
  }
}
複製代碼

再次打包發現除了bundle.js文件還生成了vendors~index.bundle.js。咱們成功的將lodash分割了出來。git

2.異步引入文件github

修改index.js代碼:web

function getComponent() {
	return import(/* webpackChunkName: "lodash" */ 'lodash').then(({default: _}) => {
		const element = document.createElement("div");
		element.innerHTML = _.join(['Hello', 'webpack'], ' ');
		return element;
	}).catch(error => 'An error occurred while loading the component');
}
getComponent().then(component => {
	document.body.appendChild(component);
})
複製代碼

刪除掉webpack中optimization.splitChunks的配置項後再進行打包,結果和上面的一致。loadsh被提取到了vendors~lodash.bundle.js文件中。因此webpack默認是僅對異步加載的塊進行分割。咱們須要default的緣由是,因爲Webpack4在導入CommonJS模塊時,導入將再也不解析爲module.exports的值,而是爲CommonJS模塊建立一我的工命名空間對象。vue-cli

咱們還能夠用async函數對上面的代碼進行簡化:npm

async function getComponent() {
	const {default: _} = await import(/* webpackChunkName: "lodash" */ 'lodash');
	const element = document.createElement("div");
	element.innerHTML = _.join(['Hello', 'webpack'], ' ');
	return element;
}
getComponent().then(component => {
	document.body.appendChild(component);
})
複製代碼

打包結果一樣將lodash分割了出來。element-ui

可是不論是同步引入模塊仍是異步引入都會受splitChunks配置項的影響(例如分割出的lodash名稱爲vendors~lodash.bundle.js)。下面咱們來詳細的說一下webpack中splitChunks默認配置:

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'async',//表示將選擇哪些塊進行優化。提供字符串時,有效值爲all、async和initial,默認是僅對異步加載的塊進行分割。
      minSize: 30000,//模塊大於minSize時纔會被分割出來。
      maxSize: 0,//生成的塊的最大大小,若是超過了這個限制,大塊會被拆分紅多個小塊。
      minChunks: 1,//拆分前必須共享模塊的最小塊數。
      maxAsyncRequests: 5,//按需加載時並行請求的最大數目。
      maxInitialRequests: 3,//入口點的最大並行請求數
      automaticNameDelimiter: '~',//默認狀況下,webpack將使用塊的來源和名稱(例如vendors~main.js)生成名稱。此選項容許您指定要用於生成的名稱的分隔符。
      automaticNameMaxLength: 30,//容許爲SplitChunksPlugin生成的塊名稱的最大長度
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,//控制此緩存組選擇的模塊。省略它將選擇全部模塊。它能夠匹配絕對模塊資源路徑或塊名稱。匹配塊名稱時,將選擇塊中的全部模塊。
          priority: -10//一個模塊能夠屬於多個緩存組,模塊出如今優先級最高的緩存組中
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true//若是當前塊包含已經從主包中分離出來的模塊,那麼該模塊將被重用,而不是生成新的模塊
        }
      }
    }
  }
};
複製代碼

這些配置共同控制打包流程,例如仍是以前同步引入lodash的例子

index.js

import _ from 'lodash';
console.log(_.join(['Hello', 'webpack'], ' '));
複製代碼

修改webpack的配置:

optimization: {
	splitChunks: {
	    chunks: 'all',
	    cacheGroups: {
		vendors: false,
	        default: false
	    }
	}
}
複製代碼

再次打包發現lodash並無被分割出來,chunks雖然設置成了'all',可是webpack還會繼續查看cacheGroups中的配置,發現都爲false,webpack就不知道要把lodash打包到哪裏,致使分割失敗。

那麼繼續修改下配置:

optimization: {
	splitChunks: {
		chunks: 'all',
		cacheGroups: {
		    vendors: {
	                test: /[\\/]node_modules[\\/]/,
	                priority: -10
	             },
		     default: false
	         }
	}
}
複製代碼

再次打包,由於lodash是屬於node_modules模塊的,因此lodash被提取到了vendors~index.bundle.js文件中,表明來源於vendors緩存組而且是從index.js模塊分割出來的。

繼續修改配置:

optimization: {
	splitChunks: {
		chunks: 'all',
		cacheGroups: {
			vendors: false,
			default: {
	                    minChunks: 2,
	                    priority: -20,
	                    reuseExistingChunk: true
	                 }
	        }
	}
}
複製代碼

再次打包發現lodash並無被分割出來,default不該該是默認匹配全部的麼,仔細查看配置發現minChunks默認爲2,意思是lodash至少須要被引用兩次纔會被分割出來,咱們的代碼中lodash只被index.js引用了一次。因此咱們修改minChunks爲1,再次打包,lodash被打包到了default~index.bundle.js文件中。說明此時被匹配到了default緩存組裏面。若是一個模塊被同時匹配到了多個緩存組,就要根據priority來決定模塊被分配到哪一個緩存組了,priority越大優先級越高。因此代碼分隔是受splitChunks多個配置項共同控制的。

vue-cli3對cacheGroups的初始配置(@vue\cli-service\lib\config\prod.js)爲:

webpackConfig
    .optimization.splitChunks({
      cacheGroups: {
        vendors: {
          name: `chunk-vendors`,
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: `chunk-common`,
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    })
複製代碼

在咱們的項目中要引入不少的第三方組件庫例如element-ui,還有公司內部的一些組件庫,若是咱們使用默認配置不作任何修改,vendors緩存組中的內容會不少,頁面引用時耗時就會較長,因此將緩存組中的內容進行拆分是頗有必要的。

例如咱們將element-ui分割成一個獨立的chunk,cacheGroups中配置以下:

cacheGroups: {
          elementUI: {
            priority: 20,
            name: "element-ui",
            test: /element-ui/,
            reuseExistingChunk: true
          },
          vendors: {
            chunks: 'all',
            test: /[\\/]node_modules[\\/]/,
            priority: 0,
            minChunks: 2,
            name: 'vendors',
            reuseExistingChunk: true
          }
}
複製代碼

elementUI緩存組的test我直接用的模塊名稱,至於爲何不是:

test: /[\\/]node_modules[\\/]element-ui[\\/]/
複製代碼

是由於項目若是是用npm install安裝的沒有問題,element-ui能夠被分割出來。可是假如是用cnpm install安裝的,安裝完的element-ui模塊名稱爲_element-ui@版本號@element-ui,就不能匹配到elementUI緩存組中了,使用模塊名稱能夠兼容兩種安裝方式。

再說一下optimization.runtimeChunk它的配置很簡單:

config.optimization.runtimeChunk = {
    name:'manifest'
};
複製代碼

做用卻很大,它是將包含chunks 映射關係的 list單獨從 app.js裏提取出來,由於每個 chunk 的 id 基本都是基於內容 hash 出來的,因此你每次改動都會影響它,若是不將它提取出來的話,等於app.js每次都會改變。緩存就失效了。

若是要分析本身項目中的打包結果,可使用webpack-bundle-analyzer插件,來對大的包或者app.js進行優化。

相關文章
相關標籤/搜索