如何编写可同时在浏览器与Node.js环境运行的JS模块代码
无需构建工具的跨环境通用JS模块编写方案
核心思路
基于现行Web标准优先的原则,用同构导出的写法,同时兼容ES Module(浏览器、Node 12.20+版本)和CommonJS(旧版Node),全程不需要借助任何打包、编译类外部工具。
方案1:ES Module标准优先(推荐,符合现行规范)
Node 12.20及以上版本已经原生支持ES Module,和浏览器端的模块规范完全一致,是当前最推荐的写法:
- 项目根目录新建
package.json,添加配置"type": "module",Node就会默认把所有.js文件识别为ES Module - 导出统一用标准
export语法,仅增加一行CommonJS兜底兼容即可 - 浏览器端调试只需给script标签加
type="module"属性,用本地静态服务加载即可解决本地文件跨域问题,不需要部署到线上
模块代码示例(myModule.js)
// 标准ES Module导出,浏览器和新版Node直接可用 export const mymodule = { feature: function(){ return '功能调用成功' }, submodule: { property: 5 } } // 兼容旧版Node的CommonJS规范,不需要兼容旧版Node可删除 if (typeof module !== 'undefined' && module.exports) { module.exports = mymodule }
调用示例
Node环境
// 新版Node(配了"type": "module")直接用import导入 import { mymodule } from './myModule.js' console.log(mymodule.submodule.property) // 输出 5 // 旧版CommonJS环境用require导入 const mymodule = require('./myModule.js') console.log(mymodule.submodule.property) // 输出 5
浏览器环境
<!-- 添加type="module"属性即可识别ES Module --> <script type="module"> import { mymodule } from './myModule.js' console.log(mymodule.feature()) // 控制台输出 功能调用成功 </script>
本地调试只需要在项目目录下执行npx serve(Node自带工具,不需要额外安装),访问http://localhost:3000即可正常加载模块调试。
方案2:UMD封装(兼容旧用法,支持全局挂载)
如果你需要保留之前直接引入HTML、挂载全局变量的用法,可以用UMD模式封装,同时兼容所有主流模块规范和全局挂载场景:
(function (root, factory) { if (typeof module === 'object' && module.exports) { // 兼容Node CommonJS module.exports = factory(); } else if (typeof define === 'function' && define.amd) { // 兼容AMD规范,用不到可删除 define([], factory); } else { // 浏览器端直接挂载到全局,和你之前的开发习惯完全一致 root.mymodule = factory(); } }(typeof self !== 'undefined' ? self : this, function () { // 这里写你的模块逻辑 return { feature: function(){ return '功能调用成功' }, submodule: { property: 5 } } }));
这个模式下直接用<script src="myModule.js"></script>引入HTML后,直接在控制台访问mymodule即可使用,和你之前的开发方式完全兼容。
注意事项
- 模块内如果要用到环境专属API(比如浏览器的
window、Node的fs),先做环境判断再调用,避免跨环境报错:
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined' const isNode = typeof process !== 'undefined' && process.versions?.node if (isBrowser) { // 浏览器端专属逻辑 } else if (isNode) { // Node端专属逻辑 }
- 如果不需要兼容Node 12以下的旧版本,直接使用方案1即可,写法更简洁,完全符合现行Web标准,学习成本极低。
内容的提问来源于stack exchange,提问作者Franartur Čech
相关产品推荐
相关产品推荐

