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

使用Webpack预渲染React到HTML模板时遇window未定义错误求助

解决Webpack+React预渲染时的window is not defined错误

这个问题我之前也碰到过,核心是预渲染代码在Node.js环境执行,但组件里访问了浏览器独有的window对象,再加上你在EJS模板里直接require预渲染模块的方式也有点问题,咱们一步步来解决:

为什么会报错?

当HtmlWebpackPlugin处理你的EJS模板时,它是在Node.js(服务端)环境中运行的,而不是浏览器环境:

  1. 你的App组件里可能有直接在顶层访问window的代码(比如const width = window.innerWidth),Node环境没有这个全局对象,直接报错。
  2. 你在EJS模板里写<%= require('../appPrerender.js') %>,相当于让Node直接执行这个模块,没有经过Webpack的客户端打包处理,组件里的浏览器代码直接在服务端跑,自然出问题。

解决方案1:修复组件中的window访问逻辑

先检查你的App.js,如果有直接在组件顶层使用window的代码,改成组件挂载后再访问,或者做环境判断:

示例:用React Hooks处理浏览器API

// client/app.js
import { useEffect, useState } from 'react';

function App() {
  const [windowWidth, setWindowWidth] = useState(0);

  useEffect(() => {
    // 组件挂载到浏览器后,再访问window
    const updateWidth = () => setWindowWidth(window.innerWidth);
    updateWidth();
    // 监听窗口变化
    window.addEventListener('resize', updateWidth);
    // 组件卸载时清理监听
    return () => window.removeEventListener('resize', updateWidth);
  }, []);

  return <div>当前窗口宽度:{windowWidth}</div>;
}

export default App;

示例:环境判断兼容服务端

如果必须在组件顶层使用,加个环境判断:

// client/app.js
const initialWidth = typeof window !== 'undefined' ? window.innerWidth : 0;

function App() {
  const [width, setWidth] = useState(initialWidth);
  // ...后续逻辑
}

解决方案2:调整预渲染的执行方式(推荐)

不要在EJS模板里直接require预渲染模块,换一种方式把预渲染内容注入到HTML中:

方法A:在Webpack配置中直接生成预渲染内容

修改你的webpack.common.js,让HtmlWebpackPlugin直接调用预渲染逻辑,而不是依赖EJS模板里的require:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
// 注意这里要正确引入你的App组件(如果用ES模块导出,要加.default)
const App = require('./client/app').default;

module.exports = {
  // ...其他Webpack配置
  plugins: [
    new HtmlWebpackPlugin({
      templateContent: () => {
        // 在Node环境中执行预渲染
        const prerenderedHtml = ReactDOMServer.renderToString(React.createElement(App));
        // 返回完整的HTML模板
        return `
<!DOCTYPE html>
<html>
<head>
<title>Anon's Website</title>
</head>
<body>
<div id="root">${prerenderedHtml}</div>
</body>
</html>
        `.trim();
      }
    })
  ]
};

方法B:单独预渲染生成静态片段

如果你的预渲染逻辑比较复杂,可以单独写脚本生成静态HTML,再让模板读取:

  1. 创建prerender.js脚本:
// 项目根目录/prerender.js
const fs = require('fs');
const path = require('path');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const App = require('./client/app').default;

// 生成预渲染HTML
const prerenderedHtml = ReactDOMServer.renderToString(React.createElement(App));
// 写入静态文件
fs.writeFileSync(path.join(__dirname, 'client/public/prerendered.html'), prerenderedHtml);
  1. 修改package.json的脚本,先预渲染再打包:
{
  "scripts": {
    "prerender": "node prerender.js",
    "build": "npm run prerender && webpack --config webpack.common.js"
  }
}
  1. 修改EJS模板,读取生成的静态文件:
<!DOCTYPE html>
<html>
<head>
<title>Anon's Website</title>
</head>
<body>
<div id="root"><%= require('fs').readFileSync(path.join(__dirname, '../client/public/prerendered.html'), 'utf8') %></div>
</body>
</html>

额外注意点

  • 确保你的React组件是同构友好的:避免在组件顶层使用浏览器特有的API(window、document、localStorage等),尽量在组件生命周期或Hooks中使用。
  • 如果用了第三方库,要确认它们支持服务端渲染,有些库可能需要额外配置(比如styled-components需要用babel-plugin-styled-components支持SSR)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:51:40