External Bundle

3.5
Info

加载 External Bundle 中的 CSS 文件需要 Lynx 3.7 及以上版本。

External Bundle 用来把 Lynx 代码打成一份可复用的独立 bundle,并在多个 Lynx 应用里按需加载。它适合抽离 ReactLynx 组件库或跨应用共享的业务 bundle。

Chunk Splitting 的区别可以简单理解为:External Bundle 解决跨应用复用,Chunk Splitting 解决单应用内拆包。

整个流程分成两步:

  1. @lynx-js/lynx-bundle-rslib-config 构建 external bundle。
  2. @lynx-js/external-bundle-rsbuild-plugin 在宿主应用里加载它。

安装

npm
yarn
pnpm
bun
deno
npm install @lynx-js/lynx-bundle-rslib-config @lynx-js/external-bundle-rsbuild-plugin @lynx-js/react-umd @rslib/core -D

构建 External Bundle

对于 ReactLynx 组件 bundle,推荐直接使用下面这套配置:

rslib-comp-lib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';

export default defineExternalBundleRslibConfig({
  id: 'comp-lib',
  source: {
    entry: {
      './App.js': './external-bundle/CompLib.tsx',
    },
  },
  plugins: [pluginReactLynx()],
  output: {
    externalsPresets: {
      reactlynx: true,
    },
  },
});

externalsPresets.reactlynx 会帮你补齐标准的 ReactLynx 运行时映射,让 ReactLynx 组件 bundle 可以直接依赖共享运行时。

执行构建:

npm
yarn
pnpm
bun
deno
npm exec rslib build --config rslib-comp-lib.config.ts

在宿主应用中加载

宿主应用里使用同一个 request key:

lynx.config.ts
import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';

export default defineConfig({
  plugins: [
    pluginReactLynx(),
    pluginQRCode(),
    pluginExternalBundle({
      externalsPresets: {
        reactlynx: true,
      },
      externals: {
        './App.js': 'comp-lib.lynx.bundle',
      },
    }),
  ],
});

这里的简写,等价于下面这份展开后的配置:

externals: {
  './App.js': {
    bundlePath: 'comp-lib.lynx.bundle',
    libraryName: './App.js',
    background: { sectionPath: './App.js' },
    mainThread: { sectionPath: './App.js__main-thread' },
    async: true,
  },
}

如果 section name 和 request key 本来就一致,这就是最简洁的推荐写法。

什么时候配置 globalObject

大多数项目都不需要配置 globalObject

只有在你的应用开启了后台线程之间共享 globalThis,并且希望后续 bundle 复用之前已经加载过的 externals 时,才需要设置 globalObject: 'globalThis'

需要时,构建侧和宿主侧都保持一致:

rslib-comp-lib.config.ts
output: {
  externalsPresets: {
    reactlynx: true,
  },
  globalObject: 'globalThis',
}
lynx.config.ts
pluginExternalBundle({
  externalsPresets: {
    reactlynx: true,
  },
  externals: {
    './App.js': 'comp-lib.lynx.bundle',
  },
  globalObject: 'globalThis',
});

为什么需要 @lynx-js/react-umd

@lynx-js/react-umd 提供的是 reactlynx 内置 preset 依赖的 ReactLynx 运行时 bundle。

当你在 pluginExternalBundle() 里开启 externalsPresets.reactlynx 时,插件会根据 NODE_ENV 自动解析 @lynx-js/react-umd/dev@lynx-js/react-umd/prod,产出 react.lynx.bundle,并通过当前运行时 public path 去加载它。

对大多数项目来说,推荐路径就是:

  • 安装 @lynx-js/react-umd
  • 开启 reactlynx
  • 让 preset 提供 ReactLynx 运行时映射

扩展 Presets

推荐让构建侧和宿主侧使用同一个 preset 名称,这样“构建阶段的 external 映射”和“运行时的 bundle 加载规则”才能保持一致。

从 Lib 侧扩展

在 external bundle 构建侧,通过 defineExternalBundleRslibConfig({ output }) 里的 externalsPresetsexternalsPresetDefinitions 扩展:

rslib-comp-lib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';

export default defineExternalBundleRslibConfig({
  output: {
    externalsPresets: {
      reactlynxPlus: true,
    },
    externalsPresetDefinitions: {
      reactlynxPlus: {
        extends: 'reactlynx',
        externals: {
          '@lynx-js/lynx-ui': ['LynxUI', 'UI'],
        },
      },
    },
  },
});

这一侧负责的是:构建时把 import 映射成 external global。

从 Lynx 页面侧扩展

在宿主应用侧,通过 pluginExternalBundle({ externalsPresets, externalsPresetDefinitions }) 扩展:

lynx.config.ts
import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';

pluginExternalBundle({
  externalsPresets: {
    reactlynxPlus: true,
  },
  externalsPresetDefinitions: {
    reactlynxPlus: {
      extends: 'reactlynx',
      resolveExternals() {
        return {
          '@lynx-js/lynx-ui': {
            libraryName: ['LynxUI', 'UI'],
            bundlePath: 'lynx-ui.lynx.bundle',
            background: { sectionPath: 'LynxUI' },
            mainThread: { sectionPath: 'LynxUI__main-thread' },
            async: false,
          },
        };
      },
    },
  },
});

这一侧负责的是:告诉运行时 bundle 从哪里加载,以及 section name 是什么。

如果某个应用需要覆盖 preset 结果,依然可以直接写显式 externals

什么时候用对象形式的 externals

当你需要自定义 section name、自定义导出 library name、设置 timeout,或者本地 bundle 输出到独立目录时,使用完整对象形式。

lynx-examples 里的 external-bundle 示例就是这种写法:

lynx.config.mjs
pluginExternalBundle({
  externalBundleRoot: 'dist-external-bundle',
  externalsPresets: {
    reactlynx: true,
  },
  externals: {
    'lodash-es': {
      bundlePath: 'lodash-es.lynx.bundle',
      background: { sectionPath: 'lodash-es' },
      mainThread: { sectionPath: 'lodash-es__main-thread' },
      async: false,
      timeout: 10000,
    },
    './components': {
      bundlePath: 'comp.lynx.bundle',
      background: { sectionPath: 'component' },
      mainThread: { sectionPath: 'component__main-thread' },
      async: true,
      timeout: 5000,
    },
  },
  timeout: 10000,
});

适合这些场景:

  • 一个 bundle 里放多个业务模块
  • section name 和 request key 不一致
  • bundle 构建在 dist-external-bundle
  • 需要单独控制每个 bundle 的超时或加载方式

示例

文档里可以直接打开 lynx-examples 的 external-bundle 示例:

API 参考

除非另有说明,本项目采用知识共享署名 4.0 国际许可协议进行许可,代码示例采用 Apache License 2.0 许可协议进行许可。