[vercel/next.js]问:如何仅在客户端执行代码

2024-05-15 348 views
6

我的应用程序中有一个回调路由,用于从 URL 哈希片段中的身份验证进程接收访问令牌。我想解析它并保存到 redux 存储中。我曾经在切换到下一个之前完成它window.location.hash,但是当页面呈现服务器端时,这是不可用的(而且哈希片段显然没有发送到服务器,所以我无法在那里解析它)。

有没有办法限制某些代码仅在客户端执行?或者还有其他方法可以实现这一目标吗?

回答

3

@vanniewelt:动态导入的异步组件加载在客户端上,您可以将所有客户端逻辑放在那里。

此外,ComponentWillReceiveProps 生命周期将在服务器端接收 props 中的请求。可以检查该变量是否存在并注入您的客户端代码。

如果库需要窗口变量,我建议您使用动态导入,对我来说效果很好。

https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import

0

您还可以使用componentDidMountReact的生命周期方法。它不称为服务器端。

2

@vanniewelt 你可以随时看看process.browser..

if (process.browser) {
  // client-side-only code
}

由@Timer编辑:

@vanniewelt 你可以随时看看typeof window..

if (typeof window !== 'undefined') {
  // client-side-only code
}
7

是的,但这不是最好的方法。假设我想检查用户是否经过身份验证,并且出于多种原因我只想在客户端运行该检查,其中之一是因为我将 JWT 存储在 localStorage 中并且不想将其存储在 cookie 中。所以在这种情况下,在第一页加载:

if (process.browser) {
  // client-side-only code
}

范围内的代码根本不会被执行,不会在服务器上也不会在客户端上执行,只有当客户端触发路由更改时才会执行

4

@sarkistlt 在某些情况下你可能是对的,具体取决于代码的放置位置..但是..

如果该代码在服务器上执行,则执行将不会到达// client-side-only code。如果该代码在客户端执行,执行达到// client-side-only code

这似乎是这个问题的有效答案:

有没有办法限制某些代码仅在客户端执行?

6

@sarkistltcomponentDidMount仅在客户端执行,componentWillMount在客户端和服务器端均执行。

0

@sergiodxa 实际上你是对的,刚刚运行了测试。谢谢!

1

我需要mapbox-gl一个模块,它是内部的客户端库ComponentDidMount,并摆脱了一个丑陋的错误ReferenceError: self is not defined

1

我无法使用 Mapbox,componentDidMount因为该组件仍然尝试在服务器上进行处理(我不知道到底要做什么)(我最终遇到了与@elaich 相同的问题)。

按照 @aga5tya 的建议使用动态导入可以解决问题吗?

8

@vanniewelt:动态导入的异步组件加载在客户端上,您可以将所有客户端逻辑放在那里。

此外,ComponentWillReceiveProps 生命周期将在服务器端接收 props 中的请求。可以检查该变量是否存在并注入您的客户端代码。

如果库需要窗口变量,我建议您使用动态导入,对我来说效果很好。

https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import

对于那些遇到此线程寻找解决方案的人。上面列出的动态导入链接已损坏。它已更新,可以在Dynamic Imports中找到。

1

如果您有一个始终应该在客户端上执行的组件,并且不想使用动态导入(例如:人们可能会忘记他们需要使用它们),您可以使用 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>
}
8

只是为了添加一些已经很好的答案:我当前的项目在两个方向上做了很多条件渲染,所以我为此使用了一个组件:

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>
7

如果您使用任何访问浏览器 API 的库,那么您可以使用 SSR=false 作为第二个参数动态导入模块。 const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } ) https://nextjs.org/docs/advanced-features/dynamic-import

1

如果您使用任何访问浏览器 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'
7

@Timer我注意到您编辑了我的评论https://github.com/vercel/next.js/issues/2473#issuecomment-362119102,我再次编辑了该评论以反映原始评论您的编辑。

  1. 为什么我们应该使用typeof window !== 'undefined'而不是仅仅process.browser?它比较冗长,所以如果没有原因,我更喜欢使用process.browser.
  2. 下次您编辑贡献者评论时,您能否在评论中使编辑内容变得明显?首先,它可能会使线程变得混乱,就像这里所做的那样,以下评论是对我的具体评论的回复。其次,它具有误导性,因为这不是我写的,也不是人们做出反应时读到的。
2

为什么我们应该使用 typeof window !== 'undefined' 而不仅仅是 process.browser ?它比较冗长,所以如果没有原因的话,我更喜欢使用 process.browser。

process.browser是非标准的,仅在 webpack 环境中可用,这意味着它将来可能会被破坏,例如 webpack 5 不再使用 polyfills process

1

的替代方案typeof window !== "undefined" 可以是 Object.prototype.isPrototypeOf(window).然而,根据这篇文章,Next.js 生成的捆绑包会检测先验情况,并且无论生成更轻的捆绑包,都应该被视为胜利。

6

在 Next 中预见一个不需要开发人员编写样板文件的 api 是很有用的。仅在客户端环境中应用一些 JS 是很常见的,并且必须创建自定义组件是样板文件,可以将其提取为易于使用的简单方法调用。

5

由于最近没有活动,此问题已被自动锁定。如果您遇到类似问题,请创建一个新问题并包含重现步骤。谢谢。