应只导出真正需外部调用的接口,封装内部逻辑与配置,通过具名导出、集中导出、类型定义隔离及tree-shaking优化,确保模块契约清晰、可维护且安全。

核心思路是:只导出真正需要被外部调用的接口,把内部逻辑、配置、工具函数全部封装在模块作用域内,不暴露给使用者。这样既防止误用,也便于后续重构而不影响下游。
明确导出边界,按职责分层声明
避免“全量导出”或随意添加 export。一个模块应有清晰的对外契约:
- 只对业务强依赖的函数、类、常量加 export,例如
export function initSDK()、export class Tracker - 配置项若需运行时覆盖,可导出一个 readonly 对象,但不要导出原始配置变量本身(防止被意外修改)
- 工具函数、类型定义、私有常量一律不导出,即使它们在同一个文件里
用命名空间或对象聚合导出,提升可读性与收敛性
比起散列导出,集中导出更利于维护和文档生成:
- 推荐写法:
export const SDK = { init, track, identify };,使用者统一通过SDK.init()调用 - 配合 TypeScript 的
declare module可进一步约束导出结构,避免 JS 中“导出但未定义”的隐性错误 - 当模块拆分变多时,可在入口文件(如
index.ts)中统一 re-export,形成稳定 API 表面
利用构建时 Tree-shaking 清理未使用导出
ESM 的静态导出特性让打包器能识别并剔除未被 import 的成员,但前提是导出必须是静态且无副作用的:
- 避免
export default () => {...}这类动态导出;优先用具名导出 +export default指向主入口 - 不要在导出语句中执行初始化逻辑(如
export const config = loadConfig()),应延迟到首次调用时执行 - 确认构建配置启用
sideEffects: false或精确列出有副作用的文件,否则 tree-shaking 会失效
配合类型定义隔离实现细节
即使 JS 层导出了某个对象,也可以通过 d.ts 文件隐藏其内部字段:
- 在
types/index.d.ts中只声明公共接口,比如export interface Tracker { track(event: string): void; } - 实际实现类可以包含私有属性、调试方法等,只要不在类型定义中暴露,TS 就不会允许外部访问
- 这样既保障了类型安全,又为内部迭代留出空间,维护成本自然下降

















