在我的Next.js应用程序中,我似乎无法访问窗口:
未处理的拒绝(ReferenceError):没有定义窗口
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
在我的Next.js应用程序中,我似乎无法访问窗口:
未处理的拒绝(ReferenceError):没有定义窗口
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
当前回答
“嗡�̶b̶r̶o̶w̶s̶e̶r̶ ̶t̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶d̶u̶r̶n̶n̶ ̶r̶̶n̶n̶ ̶o̶n̶ ̶t̶ ̶c̶l̶i̶e̶n̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶。
但是process object在Webpack5和NextJS中已经被弃用了,因为它是一个仅用于后端的NodeJS变量。
我们必须使用浏览器中的backwindow对象。
If (typeof window !== "undefined") { //客户端代码 }
其他解决方案是使用react钩子替换componentDidMount:
useEffect(() => { //客户端代码 })
其他回答
如果你使用React Hooks,你可以将代码移动到Effect Hook中:
import * as React from "react";
export const MyComp = () => {
React.useEffect(() => {
// window is accessible here.
console.log("window.innerHeight", window.innerHeight);
}, []);
return (<div></div>)
}
useEffect中的代码只在客户端(在浏览器中)执行,因此它可以访问window。
“嗡�̶b̶r̶o̶w̶s̶e̶r̶ ̶t̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶d̶u̶r̶n̶n̶ ̶r̶̶n̶n̶ ̶o̶n̶ ̶t̶ ̶c̶l̶i̶e̶n̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶。
但是process object在Webpack5和NextJS中已经被弃用了,因为它是一个仅用于后端的NodeJS变量。
我们必须使用浏览器中的backwindow对象。
If (typeof window !== "undefined") { //客户端代码 }
其他解决方案是使用react钩子替换componentDidMount:
useEffect(() => { //客户端代码 })
您可以定义一个状态变量并使用窗口事件句柄来处理这样的更改。
const [height, setHeight] = useState();
useEffect(() => {
if (!height) setHeight(window.innerHeight - 140);
window.addEventListener("resize", () => {
setHeight(window.innerHeight - 140);
});
}, []);
我在一个自定义钩子中包装了一般的解决方案(if (typeof window === 'undefined')返回;),这让我非常满意。它有一个类似的界面来反应useMemo钩子,我真的很喜欢。
import { useEffect, useMemo, useState } from "react";
const InitialState = Symbol("initial");
/**
*
* @param clientFactory Factory function similiar to `useMemo`. However, this function is only ever called on the client and will transform any returned promises into their resolved values.
* @param deps Factory function dependencies, just like in `useMemo`.
* @param serverFactory Factory function that may be called server side. Unlike the `clientFactory` function a resulting `Promise` will not be resolved, and will continue to be returned while the `clientFactory` is pending.
*/
export function useClientSideMemo<T = any, K = T>(
clientFactory: () => T | Promise<T>,
deps: Parameters<typeof useMemo>["1"],
serverFactory?: () => K
) {
const [memoized, setMemoized] = useState<T | typeof InitialState>(
InitialState
);
useEffect(() => {
(async () => {
setMemoized(await clientFactory());
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return typeof window === "undefined" || memoized === InitialState
? serverFactory?.()
: memoized;
}
使用的例子:
我使用它来动态导入与next.js中的SSR不兼容的库,因为它自己的动态导入只与组件兼容。
const renderer = useClientSideMemo(
async () =>
(await import("@/components/table/renderers/HighlightTextRenderer"))
.HighlightTextRendererAlias,
[],
() => "text"
);
正如你所看到的,我甚至实现了一个回退工厂回调,所以你可以提供一个结果时,最初呈现在服务器上。在所有其他方面,这个钩子应该表现类似于react useMemo钩子。接受反馈。
有史以来最好的解决方案 从'next/dynamic'导入dynamic; const Chart = dynamic(()=> import('react-apexcharts'), { ssr:假的, })