【问题标题】:React Next.js - Three related questions about SSR patternsReact Next.js - 关于 SSR 模式的三个相关问题
【发布时间】:2021-08-14 14:02:44
【问题描述】:

关于 next.js 的使用和 react 以实现 SSR 构建在彼此之上的以下问题,所以我想我会把它写成一篇文章。我的主要问题是第三个问题,但我觉得我需要先了解前两个问题才能到达那里。所以我们开始:

1.客户端收到后总是从头开始重新执行整个页面,我说的对吗?

考虑这个 next.js 页面组件:

const Page = () => {
  const [state, setState] = useState(getState());

  function getState() {
    console.log("compute initial state");
    return 1;
  }

  return <>{state}</>;
};

据我所知,getState() 在服务器和客户端都执行。如果我只想在服务器上执行该计算,我必须通过getInitialProps()resp 来完成。 getServersideProps(),对吧?

2.预渲染的文档如果立即丢弃,对客户端有什么价值?

让第一个问题更进一步,如果预渲染文档事件无论如何都要从头开始重新计算,为什么还要将它交给客户端。客户获得初始文件有什么好处?好的,如果客户端根本无法执行js,他们至少有一些东西。但仅此而已吗?

3.这是否意味着我不必在客户端“双重渲染”某些组件?

假设我的部分代码依赖于window,无法在服务器上执行。正如我在不同的articles 中所解释的那样,如果我在我的代码中依赖像typeof window === "undefined" 这样的检查,它可能会导致问题(并做出反应警告)。相反,我认为更好的方法是在第一次渲染后使用useEffect 执行这些功能:

const Page = () => {
    const [value, setValue] = useState();

    // Effect will be executed after the first render, e.g. never on the server
    useEffect(() => {
        const value = window.innerWidth; // Some computations or subscriptions that depend on window
        setValue(value)
    }, []);

    return (
        <>
            {!value && <h1>value pending ...</h1>}
            {value && <h1>{value}</h1>}
        </>
    );
};

现在,使用这种模式,从 SSR 的角度来看,应用程序很好。但是我正在引入在客户端已经完成的额外工作:即使window 是在第一次渲染时定义的,它也只能在第二次渲染时使用。

对于这个较小的示例,这并没有起到很大的作用,但在较大的应用程序中,可能会出现闪烁,因为不必要的第一次渲染会在几毫秒后更新。此外,代码变得更难阅读,更容易出错。

我想要的解决方案与上面的第一个问题有关:我能否以某种方式避免应用程序从头开始,而是直接从第二个渲染开始?到目前为止我错过了某种模式吗?

注意:当然我可以重写组件来简单地检查window是否被定义:

  return (
    <>
      <h1>{window ? window.innerWidth : "value pending ..."}</h1>
    </>
  );

但是,这将导致反应警告并导致我在上面喜欢的文章中描述的其他问题。另见react docs:

React 期望在服务器和客户端之间呈现的内容是相同的。它可以修补文本内容的差异,但您应该将不匹配视为错误并修复它们。

非常感谢您的帮助!

【问题讨论】:

    标签: reactjs next.js server-side-rendering


    【解决方案1】:

    客户收到后总是从头开始重新执行整个页面,我说的对吗?

    是和不是。初始 html 在服务器上构建并作为 html 发送到客户端,然后 react 被水合,以便您的页面变得可交互。例如,考虑这个“页面”:

    export default function MyPage() {
      const [greeting, setGreeting] = React.useState("Hello")
    
      React.useEffect(() => {
        setGreeting("Goodbye")
      }, [])
    
      return (<p>{greeting}</p>)
    }
    

    当页面在服务器上呈现时,它将呈现为 html 并将该 html 发送到客户端。所以如果你检查页面的源代码,你会看到:

    &lt;p&gt;Hello&lt;/p&gt;

    然后,在客户端,react 被水合,useEffect 运行,所以在浏览器中你会看到一个带有单词“再见”的段落。

    无论您使用的是 SSR(html 是在服务器上按需创建)还是 SSG(html 在构建时创建为静态 html 页面),都是如此。

    如果立即丢弃,预渲染的文档对客户有什么价值?

    如果您看到我的第一点,则不会丢弃整个文档。 SSR 中的值是如果您希望初始问候文本不是“Hello”。例如,如果您希望您的服务器解析身份验证令牌,获取用户配置文件,并在页面加载时使用“Hello Jim”播种greeting,那么您更喜欢 SSR。如果在将 html 发送到客户端之前没有任何服务器端处理,则可以选择 SSG。

    考虑这两个“页面”:

    // Using SSR
    export default function MyPage({customerName}) {
      return (<p>{customerName}</p>)
    }
    
    // Using SSG
    export default function MyPage({customerName}) {
      const [greeting, setGreeting] = React.useState("Hello")
    
      React.useEffect(() => {
        // Call server to get the customer's name
        const name = myApi.get('/name')
        setGreeting(`Hello ${name}`)
      }, [])
    
      return (<p>{greeting}</p>)
    }
    

    在第一个示例中,服务器使用客户名称(来自服务器上的某个进程)呈现 p 标记,因此 html 源代码将包含该客户的名称。这里什么都没有扔掉。

    在第二个示例中,站点以 html 格式构建,源代码的 p 标签显示“Hello”。当页面被访问时,useEffect 运行,p 标签在你的 api 响应时更新。所以用户会在 x 微秒内看到“Hello”,然后它会切换到“Hello Jim”。

    这是否意味着我必须在客户端“双重渲染”某些组件?

    不-您可以控制在服务器上与在客户端上呈现的内容。如果您在服务器上为组件提供数据并且不在客户端更改它,则它不会重新呈现。

    在你的例子中,是的 - 你是双重渲染。但你可能不需要。如您所述,客户端上不存在window。如果您绝对必须在获得窗口大小之前向您的用户显示“值待定...”行,那么您将进行双重渲染 - 一次在服务器上填充该字符串,然后一次在反应水合物时替换它客户。

    如果您不需要显示待定行,而只需要在 window 实际存在时在客户端显示值,则可以这样重写:

    export default function Page() {
      const [value, setValue] = React.useState();
    
      React.useEffect(() => {
        const newValue = window.innerWidth; // Some computations or subscriptions that depend on window
        setValue(newValue)
      }, []);
    
      if(value) {
        return <h1>{value}</h1>
      }
    
      return null
    };
    

    在这种情况下,服务器上没有渲染任何内容,因为服务器上没有 value。只有当客户端水合时,useEffect 才会运行,更新value,并渲染你的组件一次。

    【讨论】:

      猜你喜欢
      • 2021-01-12
      • 1970-01-01
      • 2012-12-25
      • 1970-01-01
      • 2023-03-21
      • 2019-03-29
      • 2011-02-19
      • 2018-07-27
      • 1970-01-01
      相关资源
      最近更新 更多