OGIOS

personal website.


Table of contents

Lazy load js package code

Note

使用 vite+vue3 构建项目

Known

测试用仓库 进入app/website文件夹下后进行bun run build即可

  1. 同步引用import xx from 'xxx'会合并, 异步引用() => import('xxx')会分包
  2. 同步引用不管深度多深都可以平铺出来,当作在根文件中执行了引用
    异步引用则直接与当前同步进行分割,可以被视为一个分支的概念,一定会被拆开成为另一个文件
    举一个例子,假设现在有a,b,c三个文件。
    • a同步引用b, b同步引用c, 同步引用可以平铺, 这其实相当于直接在a中同步引用了bc。这时如果在a中对c进行异步引用则会导致冲突, 使得c无法被分包(因为c已经在同一个地方a中被同步引用)
    • a异步引用b, b同步引用c, 这相当于a中没有任何同步引用, 但有另一个异步分支引用b。这时在a中同步引用c的话,c会与a进行合并,而在b中则通过同步引用这个合并后的文件获取c
    • a异步引用b, b异步引用c。这时在a中同步引用cc还是会与a进行合并,而在b中则通过__vitePreload异步引用这个合并后的文件获取c
  3. 异步引用而分包作为入口文件 的代码所包含的export可以被保留named export, 例如export default {}export const/function xxx = something等变为:
    const a = {};
    const o = something;
    export { a as default, o as xxx };
    
    而通过同步引用的包经过其他手段进行分包(如manualChunks)则无法保留named export, 同样引用上面的例子,打包后可能会变为:
    const a = {};
    const o = something;
    export { a as u, o as p };
    
    通过manualChunks实现类似组件库分割组件的想法不可行, 最好通过每个组件一个入口文件实现分割, 推荐通过配置build.lib来实现
  4. 如果一个文件同时 异步引用 并且 manualChunks中进行分包 则无法保留 named export

Doubt

  1. 关于分包,不知道vite是否存在chunk大小上限限制或其他的同步引用分包策略,理论上来说所有同步引用都会被打成一个chunk?

Vite打包和package.json

Note

如果该包作为组件使用而不是网页则推荐使用build.lib模式配置并打包
当然也可以自己写rollup配置文件, 但两种方式都需要自己写点脚本代码

项目src下的结构:

.
├── assets
│   └── vue.svg
├── components
│   └── i18n
│       ├── index.ts
│       └── lang
│           ├── en.ts
│           └── zh.ts
├── index.ts
└── vite-env.d.ts

这里在entry中配置了所有语言文件及其他入口,并且在fileName中通过matchLocaleLang来匹配语言文件的名字并加上路径

function getLocaleMap() {
  const cwd = "./src/components/i18n/lang/";
  const resList = Array.from(
    new Bun.Glob("*.ts").scanSync({
      cwd,
    }),
  ).map((v) => path.join(cwd, v));

  const resMap: Record<string, string> = {};
  const tsPattern = /(.*)\.ts$/i;
  resList.forEach((v) => {
    console.log(v);
    const name = "locale_" + path.basename(v).replace(tsPattern, "$1");
    resMap[name] = v;
  });

  console.log(resMap);
  return resMap;
}

function matchLocaleLang(name: string) {
  const localePathBase = "i18n/";
  const localeLangPath = "lang/";

  // local lang
  const localPattern = /^locale_(.*)/i;
  const res = localPattern.exec(name);
  if (res && res[1]) return path.join(localePathBase, localeLangPath, res[1]);

  // locale
  if (name === "locale") return path.join(localePathBase, name);

  // other
  return name;
}

export default defineConfig(() => {
  return {
    build: {
      lib: {
        entry: {
          index: "src/index.ts",
          locale: "src/components/i18n/index.ts",
          ...getLocaleMap(),
        },
        fileName(format, entryName) {
          entryName = matchLocaleLang(entryName);
          console.log(entryName);
          return `${entryName}.${format}.js`;
        },
        formats: ["es"],
      },
    },
  };
});

打包后得到:

.
├── components
│   └── i18n
│       ├── index.d.ts
│       └── lang
│           ├── en.d.ts
│           └── zh.d.ts
├── i18n
│   ├── lang
│   │   ├── en.es.js
│   │   └── zh.es.js
│   └── locale.es.js
├── index.d.ts
├── index.es.js
└── vite.svg

tsc输出的d.ts与js路径不同没事,可以通过package.json中的exports配置来实现:

{
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.es.js",
      "types": "./dist/index.d.ts"
    },
    "./i18n/lang/*": {
      "types": "./dist/components/i18n/lang/*.d.ts",
      "import": "./dist/i18n/lang/*.es.js"
    },
  },
}

这里我指明了入口和i18n/lang/*, typesimport分别指定d.ts和js的路径, 但需要注意的是*只能匹配一次, 不能实现类似**/*.xxx的匹配
这样就实现了分开导出且保留named export