如何在utils.js中按需条件导出DOM相关工具函数
问题背景
我有一个大型工具模块utils.js,包含原生JS导出和DOM相关导出(例如使用Canvas)。希望在浏览器主线程外的环境(如Worker、Deno)中避免导入DOM工具函数。
曾尝试仅在浏览器主线程导出DOM函数,但因导出必须为顶层语句,if( inDom ) export function ...会报错。用try-catch包裹代码在Deno中也无法正常工作,因为Deno会在加载时检查导入,与浏览器不同。
于是我将utils.js拆分为jsUtils.js和domUtils.js,希望保留utils.js作为入口文件,始终导出jsUtils.js的内容,仅在浏览器主线程条件导出domUtils.js的内容。
本以为可以像这样实现:
const inDom = typeof document !== undefined export * from './jsUtils.js' if( inDom ) export * from './domUtils.js'
但显然这会失败,因为第二个export不是顶层语句。
我的问题是:如何实现一个始终包含jsUtils.js且仅在inDom条件下包含domUtils.js的utils.js?
可行解决方案
1. 动态导入+导出合并(异步场景)
利用ES模块的动态导入特性,将DOM工具的导出合并到当前模块。这种方式适合异步使用DOM工具的场景:
// 始终导出原生JS工具 export * from './jsUtils.js'; const inDom = typeof document !== 'undefined'; if (inDom) { // 异步加载DOM工具并合并到导出对象 (async () => { const domUtils = await import('./domUtils.js'); Object.assign(exports, domUtils); })(); }
注意:DOM相关导出是异步可用的,同步依赖会导致获取不到。
2. 顶层await同步加载(ES2022+环境)
ES2022支持的顶层await可以实现同步条件导入,确保DOM导出在模块加载完成后可用:
export * from './jsUtils.js'; const inDom = typeof document !== 'undefined'; if (inDom) { const domUtils = await import('./domUtils.js'); // 批量将DOM工具的导出挂载到当前模块 for (const key in domUtils) { if (Object.prototype.hasOwnProperty.call(domUtils, key)) { exports[key] = domUtils[key]; } } }
需要环境支持顶层await(现代浏览器、Deno 1.20+、Node.js 14.8+等)。
3. CommonJS兼容写法(混合模块环境)
如果环境支持CommonJS或混合模块,可以用同步的方式合并导出:
// 导入并导出原生JS工具 const jsUtils = require('./jsUtils.js'); module.exports = { ...jsUtils }; const inDom = typeof document !== 'undefined'; if (inDom) { // 条件导入DOM工具并合并导出 const domUtils = require('./domUtils.js'); Object.assign(module.exports, domUtils); }
这种方式兼容性好,所有导出都是同步可用的。
4. package.json条件导出(npm包场景)
如果是npm包项目,可以通过package.json的条件导出配置,让运行时或打包工具自动选择对应环境的入口:
首先在package.json中添加:
{ "exports": { ".": { "browser": "./utils.browser.js", "default": "./utils.default.js" } } }
然后创建两个入口文件:
utils.browser.js(浏览器主线程用):
export * from './jsUtils.js'; export * from './domUtils.js';
utils.default.js(非浏览器主线程用):
export * from './jsUtils.js';
这种方式无需在代码中做环境判断,是最优雅的方案之一。
内容的提问来源于stack exchange,提问作者backspaces

