跳到主要内容
版本:7.x

服务端渲染

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

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

::: warning

目前服务端渲染支持有限。由于缺少诸如媒体查询之类的 API,因此无法提供无缝的 SSR 体验。此外,许多第三方库通常在服务端渲染中无法很好地工作。

:::

先决条件

在遵循本指南之前,请确保你的应用已经在服务端渲染中正常运行。为此,你需要确保以下几点:

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

渲染应用

首先,让我们看一个示例,了解如何在不涉及 React Navigation 的情况下使用 React Native Web 进行服务端渲染

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;
});

你可能还希望为搜索引擎、open graph 等设置正确的文档标题和描述。为此,你可以将 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 选项

const Stack = createNativeStackNavigator({
screens: {
Home: {
screen: HomeScreen,
options: {
title: 'My App',
},
},
Profile: {
screen: ProfileScreen,
options: ({ route }) => ({
title: `${route.params.name}'s 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 以获取当前屏幕的选项。
  • 使用上下文附加更多信息,例如状态代码。