【问题标题】:Disabling NextJS SSG - Hook vs Const禁用 NextJS SSG - Hook vs Const
【发布时间】:2022-01-05 07:48:23
【问题描述】:

我正在测试禁用 NextJS SSG 的不同方法。

以下钩子有效

import { useState, useEffect } from "react";

const useClientCheck = () => {
  const [isClient, isClientSet] = useState(false);
  useEffect(() => {
    isClientSet(true);
  }, []);
  return isClient;
};

export function LayerHook({ children }) {
  const isClient = useClientCheck();
  if (!isClient) return null;
  return (
    <>
      <h1>LayerHook</h1>
      {children}
    </>
  );
}

但是,如果我检查类似的 const

const ISSERVER = typeof window === "undefined";

export function LayerConst({ children }) {
  if (ISSERVER) return null;
  return (
    <>
      <h1>LayerConst</h1>
      {children}
    </>
  );
}

它用消息抱怨

预计服务器 HTML 将在 &lt;div&gt; 中包含匹配的 &lt;h1&gt;

对我来说,第二种选择等同于第一种,但效率更高。为什么用第二个选项而不是第一个选项抛出异常?

示例:https://codesandbox.io/s/next-js-hook-vs-const-n4eb7?file=/components/LayerConst.js:0-197

【问题讨论】:

  • 对不起,如果你不想 SSR,为什么要使用 NextJS? useEffect 直到初始渲染之后才会运行,这发生在客户端。我不知道 that 是否有区别。我也没有在您链接的 CSB 中看到投诉消息。
  • @DrewReese 看起来我犯了一个错误。我想为 auth 等特定页面禁用 SSG。

标签: javascript reactjs react-hooks next.js


【解决方案1】:

它们很接近但不等价。虽然useEffect 确实只在客户端上运行,但您在每个中都在说:

第一个例子(带钩子)

当你(下一步)构建这个组件时,给我一个带有 h1 的片段并渲染子元素。 (在构建阶段的这一点上,Next 没有运行 React,因此它将构建组件的 html。)当 React 水合后,运行这个组件。我告诉你isClient 是假的,所以返回null。然后在客户端,useEffect 运行后,你会看到isClienttrue,所以继续为用户呈现我的 html。

第二个例子(使用typeof window

当您(下一步)构建此组件时,请检查是否定义了 window。如果是这样,请构建此 html。如果没有,“构建”一个空组件。当 React 水合后,运行这个组件。如果我们在客户端上,您会看到 window 已定义并且组件应该为用户呈现我的 html。

为什么 Next 会/不抱怨

Next 并没有抱怨您的第一个示例,因为它在构建组件时在您的 return 内的服务器上创建 html,并将其传递给客户端,并且当 React 在客户端上水合时它匹配。它在“抱怨”您的第二个示例,因为它实际上并没有在服务器上创建该 html,所以当它在 React 水合物之后在客户端上看到它时 - 瞧,警告。

记住 Next 的 build 进程和运行 next dev 之间的区别。如果你想禁用 SSR,没问题 - 只是不要在任何地方使用 getInitialPropsgetServerSideProps。当您运行next build 时,它会将所有内容构建为静态页面,并且您将没有任何 SSR - 它只是不存在。 但是,使用您的两个示例 - 当 Next 构建此页面时,第一个将在源代码中包含原始 html (div, h1),而第二个则不会。

最后,请记住,在next dev 期间您不能真正禁用 SSR。如果您必须拥有 SSR 并且您正尝试为特定组件禁用它,那么您将在此处走一条合理的道路。但是,如果您只想要一个静态站点,则不需要像这样的解决方法来禁用 SSR - 当您构建没有 getInitialPropsgetServerSideProps(或自定义 documentapp 组件的站点时,它将被禁用)。

使用示例构建代码更新

在 cmets 中有一些讨论,所以我正在更新以包含一些 内置 代码,以显示实际编译的内容与 React 看到的内容。为此,我使用 OP 的代码在 Next 中创建了两个页面组件,但只是修改了 return 方法以返回一个段落标签:

// pages/sample-hook.tsx
import { useState, useEffect } from "react"

const useClientCheck = () => {
  const [isClient, isClientSet] = useState(false)
  useEffect(() => {
    isClientSet(true)
  }, [])
  return isClient
}

export default function SamplePageWithHook() {
  const isClient = useClientCheck()
  if (!isClient) return null
  return <p>Sample Page</p>
}
// pages/sample-null.tsx
export default function SamplePageWithNull() {
  if (typeof window === "undefined") {
    return null
  }
  return <p>Sample Page</p>
}

如果您查看 Next 构建的页面,您会得到以下信息:

// .next/server/pages/sample-hook.js
const useClientCheck = ()=>{
    const { 0: isClient , 1: isClientSet  } = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
    (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
        isClientSet(true);
    }, []);
    return isClient;
};
function SamplePageWithHook() {
    const isClient = useClientCheck();
    if (!isClient) return null;
    return(/*#__PURE__*/ react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx("p", {
        children: "Sample Page"
    }));
};
// .next/server/pages/sample-null.js
function SamplePageWithNull() {
    if (true) {
        return null;
    }
    return(/*#__PURE__*/ react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx("p", {
        children: "Sample Page"
    }));
};

sample-hook.js 中,当 React 被水合时,useClientCheck 被解释并且组件根据useClientCheck 返回 html 或null。由于这个钩子,React 知道这个组件可以返回 either html 或 null

sample-null.js 中,不管怎样,React 解释这个组件,只看到它可以返回 null。 is(true) 之后的代码永远不会到达,因此可能的输出永远不会进入 React 树并且 React 永远不会意识到它。

这就是为什么在第二种情况下会生成警告以及为什么第一种和第二种不等价的原因。警告是告诉你 React 正在渲染 sample-null.js 中的一些东西,它以前不知道并且以前不在树中。

【讨论】:

  • "Next 并没有抱怨您的第一个示例,因为它是在您的 return 内的服务器上创建 html - 不是吗? return 语句永远不会在服务器上执行,因为 isClientfalse,并且永远不会构建 html。它构建了一个null 组件。
  • @Bergi 在第一个示例中,html 仍在服务器上构建,因为 React 挂钩在 React 本身运行之前不会运行。在服务器上,React 是使用 renderToString 构建的,这意味着第一个示例中的钩子在服务器上初始 html 渲染期间不会执行,只会渲染 return 方法中的内容。一旦 React 被水合,钩子就会被调用,但此时 html 已经构建好了。在第二个示例中,节点理解 typeof window,因此 renderToString 永远不会命中具有 html 并且永远不会构建 html 的第二个 return
  • 如果你说useEffect钩子在服务器上运行,那么isClient就是falserenderToString会渲染null值,而不是带有 h1 和孩子的片段。初始 html 为空。这就是它起作用的原因,因为空的 html 可以在没有警告的情况下使用 React 进行水合,然后效果运行并且 React 将呈现片段。
  • 但是“由于这个钩子,React 知道这个组件可以返回 html 或 null。”是错误的。 React 不是某种“感知”任何事物的神奇人工智能。它只会渲染一个SamplePageWithHook,它会返回null。它可能会在效果运行后再次渲染它,然后它会返回片段。关键是,如果在第一次渲染时,它不会产生与服务器生成的 html 相同的 DOM,它会发出警告。问题是SamplePageWithNull 总是(包括第一次渲染)返回客户端上的片段。
  • 我没有使用getInitialPropsgetServerSideProps。我确实使用了自定义 _app_document。我想我混淆了术语。根据 NextJS 文档,与 SSR 不同,自动使用没有数据的 SSG。我想我现在明白 NextJS 正在比较客户端和服务器中的初始渲染。
【解决方案2】:

首先,如果 SEO 对您的项目不重要,请尝试使用纯 React 实现您的项目,但是您可以在 next.js 中实现 react,因此请在 useEffect 中发送您的请求,因为 useEffect 只在客户端运行,因此您的页面不是SSR。

【讨论】:

    猜你喜欢
    • 2022-01-08
    • 2021-08-11
    • 2021-05-05
    • 2022-12-23
    • 2016-07-24
    • 2023-02-01
    • 1970-01-01
    • 2021-01-26
    • 2021-09-07
    相关资源
    最近更新 更多