模块链接
模块链接(Module Linking)是 Esmx 提供的一种跨应用代码共享方案。它基于浏览器原生的 ESM(ECMAScript Modules)标准,让多个应用可以共享代码模块,无需额外的运行时库。
核心优势
- 零运行时开销:直接使用浏览器原生 ESM 加载器,不引入任何代理或包装层
- 高效共享:通过导入映射(Import Maps)在编译时解析依赖,运行时直接加载
- 版本隔离:支持不同应用使用不同版本的同一模块,避免冲突
- 简单易用:配置直观,与原生 ESM 语法完全兼容
简单来说,模块链接就是一个"模块共享管理器",让不同的应用可以安全、高效地共享代码,就像使用本地模块一样简单。
快速开始
基础示例
假设我们有一个共享模块应用(shared-modules)和一个业务应用(business-app):
// shared-modules/entry.node.ts - 提供共享模块
export default {
modules: {
exports: [
'pkg:axios', // 共享 HTTP 客户端
'root:src/utils/format.ts' // 共享工具函数
]
}
} satisfies EsmxOptions;
// business-app/entry.node.ts - 使用共享模块
export default {
modules: {
links: { 'shared-modules': '../shared-modules/dist' },
imports: { 'axios': 'shared-modules/axios' }
}
} satisfies EsmxOptions;
在业务应用中使用:
// business-app/src/api/orders.ts
import axios from 'axios'; // 使用共享的 axios 实例
import { formatDate } from 'shared-modules/src/utils/format'; // 使用共享工具函数
export async function fetchOrders() {
const response = await axios.get('/api/orders');
return response.data.map(order => ({
...order,
date: formatDate(order.createdAt)
}));
}
核心配置
模块链接配置位于 entry.node.ts
文件的 modules
字段内,包含四个核心配置项:
模块链接 (links)
links
配置指定当前模块链接到其他模块的路径:
// business-app/entry.node.ts
import type { EsmxOptions } from '@esmx/core';
export default {
modules: {
links: {
'shared-modules': '../shared-modules/dist', // 相对路径
'api-utils': '/var/www/api-utils/dist' // 绝对路径
}
}
} satisfies EsmxOptions;
模块导入 (imports)
imports
配置将本地模块名映射到远程模块标识符,支持标准导入和环境特定配置:
// business-app/entry.node.ts
export default {
modules: {
links: {
'shared-modules': '../shared-modules/dist'
},
imports: {
// 标准导入映射
'axios': 'shared-modules/axios',
'lodash': 'shared-modules/lodash',
// 环境特定配置
'storage': {
client: 'shared-modules/storage/client',
server: 'shared-modules/storage/server'
}
}
}
} satisfies EsmxOptions;
范围映射 (scopes)
scopes
配置为特定目录范围或包范围定义导入映射,实现版本隔离和依赖替换。支持目录范围映射和包范围映射两种类型。
目录范围映射
目录范围映射只影响特定目录下的模块导入,实现不同目录间的版本隔离。
以下示例展示了如何使用 scopes
配置为 vue2/
目录下的模块指定不同的 Vue 版本:
// shared-modules/entry.node.ts
export default {
modules: {
scopes: {
// Vue2 目录范围映射:只影响 vue2/ 目录下的导入
// 业务代码在 vue2/ 目录:import Vue from 'vue' → shared-modules/vue2 (版本隔离)
// 业务代码在其他目录:import Vue from 'vue' → Vue 3 (通用模块)
'vue2/': {
'vue': 'shared-modules/vue2',
'vue-router': 'shared-modules/vue2-router'
}
}
}
} satisfies EsmxOptions;
包范围映射
包范围映射影响特定包内部的依赖解析,用于依赖替换和版本管理:
// shared-modules/entry.node.ts
export default {
modules: {
scopes: {
// 包范围映射:影响 vue 包内部的依赖解析
// 当 vue 包依赖 @vue/shared 时,使用指定的替换版本
'vue': {
'@vue/shared': 'shared-modules/@vue/shared'
}
}
}
} satisfies EsmxOptions;
模块导出 (exports)
exports
配置定义模块向外提供的内容,仅支持数组格式:
// shared-modules/entry.node.ts
export default {
modules: {
exports: [
// npm包:保持原始导入路径
'pkg:axios', // 导入时: import axios from 'axios'
'pkg:lodash', // 导入时: import { debounce } from 'lodash'
// 源码模块:自动重写为模块路径
'root:src/utils/date-utils.ts', // 导入时: import { formatDate } from 'shared-modules/src/utils/date-utils'
'root:src/components/Chart.js', // 导入时: import Chart from 'shared-modules/src/components/Chart'
// 对象形式 - 复杂配置
{
'api': './src/api.ts', // 简单映射
'store': './src/store.ts' // 字符串值
}
]
}
} satisfies EsmxOptions;
前缀处理说明:
pkg:axios
→ 保持原始包名导入,适用于第三方 npm 包
root:src/utils/date-utils.ts
→ 转换为模块相对路径,适用于项目内部源码模块
文件扩展名支持:对于 root:
前缀的配置,支持 .js
, .mjs
, .cjs
, .jsx
, .mjsx
, .cjsx
, .ts
, .mts
, .cts
, .tsx
, .mtsx
, .ctsx
等扩展名,配置时会自动去除扩展名。对于 pkg:
前缀和普通字符串配置,不会去除扩展名。
高级配置
环境差异化构建
exports: [
{
'src/storage/db': {
client: './src/storage/indexedDB', // 客户端使用 IndexedDB
server: './src/storage/mongoAdapter' // 服务端使用 MongoDB 适配器
}
},
{
'src/client-only': {
client: './src/client-feature', // 仅客户端可用
server: false // 服务端不会构建
}
}
]
混合配置格式
exports: [
'pkg:axios',
'root:src/utils/format.ts',
{
'api': './src/api/index.ts'
},
{
'components': './src/components/index.ts'
},
{
'storage': {
client: './src/storage/browser.ts',
server: './src/storage/node.ts'
}
}
]
完整示例
基于实际项目结构的完整示例:
共享模块 (shared-modules)
// shared-modules/entry.node.ts
import type { EsmxOptions } from '@esmx/core';
export default {
modules: {
exports: [
'pkg:@esmx/router',
{
vue: 'pkg:vue/dist/vue.runtime.esm-browser.js',
'@esmx/router-vue': 'pkg:@esmx/router-vue',
vue2: 'pkg:vue2/dist/vue.runtime.esm.js',
'vue2/@esmx/router-vue': 'pkg:@esmx/router-vue'
}
],
scopes: {
'vue2/': {
vue: 'shared-modules/vue2'
}
}
}
} satisfies EsmxOptions;
Vue3 应用 (vue3-app)
// vue3-app/entry.node.ts
import type { EsmxOptions } from '@esmx/core';
export default {
modules: {
links: {
'shared-modules': '../shared-modules/dist'
},
imports: {
'vue': 'shared-modules/vue',
'@esmx/router': 'shared-modules/@esmx/router',
'@esmx/router-vue': 'shared-modules/@esmx/router-vue'
},
exports: [
'root:src/routes.ts'
]
}
} satisfies EsmxOptions;
Vue2 应用 (vue2-app)
// vue2-app/entry.node.ts
import type { EsmxOptions } from '@esmx/core';
export default {
modules: {
links: {
'shared-modules': '../shared-modules/dist'
},
imports: {
'vue': 'shared-modules/vue2',
'@esmx/router': 'shared-modules/vue2/@esmx/router',
'@esmx/router-vue': 'shared-modules/vue2/@esmx/router-vue'
},
exports: [
'root:src/routes.ts'
]
}
} satisfies EsmxOptions;
聚合应用 (business-app)
// business-app/entry.node.ts
import type { EsmxOptions } from '@esmx/core';
export default {
modules: {
links: {
'shared-modules': '../shared-modules/dist',
'vue2-app': '../vue2-app/dist',
'vue3-app': '../vue3-app/dist'
},
imports: {
'@esmx/router': 'shared-modules/vue2/@esmx/router'
}
}
} satisfies EsmxOptions;
这种配置方式展示了:
- 共享模块:提供多版本框架支持,通过作用域映射实现版本隔离
- Vue3 应用:使用 Vue 3 的业务应用,只导出路由配置
- Vue2 专用应用:专门使用 Vue 2 的业务应用,只导出路由配置
- 聚合应用:统一入口,协调不同版本的子应用,包含完整的Vue模块导入
每个模块的配置都符合实际项目的使用场景,依赖关系清晰,功能职责明确,技术实现准确。