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

Webpack打包React应用中Window未定义错误排查求助

问题:React组件库prism在Web应用beacon中使用时出现window未定义错误

我用Rollup打包了名为prism的React组件库,在Webpack打包的React Web应用beacon中引入该组件库后,运行应用时控制台抛出以下错误:

VM5253 main.js:1139 Uncaught TypeError: Cannot read properties of undefined (reading 'requestAnimationFrame')
    at eval (VM5329 index.js:31:477331)
    at eval (VM5329 index.js:31:477487)
    at prism/dist/esm/index.js (VM5253 main.js:1107:1)
    at __webpack_require__ (VM5253 main.js:1136:33)
    at fn (VM5253 main.js:1378:21)
    at eval (VM5328 Models.js:7:70)
    at ./src/components/views/Models.js (VM5253 main.js:118:1)
    at __webpack_require__ (VM5253 main.js:1136:33)
    at fn (VM5253 main.js:1378:21)
    at eval (VM5323 Router.js:10:75)

我推测错误源于prism依赖的react-dimensions调用了window.requestAnimationFrame,但不清楚为什么window变量会未定义。目前发现将react-dimensions设为prism的peer依赖并在beacon中单独安装可以解决问题,但我不想采用这种方式,希望找到其他解决方案。


相关配置文件

Prism(React组件库)

package.json

{"name": "prism","version": "1.0.4","description": "","type": "module","main": "dist/cjs/index.js","module": "dist/esm/index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "rollup -c"},"author": "","license": "ISC","devDependencies": {"@babel/cli": "^7.23.4","@babel/core": "^7.23.7","@babel/preset-env": "^7.23.8","@babel/preset-react": "^7.23.3","@rollup/plugin-babel": "^6.0.4","@rollup/plugin-commonjs": "^25.0.7","@rollup/plugin-image": "^3.0.3","@rollup/plugin-node-resolve": "^15.2.3","@rollup/plugin-terser": "^0.4.4","@rollup/plugin-typescript": "^11.1.6","@svgr/webpack": "^8.1.0","babel-plugin-add-module-exports": "^1.0.4","rollup": "^4.9.5","rollup-plugin-dts": "^6.1.0","rollup-plugin-node-polyfills": "^0.2.1","rollup-plugin-peer-deps-external": "^2.2.4","rollup-plugin-postcss": "^4.0.2"},"dependencies": {"@rollup/plugin-json": "^6.1.0","axios": "^1.6.5","babel-loader": "^9.1.3","fast-memoize": "^2.5.2","global": "^4.4.0","luxon": "^3.4.4","nodemon": "^3.0.2","react-grid-layout": "^1.4.4","rollup-plugin-inject": "^3.0.2","svg-inline-loader": "^0.8.2","victory": "^36.8.1","react-dimensions": "^1.3.1"},"peerDependencies": {"react": "^18.2.0","react-dom": "^18.2.0"}}

rollup.config.js

import terser from '@rollup/plugin-terser';import resolve from "@rollup/plugin-node-resolve";import commonjs from "@rollup/plugin-commonjs";import { babel } from '@rollup/plugin-babel';import peerDepsExternal from 'rollup-plugin-peer-deps-external';import image from '@rollup/plugin-image';import json from '@rollup/plugin-json';import packageJson from "./package.json" assert { type: "json" };export default {input: "src/index.js",output: [{file: packageJson.main,format: "cjs",sourcemap: "inline",},{file: packageJson.module,format: "esm",sourcemap: "inline",},],plugins: [peerDepsExternal(),commonjs({ include: /node_modules/ }),babel({ babelHelpers: 'bundled' }),terser(),json(),image(),resolve({browser: true}),],};

.babelrc

{"presets": ["@babel/preset-react","@babel/preset-env"]}

Beacon(React Web应用)

package.json

{"name": "beacon","version": "0.1.0","private": true,"main": "./src/index.js","dependencies": {"prism": "1.0.2","dotenv-webpack": "^8.0.1","html-webpack-plugin": "^5.6.0","lodash": "^4.17.21","react": "^18.2.0","react-dom": "^18.2.0","react-redux": "^9.1.0","react-router-dom": "^6.21.2","serve": "^14.2.1"},"scripts": {"start": "serve -s public/ -l 3000","dev": "NODE_ENV=development webpack-dev-server .","build": "webpack"},"devDependencies": {"@babel/cli": "^7.23.4","@babel/core": "^7.23.7","@babel/eslint-parser": "^7.23.3","@babel/plugin-transform-runtime": "^7.23.7","@babel/preset-env": "^7.23.8","@babel/preset-react": "^7.23.3","@babel/runtime": "^7.23.8","@svgr/webpack": "^8.1.0","babel-loader": "^9.1.3","css-loader": "^6.9.0","node-polyfill-webpack-plugin": "^3.0.0","style-loader": "^3.3.4","webpack": "^5.89.0","webpack-cli": "^5.1.4","webpack-dev-server": "^4.15.1"}}

webpack.config.js

const path = require("path");const HtmlWebpackPlugin = require('html-webpack-plugin');const Dotenv = require('dotenv-webpack');const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")module.exports = {mode: "development",entry: "./src/index.js",output: {path: path.resolve(__dirname, "public"),filename: "main.js",publicPath: "/",},target: "web",devServer: {port: "3000",static: ["./public"],open: true,hot: true,liveReload: true,historyApiFallback: true,},resolve: {extensions: ['.js', '.jsx', '.json'],alias: {'react': path.resolve(__dirname, 'node_modules/react'),'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),},fallback: {"fs": false,"tls": false,"net": false,"path": false,"zlib": false,"http": false,"https": false,"stream": false,"crypto": false,"util": false,"url": false,"assert": false,"tty": false,"os": false,}},module: {rules: [{test: /\.(js|jsx)$/,exclude: /node_modules/,loader: 'babel-loader',options: {presets: ['@babel/env', '@babel/preset-react']},},{test: /\.svg$/,use: ['@svgr/webpack'],},{test: /\.css$/,use: ['style-loader','css-loader']}]},plugins: [new NodePolyfillPlugin(),new HtmlWebpackPlugin({template: './public/index.html',}),new Dotenv(".env.development"),],}

解决方案

1. 在Rollup配置中注入全局window对象

修改prism的rollup.config.js,引入并配置rollup-plugin-inject,将window注入到依赖模块中:

import terser from '@rollup/plugin-terser';
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import { babel } from '@rollup/plugin-babel';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import image from '@rollup/plugin-image';
import json from '@rollup/plugin-json';
import inject from 'rollup-plugin-inject';
import packageJson from "./package.json" assert { type: "json" };

export default {
  input: "src/index.js",
  output: [
    {
      file: packageJson.main,
      format: "cjs",
      sourcemap: "inline",
    },
    {
      file: packageJson.module,
      format: "esm",
      sourcemap: "inline",
    },
  ],
  plugins: [
    peerDepsExternal(),
    commonjs({ include: /node_modules/ }),
    babel({ babelHelpers: 'bundled' }),
    terser(),
    json(),
    image(),
    resolve({browser: true}),
    inject({
      window: 'global/window'
    })
  ],
};

该配置会让Rollup在打包时,将依赖中对window的引用替换为global/window,确保在浏览器环境下能正确识别全局对象。

2. 调整Babel预设,明确目标环境为浏览器

修改prism的.babelrc,给@babel/preset-env指定浏览器目标,避免编译出不兼容的代码:

{
  "presets": [
    "@babel/preset-react",
    ["@babel/preset-env", {
      "targets": "> 0.25%, not dead",
      "modules": false
    }]
  ]
}

3. 让Webpack转译组件库代码

修改beacon的webpack.config.js中的babel-loader规则,取消对prism的排除,让Webpack转译组件库的代码,确保window在浏览器环境下被正确处理:

{
  test: /\.(js|jsx)$/,
  exclude: /node_modules\/(?!prism)/,
  loader: 'babel-loader',
  options: {
    presets: ['@babel/env', '@babel/preset-react']
  }
}

4. 确保组件在客户端渲染时才执行

如果prism中的组件存在服务端渲染场景,可在组件中通过环境判断,确保react-dimensions相关代码只在浏览器中执行:

import React, { useEffect, useState } from 'react';

const MyComponent = () => {
  const [isClient, setIsClient] = useState(false);

  useEffect(() => {
    setIsClient(true);
  }, []);

  if (!isClient) return null;

  const DimensionedComponent = require('react-dimensions')(SomeComponent);
  return <DimensionedComponent />;
};

export default MyComponent;

原因说明

当react-dimensions被打包进prism时,Rollup默认不会自动注入浏览器全局对象window,导致代码在Webpack打包后,window被视为未定义变量。而将其设为peer依赖时,react-dimensions由beacon的Webpack处理,Webpack的target: "web"配置会自动处理全局对象,因此不会报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 05:54:56