You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何创建同时支持浏览器与Node.js的npm包?

优雅实现跨浏览器/Node.js的npm包方案

嘿,这个问题我太熟了——很多人刚开始写跨环境npm包的时候,都会写出一堆乱糟糟的环境判断代码,别担心,现在有非常成熟且干净的方案来解决!我分两种场景给你讲:纯手动实现(不用打包工具)和更推荐的工具链方案(优雅且易维护)。

一、纯手动实现:精简的UMD风格导出

如果你不想引入打包工具,想手动搞定跨环境导出,可以用**简化版UMD(通用模块定义)**逻辑,比你之前的写法干净很多,同时支持现代浏览器的ES模块和Node.js的CommonJS/ES模块:

// 核心功能代码,先写好你的业务逻辑
const myExport = {
  coreMethod() {
    return "搞定跨环境啦!";
  },
  helperFunc() {
    return "这是辅助方法";
  }
};

// 跨环境导出逻辑,只写这一段就够
(function (global, factory) {
  if (typeof module === "object" && typeof module.exports === "object") {
    // Node.js 环境,支持CommonJS导出
    module.exports = myExport;
    // 额外支持ES模块的default导出(兼容Node.js ES模块)
    if (typeof exports === "object") {
      exports.default = myExport;
      Object.assign(exports, myExport);
    }
  } else if (typeof define === "function" && define.amd) {
    // 兼容AMD模块(比如RequireJS)
    define([], factory);
  } else if (typeof global !== "undefined") {
    // 浏览器环境:挂载到window全局
    global.myExport = myExport;
    // 支持现代浏览器的ES模块导出
    if (typeof export !== "undefined") {
      export default myExport;
      export const { coreMethod, helperFunc } = myExport;
    }
  }
})(typeof window !== "undefined" ? window : globalThis, function () {
  return myExport;
});

这个写法的好处是:

  • 兼容Node.js的require()import
  • 兼容老浏览器的全局变量挂载
  • 兼容现代浏览器的ES模块导入
  • 甚至支持AMD模块加载器(虽然现在用的人不多,但聊胜于无)

二、更推荐:用打包工具生成多格式产物

如果你的包复杂度较高,或者想长期维护,用Rollup或Webpack打包是最优解——它们能帮你自动生成多种模块格式,不用手动写环境判断,代码更干净,用户体验也更好。这里以Rollup为例(它天生适合打包库,比Webpack更轻量):

步骤1:写ES模块风格的核心代码

先把你的业务逻辑写成标准ES模块,不用管环境导出:

// src/index.js
export const coreMethod = () => "搞定跨环境啦!";
export const helperFunc = () => "这是辅助方法";

// 也可以导出默认对象
export default {
  coreMethod,
  helperFunc
};

步骤2:配置Rollup生成多格式产物

  1. 安装依赖:
npm install rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs --save-dev
  1. 创建rollup.config.js配置文件:
export default {
  input: "src/index.js", // 你的源码入口
  output: [
    // 1. CommonJS格式:给Node.js的require()用
    {
      file: "dist/my-package.cjs.js",
      format: "cjs"
    },
    // 2. ES模块格式:给现代浏览器/Node.js的import用
    {
      file: "dist/my-package.esm.js",
      format: "es"
    },
    // 3. UMD格式:兼容老浏览器、全局变量、AMD模块
    {
      file: "dist/my-package.umd.js",
      format: "umd",
      name: "myExport" // 浏览器全局变量名
    }
  ],
  plugins: [
    // 处理第三方依赖的解析
    require("@rollup/plugin-node-resolve")(),
    // 把CommonJS依赖转成ES模块
    require("@rollup/plugin-commonjs")()
  ]
};

步骤3:在package.json里配置入口字段

让不同环境的工具自动识别正确的产物:

{
  "name": "my-package",
  "version": "1.0.0",
  "main": "dist/my-package.cjs.js", // Node.js CommonJS入口
  "module": "dist/my-package.esm.js", // ES模块入口(给webpack/Rollup等工具)
  "browser": "dist/my-package.umd.js", // 浏览器环境专用入口
  "scripts": {
    "build": "rollup -c" // 执行打包的命令
  }
}

这样用户怎么用你的包?

  • Node.js环境:
    // CommonJS
    const myExport = require("my-package");
    // ES模块(需要你的package.json加"type": "module")
    import myExport from "my-package";
    
  • 现代浏览器:
    <script type="module">
      import myExport from "./dist/my-package.esm.js";
      console.log(myExport.coreMethod());
    </script>
    
  • 老浏览器:
    <script src="./dist/my-package.umd.js"></script>
    <script>
      console.log(window.myExport.coreMethod());
    </script>
    

额外小技巧

  • 如果不需要兼容老环境,甚至可以只提供ES模块格式,现在Node.js已经原生支持ES模块(只要package.json里加"type": "module"),现代浏览器也支持<script type="module">,代码会更简洁。
  • 测试跨环境可以用Jest + jsdom,既能测试Node.js逻辑,也能模拟浏览器环境测试全局变量挂载。

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

火山引擎 最新活动