ReactJS服务器端渲染与客户端渲染

161

我刚开始学习ReactJS,发现它可以提供两种页面渲染方式:服务器端和客户端。但是,我不理解如何同时使用它们两个。这是两种分开构建应用的方式,还是可以同时使用它们两个?

如果我们可以同时使用它们,那么应该怎样做呢?需要在服务器端和客户端都复制相同的元素吗?还是只需在服务器端构建应用的静态部分,在客户端构建动态部分,而不与已经预渲染的服务器端进行任何连接?


2
简短回答,不行 - 你可以解耦、发送静态 HTML 并在客户端渲染时完全更改它。我在我的回答中添加了详细信息。 - Kira
为了巩固你的理解,请查看这篇博客文章:https://kulkarniankita.com/react/react-server-client-components - fscore
4个回答

146
对于给定的网站/网络应用程序,您可以在客户端、服务器端或两者都使用React。
客户端: 在此,您完全在浏览器上运行ReactJS。这是最简单的设置,并包括大多数示例(包括http://reactjs.org上的示例)。服务器呈现的初始HTML是一个占位符,一旦所有脚本加载完毕,整个UI就在浏览器中呈现。
服务器端: 在这里,将ReactJS视为服务器端模板引擎(例如jade、handlebars等)。服务器呈现的HTML包含了应该有的UI,您不需要等待任何脚本加载。如果没有执行任何javascript,您的页面可以被搜索引擎索引。
由于UI在服务器上呈现,因此您的事件处理程序都不起作用,也没有互动性(您有一个静态页面)。
两者都使用:
在这里,初始渲染在服务器上完成。因此,浏览器接收到的HTML显示用户界面应该是正确的。一旦脚本加载完成,虚拟DOM将再次重新渲染,以设置组件的事件处理程序。
在这里,您需要确保使用与服务器端渲染时相同的props重新渲染完全相同的虚拟DOM(根ReactJS组件)。否则,ReactJS将抱怨服务器端和客户端端的虚拟DOM不匹配。
由于ReactJS在重新渲染之间对虚拟DOM进行差异比较,因此实际的DOM不会发生变化。只有事件处理程序绑定到实际的DOM元素。

1
那么在“both”的情况下,我需要编写相同的代码两次,一次用于服务器渲染,一次用于在客户端上重现此DOM?对吗? - Simcha
13
您需要在服务器和客户端上运行相同的代码两次。但是,您需要编写组件以考虑此问题 - 例如,在componentWillMount()中不应进行任何异步数据获取,因为它将在客户端和服务器上运行。您还需要一种策略,在服务器上预先获取数据并使其可用于客户端的初始呈现,以确保获得相同的输出。 - Jonny Buchanan
4
您也可以使用 typeof window == "undefined" 来检查正在执行的代码是在服务器端还是客户端,然后相应地获取您的数据。 - Gautham Badhrinathan
在你的答案中提到了“客户端”部分,你提到了“服务器渲染的初始HTML”。这听起来更像是“两者兼备”。服务器可以为真正的客户端设置提供静态HTML吗? - Ian W
1
@IanW 通常在这种情况下,服务器返回的HTML非常“简单”,只需导入您的JavaScript和样式,并包含一个React将写入的<div> - Matt Holland

72

图片来源: 沃尔玛实验室工程博客

SSR

CSR

NB:SSR(服务器端渲染)、CSR(客户端渲染)。

最主要的区别在于,使用 SSR 时,服务器响应客户端浏览器时,会包含待渲染页面的 HTML。 值得注意的是,虽然使用 SSR 可以更快地呈现页面,但在 JS 文件被下载并且浏览器执行 React 之前,页面将无法进行用户交互。

一个缺点是 SSR 的 TTFB(首字节时间)可能会稍长。这是可以理解的,因为服务器需要一些时间来创建 HTML 文档,从而增加了服务器响应的大小。


5

我其实也在研究同样的问题,查了很多资料。虽然评论中已经给出了你要找的答案,但我觉得它应该更加突出,因此我写下了这篇文章(一旦我能想出更好的解决方案,我会更新它,因为我认为当前的解决方案至少存在一些问题)。

你需要编写组件时要考虑两种方式,基本上到处都要放置if开关来确定你是在客户端还是服务器端,然后根据情况进行数据库查询(或其他适当的服务器操作)或REST调用(在客户端)。接着,你需要编写端点来生成数据并将其暴露给客户端,就可以了。

如果有更简洁的解决方案,欢迎分享。


3
“这两种构建应用程序的方式是分开的,还是可以一起使用?” “它们可以一起使用。” “如果我们可以一起使用,如何操作-我们需要在服务器端和客户端重复相同的元素吗?或者,我们可以只在服务器上构建应用程序的静态部分,在客户端构建动态部分,而不与已经预渲染的服务器端连接?” “最好渲染相同的布局,以避免重新流动和重绘操作、减少闪烁/闪烁,您的页面将更加平滑。但是,这并不是限制。您完全可以缓存SSR HTML(Electrode采用此方法来缩短响应时间)/发送静态HTML,该HTML将被CSR(客户端渲染)覆盖。”
如果你刚开始学习SSR,我建议从简单的开始,因为SSR很快就会变得非常复杂。在服务器上构建html意味着失去了像window、document这样的对象(在客户端上有这些对象),失去了合并异步操作的能力(默认情况下),并且通常需要大量代码编辑才能使你的代码符合SSR兼容性(因为你必须使用webpack来打包你的bundle.js)。像CSS导入、require vs import这样的东西突然开始困扰你(这在没有webpack的默认React应用中不是这种情况)。
SSR的一般模式如下。一个Express服务器处理请求:
const app = Express();
const port = 8092;

// This is fired every time the server side receives a request
app.use(handleRender);
function handleRender(req, res) {
    const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    console.log('fullUrl: ', fullUrl);
    console.log('req.url: ', req.url);

    // Create a new Redux store instance
    const store = createStore(reducerFn);

    const urlToRender = req.url;
    // Render the component to a string
    const html = renderToString(
        <Provider store={store}>
            <StaticRouter location={urlToRender} context={{}}>
                {routes}
            </StaticRouter>
        </Provider>
    );
    const helmet = Helmet.renderStatic();

    // Grab the initial state from our Redux store
    const preloadedState = store.getState();

    // Send the rendered page back to the client
    res.send(renderFullPage(helmet, html, preloadedState));
}

我给刚开始学习SSR的人的建议是提供静态HTML。您可以通过运行CSR SPA应用程序来获取静态HTML:

document.getElementById('root').innerHTML

不要忘记,使用SSR的唯一原因应该是:

  1. SEO
  2. 更快的加载(我会忽略这个)

技巧:https://medium.com/@gagan_goku/react-and-server-side-rendering-ssr-444d8c48abfc


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