likes
comments
collection
share

一种轻量的vite分包方式目前vite自带的splitVendorChunkPlugin已经被标记成deprecated

作者站长头像
站长
· 阅读数 25

分包策略介绍

众所周知,vite的分包需要配置rollup中的output.manualChunks选项。目前vite自带的splitVendorChunkPlugin已经被标记成deprecated。那如何在自己的项目中方便的使用分包就成了一个问题。于是我基于自己的理解,写了一个适用于CSR场景的分包插件:

vite-plugin-dynamic-chunk。

下面介绍一下这个插件的分包策略,和我的设计思路。

编译的结果大致会成如下形式:

一种轻量的vite分包方式目前vite自带的splitVendorChunkPlugin已经被标记成deprecated

  1. 只要被入口文件静态引用的依赖,就会被打包到common vendor中。
  2. 多个动态路由引用的相同依赖,会打包到同一个shared vendor中。
  3. 只有一个动态路由引用的依赖,打包到自己路由的vendor中。
  4. 多个路由引用的相同业务代码,单独拆分为一个文件。
  5. 只要被入口文件静态引用的业务代码,就会被包含到index.js中。
  6. 被一个动态入口(路由)静态应用的业务代码,会被包含到这个动态入口文件中。

这样分包的优势有:

  1. 所有依赖均从业务代码中拆分,业务更新不影响依赖文件的缓存命中。
  2. 使用动态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
评论
请登录