CommonsChunkPlugin主要是用来提取第三方库和公共模块,避免首屏加载的bundle文件或者按需加载的bundle文件体积过大,从而致使加载时间过长,着实是优化的一把利器。vue
先来讲一下各类教程以及文档中CommonsChunkPlugin说起到chunk有哪几种,主要有如下三种:node
children和async属于异步中的应用,放在了最后讲解。jquery
可能这么说,你们会云里雾里,下面用demo来检验上面的属性。webpack
如下几个demo主要是测试如下几种状况:web
项目初始结构,后面打包后会生成dist目录:npm
src目录下各个文件内容都很简洁的,以下:element-ui
common.js export const common = 'common file'; first.js import {common} from './common'; import $ from 'jquery'; console.log($,`first ${common}`); second.js import {common} from './common'; import $ from 'jquery'; console.log($,`second ${common}`);
package.json文件:json
{ "name": "test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "rimraf dist && webpack" }, "author": "", "license": "ISC", "devDependencies": { "rimraf": "^2.6.2", "webpack": "^3.10.0", "webpack-dev-server": "^2.10.1" }, "dependencies": { "jquery": "^3.2.1" } }
webpack.config.js:segmentfault
const path = require("path"); const webpack = require("webpack"); const config = { entry: { first: './src/first.js', second: './src/second.js' }, output: { path: path.resolve(__dirname,'./dist'), filename: '[name].js' }, } module.exports = config;
接着在命令行npm run build,此时项目中多了dist目录:浏览器
再来查看一下命令行中webpack的打包信息:
查看first.js和second.js,会发现共同引用的common.js文件和jquery都被打包进去了,这确定不合理,公共模块重复打包,体积过大。
这时候修改webpack.config.js新增一个入口文件vendor和CommonsChunkPlugin插件进行公共模块的提取:
const path = require("path"); const webpack = require("webpack"); const packagejson = require("./package.json"); const config = { entry: { first: './src/first.js', second: './src/second.js', vendor: Object.keys(packagejson.dependencies)//获取生产环境依赖的库 }, output: { path: path.resolve(__dirname,'./dist'), filename: '[name].js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].js' }), ] } module.exports = config;
查看dist目录下,新增了一个vendor.js的文件:
再来查看一下命令行中webpack的打包信息:
经过查看vendor.js文件,发现first.js和second.js文件中依赖的jquery和common.js都被打包进vendor.js中,同时还有webpack的运行文件。总的来讲,咱们初步的目的达到,提取公共模块,可是它们都在同一个文件中。
到这里,确定有人但愿自家的vendor.js纯白无瑕,只包含第三方库,不包含自定义的公共模块和webpack运行文件,又或者但愿包含第三方库和公共模块,不包含webpack运行文件。
其实,这种想法是对,特别是分离出webpack运行文件,由于每次打包webpack运行文件都会变,若是你不分离出webpack运行文件,每次打包生成vendor.js对应的哈希值都会变化,致使vendor.js改变,但实际上你的第三方库实际上是没有变,然而浏览器会认为你原来缓存的vendor.js就失效,要从新去服务器中获取,其实只是webpack运行文件变化而已,就要人家从新加载,好冤啊~
OK,接下来就针对这种状况来测试。
这里咱们分两步走:
先来抽离webpack运行文件,修改webpack配置文件:
plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: ['vendor','runtime'], filename: '[name].js' }), ]
其实上面这段代码,等价于下面这段:
plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].js' }), new webpack.optimize.CommonsChunkPlugin({ name: 'runtime', filename: '[name].js', chunks: ['vendor'] }), ]
上面两段抽离webpack运行文件代码的意思是建立一个名为runtime的commons chunk进行webpack运行文件的抽离,其中source chunks是vendor.js。
查看dist目录下,新增了一个runtime.js的文件,其实就是webpack的运行文件:
再来查看一下命令行中webpack的打包信息,你会发现vendor.js的体积已经减少,说明已经把webpack运行文件提取出来了:
但是,vendor.js中还有自定义的公共模块common.js,人家只想vendor.js拥有项目依赖的第三方库而已(这里是jquery),这个时候把minChunks这个属性引进来。
minChunks能够设置为数字、函数和Infinity,默认值是2,并非官方文档说的入口文件的数量,下面解释下minChunks含义:
要在vendor.js中把第三方库单独抽离出来,上面也说到了有两种方法。
第一种方法minChunks设为Infinity,修改webpack配置文件以下:
plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: ['vendor','runtime'], filename: '[name].js', minChunks: Infinity }), new webpack.optimize.CommonsChunkPlugin({ name: 'common', filename: '[name].js', chunks: ['first','second']//从first.js和second.js中抽取commons chunk }), ]
查看dist目录下,新增了一个common.js的文件:
再来查看一下命令行中webpack的打包信息,自定义的公共模块分离出来:
这时候的vendor.js就纯白无瑕,只包含第三方库文件,common.js就是自定义的公共模块,runtime.js就是webpack的运行文件。
第二种方法把它们分离开来,就是利用minChunks做为函数的时候,说一下minChunks做为函数两个参数的含义:
minChunks做为函数会遍历每个入口文件及其依赖的模块,返回一个布尔值,为true表明当前正在处理的文件(module.resource)合并到commons chunk中,为false则不合并。
继续修改咱们的webpack配置文件,把vendor入口文件注释掉,用minChunks做为函数实现vendor只包含第三方库,达到和上面同样的效果:
const config = { entry: { first: './src/first.js', second: './src/second.js', //vendor: Object.keys(packagejson.dependencies)//获取生产环境依赖的库 }, output: { path: path.resolve(__dirname,'./dist'), filename: '[name].js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].js', minChunks: function (module,count) {//让vendor只保留第三方库的办法 console.log(module.resource,`引用次数${count}`); //"有正在处理文件" + "这个文件是 .js 后缀" + "这个文件是在 node_modules 中" return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, './node_modules')) === 0 ) } }), new webpack.optimize.CommonsChunkPlugin({ name: 'runtime', filename: '[name].js', chunks: ['vendor'] }), ] }
上面的代码其实就是生成一个叫作vendor的commons chunk,那么有哪些模块会被加入到vendor中呢?就对入口文件及其依赖的模块进行遍历,若是该模块是js文件而且在node_modules中,就会加入到vendor当中,其实这也是一种让vendor只保留第三方库的办法。
再来查看一下命令行中webpack的打包信息:
你会发现,和上面minChunks设为Infinity的结果是一致的。
这两个属性主要是在code split(代码分割)和异步加载当中应用。
children
修改webpack配置文件,增长chunkFilename,以下:
output: { ........... chunkFilename: "[name].[hash:5].chunk.js", }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: ['vendor','runtime'], filename: '[name].js', minChunks: Infinity }), new webpack.optimize.CommonsChunkPlugin({ children: true, async: 'children-async' }) ]
chunkFilename用来指定异步加载的模块名字,异步加载模块中的共同引用到的模块就会被合并到async中指定名字,上面就是children-async。
修改为异步截图出来太麻烦了,就简单说明一下:first和second是异步加载模块,同时它们共同引用了common.js这个模块,若是你不设置这一步:
new webpack.optimize.CommonsChunkPlugin({ children: true, async: 'children-async' })
那么共同引用的common.js都被打包进各自的模块当中,就重复打包了。
OK,你设置以后,也得看children的脸色怎么来划分:
先来讲一下哈希值的不一样:
因此,在生产环境,要把文件名改为'[name].[chunkhash]',最大限度的利用浏览器缓存。
本文摘自https://segmentfault.com/a/1190000012828879?utm_source=tag-newest
// split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function(module, count) { // any required modules inside node_modules are extracted to vendor return( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor1', chunks: ['vendor'], minChunks: function(module, count) { // any required modules inside node_modules are extracted to vendor console.log('module.resource',module.resource); return( module.resource && /(vue)/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), new webpack.optimize.CommonsChunkPlugin({ name: 'elementUI', chunks: ['vendor'], minChunks: function(module, count) { // any required modules inside node_modules are extracted to vendor return( module.resource && /(element-ui)/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), new webpack.optimize.CommonsChunkPlugin({ name: 'ele-common', chunks: ['elementUI'], minChunks: function(module, count) { // any required modules inside node_modules are extracted to vendor return( module.resource && /(common)/.test(module.resource) ) } })