You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js服务端无法条件使用export 如何实现前后端代码共享

问题背景

我有一个名为 global-namespaces.js 的模块文件,其中包含大量常量与若干基础函数。目前我正尝试升级客户端侧代码,使其支持modules与import语句。顺带说明,我知道下方代码写法看起来较为别扭,这是因为我目前仍在摸索可行的解决方案。

服务端侧我原本直接使用*require()*引入模块,但始终找不到合适的方式从global-namespaces.js中导出所需符号,使其在客户端上下文可用的同时不会在服务端触发语法错误。我无法将export关键字或导出块放置在IF判断语句内,该写法会直接触发语法错误。以下是我当前的代码:

try {
    g_GlobalNamespaces = new GlobalNamespaces();
} catch(err) {
    console.error(`${errPrefix}Error occurred during the creation of the global GlobalNamespaces object.`);
    console.info(errPrefix + `err object:`);
    console.dir(err, {depth: null, colors: true});
}

// 同时适配客户端和服务端逻辑,判断当前是否为服务端环境
if (typeof module == 'undefined' || typeof module.exports == 'undefined')
{
    // 客户端环境:g_GlobalNamespaces 已经挂载到全局命名空间
} else {
    // 服务端环境:按照CommonJS规范导出,适配require()调用
    module.exports =
        {
            g_GlobalNamespaces: g_GlobalNamespaces
        }
}

// 尝试导出适配import语句,这行代码在服务端会直接触发语法错误
export
{
    g_GlobalNamespaces
}

我查阅的相关技术帖子中,一则尚无有效回答,主题为「Node.js下如何实现客户端和服务端代码共享」;另一则仅处理纯客户端侧的条件导出场景,无法解决前后端代码共享的问题,主题为「ES2015中的条件导出实现」。

核心诉求:如何组织global-namespaces.js的代码结构,才能让这份代码同时兼容服务端与客户端的运行环境?

解决方案

触发语法错误的核心原因是ES Module的export属于静态声明语法,必须写在模块顶层,无法被条件判断语句包裹。CommonJS环境在解析代码阶段遇到顶层的export关键字就会直接抛出语法错误,根本不会执行后续的条件判断逻辑。可以根据项目实际情况选择以下两种改造方案:

方案1:UMD同构写法(单文件兼容所有环境,无需额外构建工具)

直接在文件中检测当前运行时的模块规范,统一封装导出逻辑,从根源上避免非ESM环境遇到export关键字触发错误:

// 先完成核心逻辑初始化
try {
    var g_GlobalNamespaces = new GlobalNamespaces();
} catch(err) {
    console.error(`${errPrefix}Error occurred during the creation of the global GlobalNamespaces object.`);
    console.info(errPrefix + `err object:`);
    console.dir(err, {depth: null, colors: true});
}

// 多环境导出适配
(function (root, factory) {
    // 适配Node.js/CommonJS环境(服务端require()调用)
    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = factory();
    } 
    // 适配AMD加载器环境(可选,无相关需求可直接删除该分支)
    else if (typeof define === 'function' && define.amd) {
        define(factory);
    } 
    // 适配浏览器全局环境(客户端script标签直接引入)
    else {
        root.g_GlobalNamespaces = factory().g_GlobalNamespaces;
    }
})(typeof self !== 'undefined' ? self : this, function() {
    // 统一返回所有需要对外暴露的变量、方法
    return {
        g_GlobalNamespaces: g_GlobalNamespaces
    };
});

这种写法不需要任何编译步骤,单文件可直接在三类场景下运行:服务端require()引入、浏览器端<script>标签直接引入、打包工具处理ESMimport引入,不会出现语法报错。

方案2:统一ESM规范+多产物构建

如果你的服务端运行环境为Node.js 14及以上版本(已原生支持ESM规范),可以直接把项目模块统一改造为ESM规范:

  • 将文件后缀改为.mjs,或者在项目package.json中添加"type": "module"配置
  • 移除原代码中module.exports相关的CommonJS导出逻辑,直接使用顶层export语法做导出
  • 浏览器端引入脚本时给<script>标签添加type="module"属性即可
    如果需要兼容旧版Node.js的CommonJSrequire()调用,可以使用esbuild、Rollup等构建工具,在构建环节同时输出CommonJS和ESM两份产物,分别对应服务端和客户端场景使用。

注意:任何将export写在条件判断、函数内部等非顶层位置的写法都不符合ESM规范,会直接触发语法错误,和运行环境无关,不要尝试这类写法。


内容的提问来源于stack exchange,提问作者Robert Oschler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 00:12:53