亲宝软件园·资讯

展开

修复Next.js中window is not defined方法详解

Jovie 人气:0

引言

这个问题与Next.js的服务器端渲染有关。Next.js默认会尝试为您的网站使用SSR。这意味着,由于我们是在服务器上而不是在浏览器中,所以 "窗口 "对象并不存在。解决这个问题的方法是强迫Next.js在浏览器中运行你的代码,我将解释如何做到这一点。

使用useEffect钩子

useEffect钩子总是在浏览器中运行,所以我们可以用它来确保我们的代码只能从那里运行。关于钩子的快速入门知识,请查看这篇文章。

下面是一个简单应用的示例代码,它将用户最后一次访问网站的时间存储在本地存储中。如果你不熟悉在React或Next.js中使用localStorage,可以看看我们这里的这个教程,它使用了类似的方法。这个方法是相同的,但有一个小的区别,那就是你可以在vanilla React组件的主体中使用localStorage,因为默认没有SSR:

function updateLastSeen() {
    const lastSeen = window.localStorage.getItem('last-seen') ?? new Date();
    window.localStorage.setItem('last-seen', new Date().toString());
    return lastSeen;
}
function WindowPage() {
    const lastSeen = updateLastSeen();
    return <div>Last Seen: {lastSeen}</div>;
}

运行这个,我们得到这个你可能熟悉的错误:

为了解决这个问题,让我们引入一个自定义的钩子,它涉及一个 useEffect。

function useLastSeen() {
    const [lastSeen, setLastSeen] = useState(null);
    const retrieved = useRef(false); //To get around strict mode running the hook twice
    useEffect(() => {
        if (retrieved.current) return;
        retrieved.current = true;
        setLastSeen(updateLastSeen());
    }, []);
    return lastSeen;
}

我已经把我们的逻辑移到了一个自定义的钩子上,只是为了保持代码的简洁。现在随着我们代码的改变,代码将只在钩子运行时运行,也就是在客户端。

然后我们可以将我们的组件更新为。

function WindowPage() {
    const lastSeen = useLastSeen();
    return (
        <div>
            Last Seen: {lastSeen}
        </div>
    );
}

这段代码更简洁,而且由于 useEffect里面的钩子只在客户端运行,我们的错误就消失了

检查窗口是否被定义

另一种方法是在我们运行代码之前简单地检查窗口对象是否被定义。如果代码在服务器上运行,由于我们不在浏览器中,窗口对象就不存在。

我们不能使用与之前相同的例子,因为这种方法有一个关键的区别。因为我们没有等待组件的渲染,任何对页面HTML的差异都会导致服务器端渲染的页面版本与客户端不同,我们会在Next.js中得到一个错误。

在这个例子中,我们将为窗口对象添加一个事件监听器,以跟踪页面上的点击和它们的位置。

const isBrowser = () => typeof window !== 'undefined'; //The approach recommended by Next.js
function WindowPage() {
    const [lastClick, setLastClick] = useState('');
    if (isBrowser()) { //Only add the event listener client-side
        window.addEventListener('click', (e) =>
            setLastClick(`${e.pageX}, ${e.pageY}`)
        );
    }
    return (
        <div className="m-auto rounded bg-violet-600 p-10 font-bold text-white shadow">
            Click at: {lastClick}
        </div>
    );
}

正如我们所看到的,代码并没有在服务器上运行,所以Next.js现在很高兴了

希望这两种方法中的一种能帮助解决你的问题。我推荐第一种方法,因为它涵盖了大多数情况,而且你不必处理Next.js发现渲染不匹配的可能性。

加载全部内容

相关教程
猜你喜欢
用户评论