跳至主要内容
版本:6.x

服务器渲染

本指南将介绍如何使用 React Native for Web 和 React Navigation 对您的 React Native 应用程序进行服务器渲染。我们将涵盖以下情况

  1. 根据请求 URL 渲染正确的布局
  2. 根据聚焦屏幕设置适当的页面元数据

先决条件

在您遵循本指南之前,请确保您的应用程序已在服务器上正常渲染。为此,您需要确保以下事项

  • 您使用的所有依赖项都在发布到 npm 之前编译,这样您就不会在 Node 上遇到语法错误。
  • Node 被配置为能够require 资产文件,例如图像和字体。您可以尝试使用webpack-isomorphic-tools 来做到这一点。
  • react-native 被别名为 react-native-web。您可以使用babel-plugin-module-resolver 来实现。

渲染应用程序

首先,让我们看一个使用 React Native Web 进行服务器端渲染 的示例,不涉及 React Navigation。

import { AppRegistry } from 'react-native-web';
import ReactDOMServer from 'react-dom/server';
import App from './src/App';

const { element, getStyleElement } = AppRegistry.getApplication('App');

const html = ReactDOMServer.renderToString(element);
const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());

const document = `
<!DOCTYPE html>
<html style="height: 100%">
<meta charset="utf-8">
<meta httpEquiv="X-UA-Compatible" content="IE=edge">
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover"
>
${css}
<body style="min-height: 100%">
<div id="root" style="display: flex; min-height: 100vh">
${html}
</div>
`;

这里,./src/App 是您拥有 AppRegistry.registerComponent('App', () => App) 的文件。

如果您在应用程序中使用 React Navigation,这将渲染您的主页渲染的屏幕。但是,如果您在应用程序中配置了链接,您需要在服务器上为请求 URL 渲染正确的屏幕,以便它与在客户端渲染的内容相匹配。

我们可以使用ServerContainer 通过在 location 属性中传递此信息来做到这一点。例如,使用 Koa,您可以使用来自上下文参数的 pathsearch 属性。

app.use(async (ctx) => {
const location = new URL(ctx.url, 'https://example.org/');

const { element, getStyleElement } = AppRegistry.getApplication('App');

const html = ReactDOMServer.renderToString(
<ServerContainer location={location}>{element}</ServerContainer>
);

const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());

const document = `
<!DOCTYPE html>
<html style="height: 100%">
<meta charset="utf-8">
<meta httpEquiv="X-UA-Compatible" content="IE=edge">
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover"
>
${css}
<body style="min-height: 100%">
<div id="root" style="display: flex; min-height: 100vh">
${html}
</div>
`;

ctx.body = document;
});

您可能还想为搜索引擎、开放图谱等设置正确的文档标题和描述。为此,您可以将 ref 传递给容器,它将为您提供当前屏幕的选项。

app.use(async (ctx) => {
const location = new URL(ctx.url, 'https://example.org/');

const { element, getStyleElement } = AppRegistry.getApplication('App');

const ref = React.createRef<ServerContainerRef>();

const html = ReactDOMServer.renderToString(
<ServerContainer
ref={ref}
location={location}
>
{element}
</ServerContainer>
);

const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());

const options = ref.current?.getCurrentOptions();

const document = `
<!DOCTYPE html>
<html style="height: 100%">
<meta charset="utf-8">
<meta httpEquiv="X-UA-Compatible" content="IE=edge">
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover"
>
${css}
<title>${options.title}</title>
<body style="min-height: 100%">
<div id="root" style="display: flex; min-height: 100vh">
${html}
</div>
`;

ctx.body = document;
});

确保您在屏幕中指定了 title 选项。

<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{ title: 'My profile' }}
/>

处理 404 或其他状态码

为无效 URL 渲染屏幕 时,我们还应该从服务器返回 404 状态码。

首先,我们需要创建一个上下文,我们将在此上下文中附加状态码。为此,请将以下代码放在一个单独的文件中,我们将在服务器和客户端上导入该文件。

import * as React from 'react';

const StatusCodeContext = React.createContext();

export default StatusCodeContext;

然后,我们需要在 NotFound 屏幕中使用上下文。在这里,我们添加了一个 code 属性,其值为 404,以表示未找到屏幕。

function NotFound() {
const status = React.useContext(StatusCodeContext);

if (status) {
staus.code = 404;
}

return (
<View>
<Text>Oops! This URL doesn't exist.</Text>
</View>
);
}

如果您需要,您也可以在此对象中附加其他信息。

接下来,我们需要创建一个状态对象,并在服务器上的上下文中传递它。默认情况下,我们将code设置为200。然后将该对象传递到StatusCodeContext.Provider中,它应该用ServerContainer包装元素。

// Create a status object
const status = { code: 200 };

const html = ReactDOMServer.renderToString(
// Pass the status object via context
<StatusCodeContext.Provider value={status}>
<ServerContainer ref={ref} location={location}>
{element}
</ServerContainer>
</StatusCodeContext.Provider>
);

// After rendering, get the status code and use it for server's response
ctx.status = status.code;

在使用ReactDOMServer.renderToString渲染应用程序后,如果渲染了NotFound屏幕,status对象的code属性将更新为404

您可以对其他状态代码也采用类似的方法,例如,401表示未授权等。

总结

  • 使用ServerContainer上的location属性根据传入的请求渲染正确的屏幕。
  • ref附加到ServerContainer以获取当前屏幕的选项。
  • 使用上下文附加更多信息,例如状态代码。