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

浏览器中使用ES模块并兼容ES5:解决dotenv解析报错及打包疑问

解决方案:必须用Webpack(或同类打包工具)处理

为什么直接在浏览器里用原生ES模块会报错?

浏览器原生ES模块只能识别带相对/绝对路径的导入(比如./utils.js、/src/xxx.js),无法解析dotenv、node-fetch这种裸模块标识符(npm包名)。这些包的代码本身是Node环境的,浏览器也无法直接运行,必须通过打包工具转译、合并成浏览器能识别的代码。

另外你的代码里还有两个浏览器不兼容的特性:

  • process.env是Node环境变量,浏览器没有这个全局对象
  • node-fetch是Node端的fetch实现,浏览器本身已经有原生fetch

用Webpack实现你的两个需求

下面是具体步骤,同时满足「浏览器可作为ES模块引入」和「兼容ES5支持require调用」:

1. 调整原代码

先修正代码里的浏览器不兼容部分:

// 去掉dotenv的导入,改用Webpack注入环境变量
// import * as dotenv from 'dotenv';
// dotenv.config();

// 浏览器有原生fetch,不需要node-fetch
// import fetch from 'node-fetch';

// 定义mockLocalStorage,避免未定义报错
const mockLocalStorage = {
  getItem: (key) => localStorage.getItem(key),
  setItem: (key, value) => localStorage.setItem(key, value)
};

const BASE_URL = process.env.BASE_URL || 'localhost:9000';
const myLocalStorage = process.env.NODE_ENV === 'development' ? mockLocalStorage : window.localStorage;

export async function getDeviceId() {
  const API_URL = `${BASE_URL}/get_device_id`;

  try {
    const response = await fetch(API_URL);
    const uuid = await response.json();

    if (uuid && uuid.length > 0) {
      console.log(uuid)
    } else {
      throw new Error('UUID is not available');
    }
  } catch (error) {
    console.error('Error fetching uuid:', error);
  }
}

2. 安装依赖

执行以下命令安装Webpack和相关工具:

npm install webpack webpack-cli babel-loader @babel/core @babel/preset-env dotenv-webpack --save-dev

3. 编写Webpack配置文件(webpack.config.js)

这个配置会同时生成两个版本的包:

  • ES模块版本(供浏览器用<script type="module">引入)
  • CommonJS版本(供Node环境用require调用),且转译为ES5兼容代码
const path = require('path');
const Dotenv = require('dotenv-webpack');
const webpack = require('webpack');

module.exports = [
  // 配置1:生成浏览器可用的ES模块
  {
    entry: './src/index.js',
    output: {
      filename: 'device-utils.esm.js',
      path: path.resolve(__dirname, 'dist'),
      library: {
        type: 'module'
      },
      environment: {
        arrowFunction: false // 可选:禁用箭头函数,更好兼容旧浏览器
      }
    },
    experiments: {
      outputModule: true
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                ['@babel/preset-env', {
                  targets: 'last 2 versions, not dead',
                  modules: false // 保持ES模块结构
                }]
              ]
            }
          }
        }
      ]
    },
    plugins: [
      new Dotenv(), // 加载根目录的.env文件
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      })
    ],
    mode: process.env.NODE_ENV || 'development'
  },
  // 配置2:生成兼容ES5的CommonJS模块(支持require)
  {
    entry: './src/index.js',
    output: {
      filename: 'device-utils.cjs.js',
      path: path.resolve(__dirname, 'dist'),
      library: {
        type: 'commonjs2'
      }
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                ['@babel/preset-env', {
                  targets: 'ie 11, last 2 versions',
                  modules: 'commonjs' // 转成CommonJS
                }]
              ]
            }
          }
        }
      ]
    },
    plugins: [
      new Dotenv(),
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
      })
    ],
    mode: process.env.NODE_ENV || 'development'
  }
];

4. 打包并使用

  • 执行打包命令:

    # 开发环境
    npm run build-dev # 需要在package.json的scripts里加"build-dev": "webpack --mode development"
    # 生产环境
    npm run build # 加"build": "webpack --mode production"
    
  • 浏览器端使用ES模块:

    <script type="module">
      import { getDeviceId } from '/dist/device-utils.esm.js';
      getDeviceId();
    </script>
    
  • Node环境使用require:

    const { getDeviceId } = require('./dist/device-utils.cjs.js');
    // 注意:Node环境调用getDeviceId会因为没有window和fetch报错,若要在Node用,需要重新引入node-fetch并处理localStorage
    

关键说明

  1. 环境变量处理:用dotenv-webpack加载.env文件,通过DefinePlugin注入到代码中,替代Node的process.env。
  2. ES5兼容:通过@babel/preset-env配置目标浏览器,将ES6+代码转译为ES5。
  3. 多模块输出:Webpack支持多配置数组,同时生成ES模块和CommonJS模块,满足不同场景需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:32:49