rollup 是怎样解决循环依赖的

977 阅读1分钟

循环依赖的产生

// main.js
import { funcB } from "./b.js";

funcB();

export var funcA = () => {
  console.log("a");
};

第一行去加载b.js

// b.js
import { funcA } from "./main.js";

funcA();

export var funcB = () => {
  console.log("b");
};

第一行又加载main.js,此时funcA并没有定义,直接执行就会报错

image.png

解决办法

比如业务代码main引用了A包, main,A需要分两个chunk,A包有个import B

如果打包的结果是 B打入了 chunkmain的内容,就会出现上面的这种情况

rollup生成图的时候,有这样的关系

B.importers -> A

那么是不是只要在split的时候,按照这个引用关系,把B打入到A的chunk之中就好了

const chunkGroups = {
  'A-vendor': [
    normalizePath(path.resolve('./src/a.js')),
  ],
}

manualChunks(id, { getModuleInfo }) { 
  for (const group of Object.keys(chunkGroups)) {
    const deps = chunkGroups[group];
    if (isDepInclude(id, deps, [], getModuleInfo)) { 
      return group; // chunk的名字,这里需要a.js和b.js都走到这步并且返回相同的名字
    }
  }
}
function isDepInclude (id, depPaths, importChain, getModuleInfo)  {
  id = normalizePath(id);
  const key = `${id}-${depPaths.join('|')}`;

  // 这步是防止自己引用自己,会造成死循环
  if (importChain.includes(id)) {
    cache.set(key, false);
    return false;
  }
  if (cache.has(key)) {
    return cache.get(key);
  }
  // 命中依赖列表 处理a.js会走到这步,b.js递归也会走到这步
  if (depPaths.includes(id)) {
    importChain.forEach(item => cache.set(`${item}-${depPaths.join('|')}`, true));
    return true;
  }
  const moduleInfo = getModuleInfo(id);
  if (!moduleInfo || !moduleInfo.importers) {
    cache.set(key, false);
    return false;
  }
  // 递归查找上层引用者
  const isInclude = moduleInfo.importers.some(
    importer => isDepInclude(importer, depPaths, importChain.concat(id), getModuleInfo)
  );
  // 设置缓存
  cache.set(key, isInclude);
  return isInclude;
};

处理b.js的时候,会去递归isDepInclude,然后命中了a.js,就会和a.js一起打入A-vendor

vite-plugin-chunk-split

用这插件就不用自己写分包代码了