如何解决 React 和 Next.js 中的“窗口未定义”错误

2025-05-28

如何解决 React 和 Next.js 中的“窗口未定义”错误

Next.js 是一个具有预渲染功能的 React 框架。这意味着对于每个页面,Next.js 都会尝试生成该页面的 HTML,以获得更好的 SEO 和性能。

这就是为什么,如果你想这样做:

// components/Scroll.js
window.addEventListener("scroll", function() {
  console.log("scroll!")
});
Enter fullscreen mode Exit fullscreen mode

然后它将失败并显示“ReferenceError:窗口未定义”:

反应错误

因为在Node.js的世界里,window是没有定义的,window只在浏览器中可用。

有三种方法可以解决这个问题:

1. 第一个解决方案:typeof

虽然您不能使用:

if (window !== undefined) {
  // browser code
}
Enter fullscreen mode Exit fullscreen mode

因为这会尝试将不存在的变量(window)与 undefined 进行比较,从而导致严重的“ReferenceError: window is notdefined”。你仍然可以使用:

if (typeof window !== "undefined") {
  // browser code
}
Enter fullscreen mode Exit fullscreen mode

因为 typeof 不会尝试评估“window”,它只会尝试获取其类型,在我们的 Node.js 例子中为:“undefined”。

附言:感谢
Rogier Nitschelm
提醒我这一点。我最初尝试过,if (typeof window !== undefined)但由于前面提到的原因,最终失败了。

下面的其他解决方案更加奇特,但仍然值得。

2. 第二种解决方案:useEffect hook

解决这个问题的“React”方法是使用useEffect React hook。它只在渲染阶段运行,因此不会在服务器上运行。

让我们更新我们的 scroll.js 组件:

// components/Scroll.js

import React, { useEffect } from "react";

export default function Scroll() {
  useEffect(function mount() {
    function onScroll() {
      console.log("scroll!");
    }

    window.addEventListener("scroll", onScroll);

    return function unMount() {
      window.removeEventListener("scroll", onScroll);
    };
  });

  return null;
}
Enter fullscreen mode Exit fullscreen mode

我们在这里所做的是将我们的初始 JavaScript 文件转换为真正的 React 组件,然后需要通过以下方式将其添加到您的 React 树中:

// pages/index.js

import Scroll from "../components/Scroll";

export default function Home() {
  return (
    <div style={{ minHeight: "1000px" }}>
      <h1>Home</h1>
      <Scroll />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

提示:示例中我们使用 useEffect 的方式是在挂载/卸载时注册和注销监听器。但您也可以只在挂载时注册,并忽略其他渲染事件,具体操作如下:

// components/Scroll.js

import React, { useEffect } from "react";

export default function Scroll() {
  useEffect(function onFirstMount() {
    function onScroll() {
      console.log("scroll!");
    }

    window.addEventListener("scroll", onScroll);
  }, []); // empty dependencies array means "run this once on first mount"

  return null;
}
Enter fullscreen mode Exit fullscreen mode

3.第三种方案:动态加载

另一种解决方案是使用动态导入和选项来加载你的 Scroll 组件srr: false。这样,你的组件根本不会在服务器端渲染。

当您导入依赖于 的外部模块时,此解决方案特别有效window(感谢Justin!)

// components/Scroll.js

function onScroll() {
  console.log("scroll!");
}

window.addEventListener("scroll", onScroll);

export default function Scroll() {
  return null;
}
Enter fullscreen mode Exit fullscreen mode
// pages/index.js

import dynamic from "next/dynamic";

const Scroll = dynamic(
  () => {
    return import("../components/Scroll");
  },
  { ssr: false }
);

export default function Home() {
  return (
    <div style={{ minHeight: "1000px" }}>
      <h1>Home</h1>
      <Scroll />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

如果您不需要 useEffect 的功能,您甚至可以完全删除它的使用,如下所示。

最后,如果您想要全局加载一个组件并忘记它(页面更改时不再挂载/卸载),那么您也可以仅在_app.jsScroll中加载您的组件。

在本文中,我使用了这种技术通过NProgress显示顶级进度条:

文章来源:https://dev.to/vvo/how-to-solve-window-is-not-defined-errors-in-react-and-next-js-5f97
PREV
学习 Rails 6 和 Ruby 的最佳现代资源第一天上午:TryRuby 第一天下午:Rails 网站的官方入门指南本周剩余时间接下来的两周:Learn Enough 的 Ruby on Rails 教程✋ 停止学习并构建
NEXT
2024:面向 Web 开发的免费托管平台 优质提供商 总结