React服务端渲染原理解析与实践

React服务端渲染 (Server-Side Rendering, SSR) 是指在服务端实现页面渲染的技术。相对于客户端渲染(CSR),SSR有着更好的首屏渲染性能、更好的搜索引擎优化(SEO)和更好的社交分享体验,因此在实际项目中使用越来越广泛。

客户端渲染的问题

在客户端渲染模式下,首先浏览器请求到HTML,然后请求到JavaScript文件,随后JavaScript文件加载完毕后,需要渲染 React 组件,最终才能将已经渲染的React组件挂载到页面上。然而,这样的渲染模式会存在一些问题:

  • 传输内容过多:页面会传输一个HTML文件和一个JavaScript文件,并且JavaScript文件需要等到加载完才能展示。这样就会出现内容较少的页面,而加载和渲染时间很长的状况。
  • 首屏渲染较慢:因为客户端渲染需要等到JavaScript加载完成后再进行渲染操作,所以相对来说,页面首屏渲染比较慢,特别是对于网络比较差的用户。
  • SEO不友好:搜索引擎爬虫一般只对普通的HTML文档进行解析,而由JavaScript生成的页面则不会进行解析。也就是说,在客户端渲染模式下,由JavaScript生成的页面无法被搜索引擎收录,会造成SEO的不利影响。
  • 社交分享无法正常展示:与搜索引擎不同,对于社交媒体来说,页面链接预览的图片、文字等数据是根据HTML文档来生成,而不关心JavaScript生成的页面内容。因此,如果页面是由JavaScript生成的,则社交媒体预览时无法正常展示。

React服务端渲染的好处

React服务端渲染采用将所调用的React组件在服务端渲染后生成HTML,首屏渲染的时候就返回生成的HTML到浏览器。这就避免了客户端渲染的问题,具体好处如下:

  • 传输内容减少:浏览器只需要请求HTML,服务端已经将React组件渲染成了标准的HTML,因此减少了请求量,节省的是客户端与服务端之间传输的数据量。
  • 首屏渲染快:由于React组件在服务端就已渲染好了,因此能够避免等待JavaScript加载完毕后再进行渲染操作的状况,能够大大减少页面首次渲染的时间,提升用户体验。
  • SEO更加友好:由于服务端已经将React组件渲染成了标准的HTML文档,能够被搜索引擎进行索检,因此提升了网站的SEO排名。
  • 社交分享无误展示:由于服务端渲染,社交媒体等应用也能够正常预览并展示网站的信息。

SSR 原理

React服务端渲染,就是将React组件在服务端渲染成HTML,而生成HTML的过程可以概括为以下五个步骤:

1.通过请求获取到需要渲染的React组件。
2.创建根组件,并用 Rendering Context 管理生成的 HTML。
3.使用 ReactDOM/server 渲染器,为每一个需要渲染的组件生成 HTML 。
4.根据 Rendering Context 的 HTML 生成结果,生成完整的 HTML 文档。
5.用生成的 HTML 响应请求。

具体的实现可以参考下面的实例:

1. 路由配置

文章详情页会根据URL参数来获取对应的文章数据,并通过props传递给组件显示。因此需要路由配置来绑定url和React组件。

import { BrowserRouter, Route } from 'react-router-dom'
import Article from './Article'

const App = () => (
  <BrowserRouter>
    <Route path="/" component={Article} />
    <Route path="/article/:id" component={Article} />
  </BrowserRouter>
)

2. 获取文章数据

在服务端渲染过程中,需要从数据源获取对应数据,因此网络请求在这里就显得特别重要。可以使用 isomorphic-fetch这个库发起网络请求。

import fetch from 'isomorphic-fetch'

const getArticle = (id) => {
  return fetch(`/api/article/${id}`).then(res => res.json())
}

3. 渲染React组件

在服务端渲染的时候,需要用 renderToString 方法将React组件渲染成HTML字符串。

import React from 'react'
import { renderToString } from 'react-dom/server'
import Article from './Article'
import Layout from './Layout'

const articleHandler = async (req, res) => {
  const { id } = req.params
  const data = await getArticle(id) // 获得数据

  const renderedMarkup = renderToString(
    <Article data={data} /> //习惯性传值方式
  )
  res.send(Layout(renderedMarkup, data)) // Layout 用来渲染 html 外壳
}

4. 完整的HTML生成

最终的HTML需要包含HTML的doctype、Head等信息。这部分的内容可以使用 react-helmet 生成。

import { renderToString } from 'react-dom/server'
import Layout from './Layout'
import Article from './Article'
import { Helmet } from 'react-helmet'

const articleHandler = async (req, res) => {
  const { id } = req.params
  const data = await getArticle(id) // 获得数据

  const html = renderToString(<Article data={data} />)
  const helmet = Helmet.renderStatic()

  const fullHtml = Layout({
    html,
    title: data.title,
    meta: helmet.meta.toString(),
    link: helmet.link.toString(),
    script: helmet.script.toString(),
  })
  res.send(fullHtml)
}

SSR 实践

使用 React 官方脚手架:Create React App 来构建一个包含 SSR 的 React 应用。

首先,安装 Create React App 脚手架

npm i -g create-react-app   # 全局安装
create-react-app my-app --scripts-version=react-scripts-rewired
cd my-app

Create React App 默认是客户端渲染,需要改造成服务端渲染:

npm i express cors

接下来,在 src 目录下新建一个 server.js 文件,实现服务端渲染的逻辑。

import express from "express";
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import App from './App';

const app = express();

app.use(express.static('./build'));

app.get('/*', function (req, res) {
  const context = {};

  const markup = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context}>
      <App />
    </StaticRouter>
  );

  if (context.url) {
    res.redirect(301, context.url);
  } else {
    res.status(200).send(`
       <!DOCTYPE html>
       <html lang="en">
       <head>
         <meta charset="utf-8">
         <meta http-equiv="X-UA-Compatible" content="IE=edge">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
         <title>React App</title>
       </head>
       <body>
         <div id="root">${markup}</div>
         <script src="./static/js/main.js"></script>
       </body>
       </html>
    `);
}
});

创建好服务端渲染的脚本后,可以用 npm run build 命令来打包Server端代码。

npm i -g serve   # 安装 serve 静态文件服务器
npm run build    # 客户端代码和服务端代码都打包到 ./build 目录中

serve -s build   # 启动静态文件服务器

上面的内容就是我们的服务端渲染的核心处理步骤,为了最大效果,我们还需要加上上面两个用户故事里的 SEO 和 社交分享的需求解决。为此我们在客户端和服务器端都使用 react-helmet 解决SEO等问题。

npm i react-helmet

然后,在服务器端可能的每个 URL 访问之前,我们都请求数据控制器获取必要数据。

const fetchData = async () => {
  const res = await fetch(`/api/search?query=${q}`);
  const data = await res.json();
  return data;
};

app.get('/:query', async (req, res) => {
  const data = await fetchData(req.params.query);
  const helmet = Helmet.renderStatic();
  const markup = ReactDOMServer.renderToString(
    <StaticRouter location={req.url}>
      <App data={data} />
    </StaticRouter>
  );

  res.send(template(helmet, markup, data));
});

这里演示一个简单的服务端渲染的例子搜索结果页面的服务端渲染:

import { renderToString } from "react-dom/server";
import React from "react";
import { StaticRouter } from "react-router-dom";
import { Helmet } from "react-helmet";
import App from "../src/App";

const path = require("path");
const fs = require("fs");

// 读取模板文件 html
const HTML_TEMPLATE = fs.readFileSync(
  path.resolve(__dirname, "../build/index.html"),
  "utf8"
);

const helmet = Helmet.renderStatic();
const content = renderToString(
  <StaticRouter location={req.url} context={{}}>
    <App />
  </StaticRouter>
);

const html = HTML_TEMPLATE.replace(
  '<div id="root"></div>',
  `<div id="root">${content}</div>
   ${helmet.meta.toString()}
   ${helmet.title.toString()}
   ${helmet.link.toString()}
   ${helmet.script.toString()}
  `
);

res.send(html);
}

通过上述方式,我们就能够快速完成一个React服务端渲染的项目。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:React服务端渲染原理解析与实践 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • Vue.js项目部署到服务器的详细步骤

    下面来详细讲解“Vue.js项目部署到服务器的详细步骤”。 1. 前置条件 在进行 Vue.js 项目部署之前,需要在服务器上安装 Node.js 和 Git 工具。如果你的服务器已经安装过了,那么可以跳过此步骤。 安装 Node.js: # 安装 Node.js sudo apt-get update sudo apt-get install nodejs…

    node js 2023年6月8日
    00
  • vue.js diff算法原理详细解析

    Vue.js Diff算法原理详细解析 什么是Vue.js的Diff算法? Vue.js是一个基于组件化的视图框架,它通过数据驱动视图的更新。在这个过程中,Vue会对比新旧虚拟DOM树间的差异,并且仅仅更新有变化的DOM元素。而这个通过比较两个虚拟DOM树之间的差异,找到需要更新的节点的过程,我们称之为Vue.js的Diff算法。 Vue.js 2.x中的D…

    node js 2023年6月8日
    00
  • npm script和package-lock.json使用示例详解

    我来为您详细讲解 “npm script和package-lock.json使用示例详解”。 什么是npm script和package-lock.json? 在正式讲解之前,先简单介绍一下npm script和package-lock.json。 npm script npm script是在package.json文件中定义的一组脚本命令。npm scr…

    node js 2023年6月8日
    00
  • 详解nodejs实现本地上传图片并预览功能(express4.0+)

    以下是详解“详解nodejs实现本地上传图片并预览功能(express4.0+)”的完整攻略。 1. 确定目标 本文将讲解如何使用 Node.js 和 Express4.0+ 实现本地上传图片并预览功能。具体来说,我们要实现以下功能: 用户可以在网页上选择一张本地图片,并将其上传至服务器; 上传完成后,网页上会立即显示上传的图片以供用户预览。 2. 编写服务…

    node js 2023年6月8日
    00
  • vue中wangEditor5编辑器的基本使用

    Vue中wangEditor5编辑器的基本使用攻略 安装wangEditor5 通过npm进行安装 npm install wangeditor –save 引入wangEditor 在Vue项目的入口文件main.js中引入wangEditor,并且将它挂载到Vue实例上去。 “` import Vue from ‘vue’ import WangEd…

    node js 2023年6月9日
    00
  • Nest 复杂查询示例解析

    Nest 复杂查询示例解析 简介 Nest 是一个基于 Node.js 平台的开发框架,它利用现代化的 JavaScript 技术为构建可伸缩的服务器端应用程序提供了一种优美且快速的方式。 在 Nest 中,ORM(对象关系映射)库 TypeORM 可以用于构建复杂的 SQL 查询,并通过 Nest 提供的数据访问对象(Data Access Object,…

    node js 2023年6月8日
    00
  • node.js express框架简介与实现

    Node.js是一个基于Chrome V8引擎的JavaScript运行环境,可用于快速构建高性能、可扩展的网络应用程序。Express.js是一个基于Node.js的快速、灵活的Web应用框架。 一、Node.js express框架简介 1.1 什么是Express框架 Express框架是一个快速、开放、极简的Web应用框架,是基于Node.js环境的…

    node js 2023年6月8日
    00
  • nodejs npm包管理的配置方法及常用命令介绍

    Node.js npm包管理的配置方法及常用命令介绍 配置方法 安装 Node.js Node.js官网上提供了常规的安装方式,具体可见https://nodejs.org/zh-cn/download/。不过由于 Node.js 需要依赖于系统的 C++ 编译环境,如果你在安装过程中遇到问题,可以考虑使用编译好的二进制程序安装 Node.js,例如 nvm…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部