我的应用程序中有一个回调路由,它从 URL 哈希片段中的身份验证进程接收访问令牌。 我想解析它并保存到 redux 存储中。 在切换到下一个之前,我曾经通过window.location.hash
获取它,但是当页面呈现服务器端时这不可用(哈希片段显然没有发送到服务器,所以我无法在那里解析它)。
有没有办法限制某些代码只能在客户端执行? 或者有没有其他方法可以实现这一目标?
@vanniewelt :在客户端加载动态导入的异步组件,您可以将所有客户端逻辑放在那里。
此外 ComponentWillReceiveProps 生命周期将在服务器端接收道具中的请求。
可以检查此变量是否存在并注入您的客户端代码。
如果库需要窗口变量,我建议您使用动态导入,对我来说效果很好。
https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import
你也可以使用 React 的componentDidMount
生命周期方法。 它不称为服务器端。
@vanniewelt您可以随时查看process.browser
..
if (process.browser) {
// client-side-only code
}
@Timer编辑:
@vanniewelt您可以随时查看typeof window
..
if (typeof window !== 'undefined') {
// client-side-only code
}
是的,但这不是最好的方法。
假设我想检查用户是否已通过身份验证,并且出于多种原因我只想在客户端运行该检查,其中之一是因为我将 JWT 存储在 localStorage 中并且不想将其存储在 cookie 中。
所以在这种情况下,在第一页加载时:
if (process.browser) {
// client-side-only code
}
范围内的代码根本不会执行,不在服务器上而不是在客户端上,只有在客户端触发路由更改时才会执行
@sarkistlt在某些情况下,您可能是对的,具体取决于该代码的放置位置……但是……
如果该代码在服务器上执行,则执行将不会到达// client-side-only code
。 如果该代码在客户端上执行,则执行将达到// client-side-only code
。
这似乎是这个问题的有效答案:
有没有办法限制某些代码只能在客户端执行?
@sarkistlt componentDidMount
只在客户端执行, componentWillMount
在客户端和服务器端都执行。
@sarkistlt https://reactjs.org/docs/react-component.html#componentwillmount
这是在服务器渲染上调用的唯一生命周期钩子。
我做了很多次,你把componentWillMount
和componentDidMount
混淆
@sergiodxa实际上你是对的,刚刚运行了测试。 谢谢!
@sergiodxa componentWillMount
的链接更改为https://reactjs.org/docs/react-component.html#unsafe_componentwillmount。
我需要mapbox-gl
模块,它是ComponentDidMount
一个客户端库,并摆脱了一个丑陋的错误ReferenceError: self is not defined
我无法使用componentDidMount
制作 Mapbox,因为该组件仍会尝试在服务器上进行处理(我不知道究竟要做什么)(我最终遇到了与 @elaich 相同的问题) )。
使用@aga5tya建议的动态导入可以解决问题🎉。
@vanniewelt :在客户端加载动态导入的异步组件,您可以将所有客户端逻辑放在那里。
此外 ComponentWillReceiveProps 生命周期将在服务器端接收道具中的请求。
可以检查此变量是否存在并注入您的客户端代码。如果库需要窗口变量,我建议您使用动态导入,对我来说效果很好。
https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import
对于那些遇到此线程以寻找解决方案的人。 上面列出的动态导入链接已损坏。 它已更新,可在Dynamic Imports 中找到。
如果您有一个始终应该在客户端执行的组件,并且不想使用动态导入(例如:人们可能会忘记他们需要使用它们),您可以使用 Hooks(或等效的componentDidMount
) 如下:
import React, { useEffect, useState } from 'react'
function CreateInteraction ({ input }) {
const [isComponentMounted, setIsComponentMounted] = useState(false)
useEffect(() => setIsComponentMounted(true), [])
if(!isComponentMounted) {
return null
}
return <h1>I'm only executed on the client!</h1>
}
只是添加一些已经很好的答案:我当前的项目在两个方向上都做了很多条件渲染,所以我为此使用了一个组件:
import React, { useEffect, useState } from "react";
export interface ConditionallyRenderProps {
client?: boolean;
server?: boolean;
}
const ConditionallyRender: React.FC<ConditionallyRenderProps> = (props) => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => setIsMounted(true), []);
if(!isMounted && props.client) {
return null;
}
if(isMounted && props.server) {
return null;
}
return props.children as React.ReactElement;
};
export default ConditionallyRender;
然后按如下方式使用它:
<Layout>
<ConditionallyRender server>
<p>This is rendered only on server.</p>
</ConditionallyRender>
<ConditionallyRender client>
<p>This is rendered only on client.</p>
</ConditionallyRender>
</Layout>
如果您使用任何访问浏览器 API 的库,那么您可以使用 SSR=false 作为第二个参数动态导入模块。
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
https://nextjs.org/docs/advanced-features/dynamic-import
如果您使用任何访问浏览器 API 的库,那么您可以使用 SSR=false 作为第二个参数动态导入模块。
const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } )
https://nextjs.org/docs/advanced-features/dynamic-import
如果您想为查看@yashwant-dangi 代码的任何人使用“动态”导入,您还需要添加此导入!
import dynamic from 'next/dynamic'
@Timer我注意到您编辑了我的评论https://github.com/vercel/next.js/issues/2473#issuecomment -362119102,我再次编辑以反映原始评论和您的编辑。
typeof window !== 'undefined'
而不是process.browser
? 它更冗长,所以如果这里没有理由,我更喜欢使用process.browser
。为什么我们应该使用 typeof window !== 'undefined' 而不是 process.browser? 它比较冗长,所以如果这里没有理由,我更喜欢使用 process.browser。
process.browser
是非标准的,仅在 webpack 环境中可用,这意味着它在未来可能会崩溃,例如 webpack 5 不再填充process
。
最有用的评论
@vanniewelt您可以随时查看
process.browser
..@Timer编辑:
@vanniewelt您可以随时查看
typeof window
..