SplitChunksPlugin
从webpack v4开始, CommonsChunkPlugin删除了,而改为optimization.splitChunks。
只要用来提取第三方库和公共模块,避免首屏加载的bundle文件或者按需加载的bundle过大,导致加载时间过长
多入口文件配置
使用对象语法,可以添加多个入口,同时也需要多出口,使用filename: '[name].js' 语法,匹配入口名称
// 导入处理路径的模块
var path = require('path');
module.exports = {
entry: {//多入口
index1:'./src/index1.js',
index2:'./src/index2.js',
},//应用程序开始执行
output: { // 配置输出选项
path: path.resolve(__dirname, 'dist'), // 配置输出的路径
filename: '[name].js' // 配置输出的文件名,[name]——表示输出文件名与入口文件一致
},
}
条件
webpack将根据以下条件自动分割块:
- 可以共享新块,或者模块来自node_modules文件夹
- 新的块将大于30kb(在min + gz之前)
- 按需加载块时并行请求的最大数量将小于或等于5
- 初始页面加载时并行请求的最大数量将小于或等于3
当试图满足最后两个条件时,最好使用较大的块。
optimization.splitChunks
配置对象代表的默认行为SplitChunksPlugin
默认的配置:
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
可以将此配置与HtmlWebpackPlugin结合使用。它将为您注入所有生成的供应商块。
- chunks: 可填 async, initial, all. async
chunks: 'async', //async表示只从异步加载得模块(动态加载import())里面进行拆分
chunks: 'ainital',//initial表示只从入口模块进行拆分
chunks: 'all',//包括async, initial的功能
- minSize (默认是30000):形成一个新代码块最小的体积
minSize: 30000,
- maxSize(默认是0) :告诉webpack尝试将大于大块的块拆分maxSize为较小的部分。
maxSize: 0,
- minChunks: (默认是1):在分割之前,这个代码块最小应该被引用的次数
minChunks: 1,
- maxAsyncRequests(默认是5):按需加载时候最大的并行请求数。
maxAsyncRequests: 5,
- maxInitialRequests(默认是3):一个入口最大的并行请求数
maxInitialRequests: 3,
- automaticNameDelimiter:默认情况下,webpack将使用块的来源和名称生成名称(例如vendors~main.js)。此选项使您可以指定用于生成名称的定界符。
automaticNameDelimiter: '~',
- name:chunk的名字,如果设成true,会根据被提取的chunk自动生成。
name: true,
name (module, chunks, cacheGroupKey) {
// 生成块名称
return; //...
}
- cacheGroups: 这个就是重点了,我们要切割成的每一个新chunk就是一个cache group。
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
- test:用来决定提取哪些module,可以接受字符串,正则表达式,或者函数,函数的一个参数为module,第二个参数为引用这个module的chunk(数组).
test(module, chunks) {
//...
return module.type === 'javascript/auto';
}
- priority:一个模块可以属于多个缓存组。优化将首选具有较高的缓存组priority。默认组的优先级为负,以允许自定义组获得更高的优先级(默认值适用0于自定义组)。
- reuseExistingChunk: 当module未变时,是否可以使用之前的chunk.
注意:
1. minChunks、maxAsyncRequests、maxInitialRequests的值必须设置为大于等于1的数
2. 当chunk没有名字时,通过splitChunks分出的模块的名字用id替代,当然也可以通过name属性自定义
3. splitChunks的配置项都是作用于cacheGroup上的,如果将cacheGroup的默认两个分组vendor和default设置为false,则splitChunks就不会起作用
4. splitChunks.cacheGroup必须同时满足各个条件才能生效
5. 当父chunk和子chunk同时引入相同的module时,并不会将其分割出来而是删除掉子chunk里面共同的module,保留父chunk的module,是因为 optimization.removeAvaliableModules 默认是true
6. 当两个cacheGroup.priority相同时,先定义的执行
缓存组
一个模块可以被分配到多个缓存组,优化策略会将模块分配至跟高优先级别(priority)的缓存组,或者会分配至可以形成更大体积代码块的组里。
splitChunks默认有两个缓存组:vender和default
default缓存组的优先级(priotity)是负数,因此所有自定义缓存组都可以有比它更高优先级
禁用default缓存组:
defalut:false;
缓存组可以继承和/或覆盖splitChunks.*;中的任何选项。
若要禁用任何默认缓存组,请将它们设置为false
测试
- 创建相关入口文件
//commons.js —— 一个公共组件
export const t = '这是公共文件'
//index1.js —— 入口文件一
import {t} from "./commons.js";//引入公共资源
import Vue from "../vue";//引入第三方库
new Vue({
el:"#app",
data(){
return{
msg : t
}
}
});
alert(t+'index1');
//index2.js —— 入口文件二
import {t} from "./commons.js";//引入公共资源
import Vue from "../vue";//引入第三方库
new Vue({
el:"#app",
data(){
return{
msg : t
}
}
});
alert(t+'index2');
- 配置文件
默认的东西根据需要修改,然后再添加一些缓存组
注意:要下载插件cnpm i html-webpack-plugin --save-dev,手动引入文件,注意修改配置文件中参照文件的目录,也可一不下载但是要删除对应的内容,
例如:
// 导入处理路径的模块
var path = require('path');
// 导入自动生成HTMl文件的插件
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {//多入口
index2:'./src/index1.js',
index3:'./src/index2.js',
},//应用程序开始执行
output: { // 配置输出选项
path: path.resolve(__dirname, 'dist'), // 配置输出的路径
filename: '[name].js' // 配置输出的文件名,[name]——表示输出文件名与入口文件对象名一致
},
plugins:[ // 添加plugins节点配置插件
new htmlWebpackPlugin({
template:path.resolve(__dirname, 'src/index.html'),//模板路径
filename:'index.html'//自动生成的HTML文件的名称
})
],
optimization: {
splitChunks: {
maxInitialRequests:4,//入口最大的并行请求数,默认为3,因为要产生4个文件因此需要修改
automaticNameDelimiter:'_',
minChunks: 2,
chunks:'all',
cacheGroups: {
//待添加缓存组
}
}
}
}
添加缓存组
// 获取包含所有被其他入口(entrypoints)共享的代码。
common_1:{//缓存组名称
name:"commons",//设置分割文件的名字
//可以不设置,默认会使用缓存组名称以及其他出口文件名的组合,
// 使用automaticNameDelimiter声明的分隔符号分割,默认值为'~',可以修改为其他值
// 因此文件名:commons_index2_index3.js
},
打包对应的文件目录
分隔出文件commons.js , 其内容包含第三方库,公共组件,webpack打包组件
// 获取包含整个应用所有来自node_modules的代码
common_2: {
test: /[\\/]node_modules[\\/]/,
name: "vender",
}
打包对应的文件目录
产生两个文件,index2_index3.js和vender.js
index2_index3.js包含第三方库,公共组件
vender.js包含webpack打包组件
参考 :
异步加载
1、System.import(); 已废除,不推荐
2、require.ensure(); v1和v2均可使用
3、import();v2支持,v1不支持
import()
function(string path):Promise
动态加载模块。对的调用import()被视为拆分点,这意味着所请求的模块及其子级被拆分为单独的块。
import('lodash').then(_ => {
// Do something with lodash (a.k.a '_')...
})
函数只接受一个参数,就是引用包的地址;此功能在Promise内部依赖
let filename = 'module.js';
import('./' + filename). then(module =>{
console(module);
});
知道 export的函数名或者参数名
import('./' + filename). then(({Name}) =>{
console(Name);
});
如果使用的是export default function()导出的函数或者参数
import('./' + filename). then(module =>{
console(module.default);
});
或
import('./' + filename). then(({default:fnName}) =>{
console(fnName);
});
import()必须包含在模块位于至少一些信息,捆绑可以限制为特定目录或文件集,以便在使用动态表达式时- import()包括可能在呼叫中请求的每个模块。
参数
/* */ 在这不代表着注释,这些参数以此方式存在并实现自身作用
// Multiple possible targets
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
'ignored-module.js'
);
import(/* webpackIgnore: true */ 'ignored-module.js');
ignored-module.js一个文件的目录,可以是相对路径,也可以是绝对路径
- webpackIgnore —— 设置为时,禁用动态导入解析true,退出代码拆分。
webpackIgnore: true
webpackChunkName —— 新块的名称。在给定的字符串中,占位符[index]和[request]被支持为递增的数字或实际解析的文件名。
webpackMode —— 可以指定用于解决动态导入的不同模式。
- 'lazy'(默认值) —— 为每个 import() ed模块生成可延迟加载的块。
- 'lazy-once'—— 生成一个可以满足对的所有调用的可延迟加载的块import()。该块将在对的第一次调用时获取import(),而对的后续调用import()将使用相同的网络响应。
- 'eager' —— 不生成额外的块。所有模块都包含在当前块中,并且不发出其他网络请求。
- 'weak' —— :如果已经以其他方式加载了模块功能(例如,导入了另一个块或加载了包含该模块的脚本),则尝试加载该模块。
webpackPrefetch —— 告诉浏览器将来可能需要某种资源来进行某些导航
webpackPrefetch: true
- webpackPreload —— 告诉浏览器在当前导航期间可能需要该资源。
webpackPreload: true
- webpackInclude —— 在导入解析期间将与之匹配的正则表达式。仅将匹配的模块捆绑在一起。
webpackInclude: /\.json$/
- webpackExclude —— 在导入解析期间将与之匹配的正则表达式。匹配的任何模块都不会捆绑在一起。
webpackExclude: /\.noimport\.json$/
注意:webpackInclude和webpackExclude选项不会干扰前缀
实际操作
- 新建文件
这些文件均位于src目录下
A.js
export let A = {
"data" : "this is A"
}
B.js
export let B= {
"data" : "this is B"
};
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<button id="aBtn">ABtn</button>
<br>
<button id="bBtn">BBtn</button>
</body>
</html>
index.js
console.log("This is main!");
// 获取依赖
document.getElementById("aBtn").onclick=function(){
// 异步加载,A.js
import(/*webpackChunkName:'fileA'*/'./A'). then(({A})=>{
alert(A.data);
})
}
document.getElementById("bBtn").onclick=function(){
// 异步加载,B.js
import(/*webpackChunkName:'fileB'*/'./B'). then(({B})=>{
alert(B.data);
})
};
- 下载html-webpack-plugin插件
npm i html-webpack-plugin -D
- 配置webpack.conf.js
// 导入处理路径的模块
const path = require("path");
// 导入自动生成HTMl文件的插件
var htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
"index": "./src/index.js", //应用程序开始执行
},
output: {
path: path.resolve(__dirname, "dist"),// 配置输出的路径
filename: "[name].js",// 配置输出的文件名,[name]——表示输出文件名与入口文件对象名一致
publicPath: './',//动态import文件路径
chunkFilename: "[name].chunk.js"//动态import文件名
},
plugins:[ // 添加plugins节点配置插件
new htmlWebpackPlugin({
template:path.resolve(__dirname, 'src/index.html'),//模板路径
filename:'index.html',//自动生成的HTML文件的名称
})
],
};
- 打包测试
打开dist中的index.html,鼠标右击检查,找到NetWork,刷新一下,会出现如下的一些js文件:
可以发现这里并没有生成的fileB.chunk.js和fileA.chunk.js文件,当点击ABtn按钮,就会出现对应的fileA.chunk.js文件,很明显这样就可以异步加载成功了
注意:
- chunkFilename: "[name].chunk.js" 是给import文件重新命名的,默认情况下按着顺序从0.js,1.js,····,
- 也可以在webpackChunkName:'fileA'进行设置名字
参考: