导出受限包的核心是精准控制暴露边界,只共享稳定、有契约的数据或能力。通过函数封装读写逻辑、Object.freeze()冻结配置、Go中私有字段配getter、分层导出(SDK/内部/测试)、闭包工厂替代全局变量、同构环境按需加载等方式实现安全共享。

导出受限包不是为了“共享所有变量”,而是通过精准控制暴露边界,让模块间只共享真正需要的、稳定的、有契约的数据或能力。核心在于“限制”二字——限制什么能出、谁可以拿、怎么用才安全。
明确导出意图:只暴露接口,不暴露状态
变量共享≠把内部变量全扔出去。高级共享的关键是提供可组合、可测试、不可误改的访问方式。
- 用函数封装读写逻辑,而不是直接导出可变变量(如避免
export let counter = 0;改用export const getCounter = () => count和export const incCounter = () => { count++ }) - 导出常量或配置对象时,用
Object.freeze()冻结,防止外部篡改 - 在 Go 中,不导出字段本身,而是导出 getter 方法(如
func (c Config) Timeout() time.Duration),把结构体字段设为小写私有
按场景分层导出:区分“供调用”和“供继承”
同一模块对不同使用者应提供不同粒度的出口。例如:
-
对外 SDK 层:只导出稳定 API 函数与类型定义(如
export interface RequestConfig+export function sendRequest()) -
同组织内部扩展层:额外导出抽象类或 hook 工厂(如
export function createCustomTransport()),但不暴露底层网络实例 -
测试专用层:仅在测试环境下导出模拟入口(如通过条件导出
export const __test__resetState = () => {...},生产构建自动剔除)
用闭包+工厂模式替代全局变量共享
当多个模块需共用一套运行时状态(如用户会话、缓存池),不要导出裸变量,而应导出一个受控工厂:
- 定义模块内私有状态:
const sessions = new Map() - 导出单例访问器:
export const getSessionStore = () => sessions(返回只读引用)或更安全的export const withSession = (id, cb) => { const s = sessions.get(id); return s ? cb(s) : null } - 配合 TypeScript 的
readonly类型或as const提升类型安全性
跨环境兼容导出:Node.js 与浏览器统一收口
若模块需同构运行(如工具库),导出逻辑必须屏蔽环境差异:
- ES 模块中统一使用
export default导出主对象,内部根据typeof window !== 'undefined'或process?.env?.NODE_ENV动态挂载能力 - 避免在顶层导出依赖环境的变量(如
export const fs = require('fs')),改用按需加载函数:export async function readConfig() { if (isNode()) return import('fs').then(m => m.readFileSync(...)) } - 构建时用 Rollup/Vite 的
tree-shaking自动剔除未被引用的环境分支代码

















