External Bundle
3.5
External Bundle 是指将代码打包为可在不同 Lynx 应用中运行时动态加载的模块,以减小产物体积。使用流程包含两个步骤:
@lynx-js/lynx-bundle-rslib-config - 用于构建 External Bundle
@lynx-js/external-bundle-rsbuild-plugin - 用于在应用中加载 External Bundle
INFO
与 Chunk Splitting 区别:
- 线程支持范围:Chunk Splitting 仅支持后台线程代码拆分,External Bundle 同时支持后台线程和主线程
- 跨应用共享:External Bundle 可以在多个 Lynx 应用之间共享,减少重复代码;而 Chunk Splitting 的产物仅在当前应用内使用
- 打包方式:Chunk Splitting 是构建时自动拆分,与应用产物一起部署;External Bundle 需要单独构建,可以与应用产物一起部署、也可以上传到 CDN 或服务器,运行时动态加载
- 使用场景:External Bundle 适合跨应用共享的通用库(如
lodash-es、moment);Chunk Splitting 适合单应用内 的代码分割优化
1. 构建 External Bundle
安装
npm install @lynx-js/lynx-bundle-rslib-config @rslib/core -D
基础用法
在 rslib.config.ts 中使用 defineExternalBundleRslibConfig() 定义 External Bundle 的构建配置。
示例 1:同时支持后台线程和主线程
rslib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
export default defineExternalBundleRslibConfig({
id: 'lodash-es',
source: {
entry: {
'lodash-es': require.resolve('lodash-es'),
},
},
});
执行以下命令构建:
输出文件为 dist/lodash-es.lynx.bundle,文件名前缀由 id 配置决定。可将 .lynx.bundle 文件上传至 CDN 或服务器。
TIP
由于 Lynx 后台线程和主线程的产物格式不同,当前版本会针对两个线程分别构建产物。dist/lodash-es.lynx.bundle 产物内容类似于:
{
'lodash-es': `// background code`, // 后台线程产物
'lodash-es__main-thread': `// main-thread code`, // `<entry>__main-thread` 是自动生成的后缀,代表主线程产物
}
后续版本将支持统一的代码产物,以进一步减小产物体积。
示例 2:仅用于后台线程
rslib.config.ts
import {
defineExternalBundleRslibConfig,
LAYERS,
} from '@lynx-js/lynx-bundle-rslib-config';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
export default defineExternalBundleRslibConfig({
id: 'lodash-es',
source: {
entry: {
'lodash-es': {
import: require.resolve('lodash-es'),
layer: LAYERS.BACKGROUND,
},
},
},
});
dist/lodash-es.lynx.bundle 产物内容类似于:
{
'lodash-es': `// lodash-es background code`,
}
示例 3:仅用于主线程
rslib.config.ts
import {
defineExternalBundleRslibConfig,
LAYERS,
} from '@lynx-js/lynx-bundle-rslib-config';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
export default defineExternalBundleRslibConfig({
id: 'lodash-es',
source: {
entry: {
'lodash-es': {
import: require.resolve('lodash-es'),
layer: LAYERS.MAIN_THREAD,
},
},
},
});
dist/lodash-es.lynx.bundle 产物内容类似于:
{
'lodash-es': `// lodash-es main-thread code`,
}
打包多个依赖
支持将多个依赖打包到同一个 external bundle,减少运行时请求次数,提升性能。
rslib.config.ts
import {
defineExternalBundleRslibConfig,
LAYERS,
} from '@lynx-js/lynx-bundle-rslib-config';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
export default defineExternalBundleRslibConfig({
id: 'lodash',
source: {
entry: {
'lodash.get': require.resolve('lodash.get'),
'lodash.isequal': require.resolve('lodash.isequal'),
},
},
});
dist/lodash.lynx.bundle 产物内容类似于:
{
'lodash.get': '// lodash.get background code',
'lodash.get__main-thread': '// lodash.get main-thread code',
'lodash.isequal': '// lodash.isequal background code',
'lodash.isequal__main-thread': '// lodash.isequal main-thread code',
}
引用其他 External Bundle
当 external bundle 依赖其他 external bundle 时,可通过 output.externals 将这些依赖声明为外部依赖,避免重复打包:
rslib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';
export default defineExternalBundleRslibConfig({
id: 'my-components',
output: {
externals: {
// 从 ReactLynx.React 中读取 @lynx-js/react
'@lynx-js/react': ['ReactLynx', 'React'],
},
},
});
WARNING
在运行时加载 my-components 之前,必须确保 @lynx-js/react external bundle 已经加载完成,否则会导致运行时错误。
自定义配置
defineExternalBundleRslibConfig() 会自动应用默认配置。如需修改默认配置,直接在配置对象中覆盖即可:
rslib.config.ts
import { defineExternalBundleRslibConfig } from '@lynx-js/lynx-bundle-rslib-config';
export default defineExternalBundleRslibConfig({
source: {
define: {
__PROFILE__: false,
__MAIN_THREAD__: "__webpack_layer__ === 'main-thread'",
__BACKGROUND__: "__webpack_layer__ === 'background'",
},
},
autoExternal: {
dependencies: true, // 将 dependencies 设为外部依赖
},
});
查看 Bundle 中间产物
.lynx.bundle 是编译后的产物,不可直接阅读。如需查看编译前代码,添加 DEBUG=rspeedy 环境变量:
DEBUG=rspeedy npm exec rslib build
会输出以下中间产物:
./dist
├── lodash-es.js
├── lodash-es.lynx.bundle
├── lodash-es\_\_main-thread.js
└── tasm.json
2. 加载 External Bundle
基于 Webpack Externals 实现。引入 external bundle 时,依赖不会被打包到主 bundle 中,而是从 .lynx.bundle 文件动态加载。
安装
npm install @lynx-js/external-bundle-rsbuild-plugin -D
基础用法
若代码中使用 lodash-es:
import _ from 'lodash-es';
_.isEmpty([]);
在 lynx.config.ts 中使用 pluginExternalBundle 插件,将 lodash-es 配置到 externals,避免打包到主 bundle 中:
lynx.config.ts
import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
export default {
plugins: [
pluginReactLynx(), // 必须在前面
pluginExternalBundle({
externals: {
'lodash-es': {
url: 'http://cdn.example.com/lodash-es.lynx.bundle',
background: { sectionPath: 'lodash-es' },
mainThread: { sectionPath: 'lodash-es__main-thread' },
},
},
}),
],
};
配置完成后,代码中 lodash-es 不会被打包到主 bundle 中,而是在运行时从指定 URL 动态加载。
配置多个 external bundle
可在 externals 中配置多个 external bundle:
lynx.config.ts
pluginExternalBundle({
externals: {
'@lynx-js/react': {
libraryName: ['ReactLynx', 'React'],
url: 'http://example.com/react.lynx.bundle',
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false, // 同步加载
},
'my-components': {
url: 'http://example.com/comp-lib.lynx.bundle',
background: { sectionPath: 'components' },
mainThread: { sectionPath: 'components__main-thread' },
},
},
});
配置项
- url: External Bundle 的加载地址(必填)
- libraryName: 指定全局变量名称,
string / string[](可选)
- background: 后台线程配置,包含
sectionPath(可选)
- mainThread: 主线程配置,包含
sectionPath(可选)
- async: 是否异步加载,默认为
true(可选)
libraryName
指定从哪个全局变量读取 external bundle 的模块导出。通常不需要配置,插件会根据 package 的导入名自动推断。
当 external bundle 挂载到嵌套对象时,使用 string[] 指定访问路径:
lynx.config.ts
pluginExternalBundle({
externals: {
'@lynx-js/react': {
libraryName: ['ReactLynx', 'React'], // 类似从 global.ReactLynx.React 读取
url: 'http://example.com/react.lynx.bundle',
},
},
});
sectionPath
sectionPath 是 external bundle 内部模块的路径标识符:
- background:后台线程使用的模块路径
- mainThread:主线程使用的模块路径
sectionPath 必须与构建 external bundle 时的实际输出 section 名称完全一致,否则运行时因找不到对应模块而报错。
以 构建 External Bundle - 示例 1 为例,对应的 sectionPath 配置如下:
lynx.config.ts
import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';
export default {
plugins: [
pluginExternalBundle({
externals: {
'lodash-es': {
url: 'http://cdn.example.com/lodash.lynx.bundle',
background: { sectionPath: 'lodash-es' },
mainThread: { sectionPath: 'lodash-es__main-thread' },
},
},
}),
],
};
若 external bundle 仅支持某个线程(参考 构建 External Bundle - 示例 2 和 构建 External Bundle - 示例 3),只需配置对应线程的 sectionPath:
lynx.config.ts
pluginExternalBundle({
externals: {
'lodash-es': {
url: 'http://cdn.example.com/lodash.lynx.bundle',
background: { sectionPath: 'lodash-es' },
},
},
});
lynx.config.ts
// 主线程
pluginExternalBundle({
externals: {
'lodash-es': {
url: 'http://cdn.example.com/lodash.lynx.bundle',
mainThread: { sectionPath: 'lodash-es' },
},
},
});