一种轻量的vite分包方式目前vite自带的splitVendorChunkPlugin已经被标记成deprecated
分包策略介绍
众所周知,vite的分包需要配置rollup中的output.manualChunks选项。目前vite自带的splitVendorChunkPlugin已经被标记成deprecated。那如何在自己的项目中方便的使用分包就成了一个问题。于是我基于自己的理解,写了一个适用于CSR场景的分包插件:
vite-plugin-dynamic-chunk。
下面介绍一下这个插件的分包策略,和我的设计思路。
编译的结果大致会成如下形式:
- 只要被入口文件静态引用的依赖,就会被打包到common vendor中。
- 多个动态路由引用的相同依赖,会打包到同一个shared vendor中。
- 只有一个动态路由引用的依赖,打包到自己路由的vendor中。
- 多个路由引用的相同业务代码,单独拆分为一个文件。
- 只要被入口文件静态引用的业务代码,就会被包含到index.js中。
- 被一个动态入口(路由)静态应用的业务代码,会被包含到这个动态入口文件中。
这样分包的优势有:
- 所有依赖均从业务代码中拆分,业务更新不影响依赖文件的缓存命中。
- 使用动态import路由的情况,访问其中一个路由,不会加载该路由不会使用的依赖代码,访问会更快。
实现细节
分析这个文件是否被入口文件静态import
function staticImportedScan(
id: string,
getModuleInfo: (arg0: any) => any,
cache: Map<any, any>,
importChain: string[] = [],
) {
if (cache.has(id)) {
return cache.get(id);
}
if (importChain.includes(id)) {
// circular deps!
cache.set(id, false);
return false;
}
const mod = getModuleInfo(id);
if (!mod) {
cache.set(id, false);
return false;
}
if (mod.isEntry) {
cache.set(id, true);
return true;
}
const staticImport = mod.importers.some((importer: any) =>
staticImportedScan(importer, getModuleInfo, cache, importChain.concat(id)),
);
cache.set(id, staticImport);
return staticImport;
}
分析这个文件被哪些文件动态import
function dynamicImportedScan(
id: string,
getModuleInfo: (arg0: unknown) => {
importers: any;
dynamicImporters: any;
isEntry?: any;
},
) {
const { importers, dynamicImporters } = getModuleInfo(id);
const dependentEntryPoints = new Set<string>(dynamicImporters);
const idsToHandle = new Set<string>(importers);
const lastImporters = new Set<string>();
for (const moduleId of idsToHandle) {
const { isEntry, dynamicImporters: _dynamicImporters, importers: _importers } = getModuleInfo(moduleId);
if (isEntry) {
continue;
}
if (_dynamicImporters.length > 0) {
lastImporters.add(moduleId);
_dynamicImporters.map((item: string) => dependentEntryPoints.add(item));
}
if (_importers.length > 0) {
for (const importer of _importers) {
idsToHandle.add(importer);
}
}
}
const result: string[] = [];
for (const s of lastImporters) {
result.push(s);
}
const alldynamic: string[] = [];
for (const s of dependentEntryPoints) {
alldynamic.push(s);
}
return {
result,
alldynamic,
};
}
只要知道一个文件是否被入口文件静态import了,以及有哪些动态import。我就可以实现上述的分包策略。具体细节就不一一展示了。
以上我根据自己的分包想法,实现一个轻量级的分包插件的一些简单陈述。如果你有类似的场景欢迎使用,也欢迎提出您的优化建议。
转载自:https://juejin.cn/post/7399985328734355506