Next.js - 在某些页面上禁用服务器端渲染

94

是否可以在Next.js中禁用某些页面的服务器端渲染(SSR)?例如,我有一个产品描述页面,在这个页面上我使用SSR进行SEO,但我也有一个包含项目或产品列表的页面,我可以对其进行筛选,对于该页面,我不想使用SSR,因为每次都会动态生成,如何在此页面上禁用SSR?


如果您使用Material UI,那么可以直接使用其中的NoSsr组件。 - guest
9个回答

141

dynamic() 函数也可以在没有动态导入的情况下使用:

// components/NoSsr.jsx

import dynamic from 'next/dynamic'
import React from 'react'

const NoSsr = props => (
  <React.Fragment>{props.children}</React.Fragment>
)

export default dynamic(() => Promise.resolve(NoSsr), {
  ssr: false
})

任何放在这个组件中的内容都不会在服务器端渲染的源代码中显示。例如:

// inside the page you want to disable SSR for

import NoSsr from "components/NoSsr";

Contact me at <NoSsr>email@example.com</NoSsr>

2
非常好用,可以在 _app.js 中用来包装 <Component /> 进行调试。 - AveryFreeman
3
非常好,因为你也可以将它与 _app 一起使用。 - Eric Burel
一个限制是:它可能会影响代码加载的方式,带来其他副作用。纯React解决方案可能已经足够了(例如在 https://dev59.com/MlQJ5IYBdhLWcg3wx40V#57512843 中描述)。 - Eric Burel
太棒了!只需要一行代码的修改就解决了我的问题。 - jasxir

128

1
如果没有SSR元素的子元素,会发生什么?我需要no ssr用于包含其他子元素的顶级元素,这会影响我的SEO和性能吗? - Nikola-Milovic
3
如何在页面级别上执行? - Diptom Saha
如果组件具有命名导出变量,则这也不起作用。 - Jamie Hutber
“export default () => <DynamicComponentWithNoSSR />”和“export default DynamicComponentWithNoSSR”不是一样的吗?无论如何,你解救了我的一天,回答得很准确! - Page not found
“export default () => <DynamicComponentWithNoSSR />”和“export default DynamicComponentWithNoSSR”不是一样的吗?不管怎样,你解救了我的一天,回答得非常准确! - undefined

52

这将禁用 next.js SSR,在您的 _app.tsx 文件中添加此代码。

import type { AppProps } from "next/app";
import dynamic from "next/dynamic";
import React from "react";

const App = ({ Component, pageProps }: AppProps) => {
  return <Component {...pageProps} />;
};

export default dynamic(() => Promise.resolve(App), {
  ssr: false,
});

9
这会禁用整个项目的SSR,SSR带来的优势,如SEO友好页面将会丧失。(仅供参考) - Herii
1
它就像魔法一样奏效了。 - Boshra Jaber
1
不需要为整个项目做这个。你也可以仅在特定页面使用它。 - Eugene P.
SPA使用下一路由速度更快;很棒的技巧。 - Mechanic

13
这是一个晚回答,但如果有人遇到这个问题:
const isServer = () => typeof window === `undefined`;

const Page = () => isServer() ? null : (

  <> 
      <YourClientSideComponent />
  </>
);

这是关于“页面”级别的内容。isServer()可以防止在服务器端渲染任何内容。如果你愿意,你也可以在组件级别上防止ssr:

const isServer = () => typeof window === `undefined`;

const Page = () =>(

  <> 
      { !isServer() && <YourClientSideComponent /> }
  </>
);

!isServer 前面的 "." 是有意为之吗?这是无效的 JavaScript 语法。 - Dawson B
我在 Next.js 项目中使用了 styled-components,它能够正常工作,但并不总是按照要求工作。不确定原因!接受的解决方案最适合。 - Vaishak
3
这会导致服务器渲染和客户端渲染不匹配,但实际上并没有禁用渲染。 - Eric Burel

5

完全基于Erik Hofer的回答,您可以创建一个HOC来包装您的页面或其他组件。

import dynamic from 'next/dynamic';
import React from 'react';

const withNoSSR = (Component: React.FunctionComponent) => dynamic(
    () => Promise.resolve(Component),
    { ssr: false },
);

export default withNoSSR;

然后按如下方式使用:
import React from 'react';
import withNoSSR from './withNoSSR';

function SomePage() {
    return (
        <div>Only renders in the browser</div>
    );
}

// wrap with HOC on export
export default withNoSSR(SomePage);

3

我们还可以使用react-no-ssr React组件。

假设Comments是我们的客户端组件。现在我们需要只在客户端上渲染它。以下是如何实现。

import React from 'react';
import NoSSR from 'react-no-ssr';
import Comments from './comments.jsx';

const MyPage = () => (
  <div>
    <h2>My Blog Post</h2>
    <hr />
    <NoSSR>
      <Comments />
    </NoSSR>
  </div>
);

2
在 @refactor 的答案中,react-no-ssr 因为一些 TypeScript 问题而无法正常工作。
我刚刚写了一个非常简单的 NoSSR 组件,如下所示:
import { ReactNode, useEffect, useLayoutEffect, useState } from 'react'

const DefaultOnSSR: React.FC = () => null

export const NoSSR: React.FC<{ children: ReactNode; onSSR?: ReactNode }> = ({ children, onSSR = <DefaultOnSSR /> }) => {
  const [onBrowser, setOnBrowser] = useState(false)
  useLayoutEffect(() => {
    setOnBrowser(true)
  }, [])
  return <>{onBrowser ? children : onSSR}</>
}

如果您想避免像下面这样的服务器端渲染,可以在任何地方使用:

const MyPage = () => (
  <div>
    <h2>My Blog Post</h2>
    <hr />
    <NoSSR>
      <Comments />
    </NoSSR>
  </div>
);

这个解决方案给了我一个警告:“Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format.” - Mahmoud

2

我刚刚想出了一个解决方案,这个方案是基于React文档中提到的内容:https://reactjs.org/docs/react-dom.html#hydrate

class ClientOnly extends React.Component {
  state = {
    isClient: false,
  };

  componentDidMount() {
    this.setState({
      isClient: true,
    });
  }

  render() {
    const { isClient } = this.state;
    const { children } = this.props;

    return isClient ? children : false;
  }
}

您可以使用这个功能来包装任何组件/块,这样它就不会在服务器上渲染。例如:
<ClientOnly>You're logged in as {name}</ClientOnly>

这也可以避免以下 React.hydrate 警告:“警告:预期服务器 HTML 包含在中的匹配项。”

1
等价于钩子的代码应该是这样的:const useClientCheck =() => { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true)}, []); return isClient } - Eric Burel

1

另一个我认为最简单的解决方案是只使用process.browser,这将仅在客户端运行时为真。

<div>
  {
    process.browser && <Hidden />
  }
</div>

process对象仅在Node中可用。 - Mechanic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接