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日

相关文章

  • 利用VS Code开发你的第一个AngularJS 2应用程序

    以下是利用VS Code开发AngularJS 2应用程序的完整攻略: 背景介绍 AngularJS 2是一个强大的前端框架,在现代Web开发中被广泛使用。VS Code是一个轻量级的代码编辑器,支持很多编程语言,适合前端开发人员。在本攻略中,我们将介绍如何使用VS Code为AngularJS 2开发一个简单的应用程序。 环境准备 Node.js的安装:我…

    node js 2023年6月8日
    00
  • Node常见的三种安全防范手段详解

    Node常见的三种安全防范手段详解 Node.js虽然广泛应用于Web开发的各个领域,但是它也有一些安全问题,尤其是在网络攻击频发的今天,Node.js和它的应用面临着更多的安全威胁。本文将介绍三种常见的Node.js安全防范手段,帮助开发者确保代码的安全性。 1. 尽量不使用eval()和Function()方法 eval()和Function()方法是一…

    node js 2023年6月8日
    00
  • nodejs实现截取上传视频中一帧作为预览图片

    首先,需要说明的是,实现截取上传视频中一帧作为预览图片需要使用到nodejs和第三方库ffmpeg。下面是完整的实现步骤。 步骤一:安装ffmpeg 在命令行输入以下命令: sudo apt-get install ffmpeg 如果你使用的是Windows系统,可以到ffmpeg官网下载相应的安装包。 步骤二:安装相关库 在nodejs项目中,需要使用到以…

    node js 2023年6月8日
    00
  • node.js实现的装饰者模式示例

    下面是如何实现“node.js装饰者模式示例”的攻略。 什么是装饰者模式 装饰者模式是一种结构设计模式,经常用于在不修改现有对象的情况下,向其添加操作。这种模式可帮助拆分逻辑,使其更加可重用。在装饰者模式中,新的功能是通过将其添加到源对象上而非继承方式来实现的。 装饰者模式的实现 下面是一个实现装饰者模式的示例: // 创建一个简单的对象 const som…

    node js 2023年6月8日
    00
  • Nest.js散列与加密实例详解

    Nest.js散列与加密实例详解 本文将介绍如何在 Nest.js 中使用散列和加密,以保护密码和敏感数据的安全。 什么是散列和加密 散列 散列是一种将任意长度的二进制数据转换为固定长度的哈希值的过程。哈希值通常被用于验证数据的完整性和保密性。散列算法是单向的,这意味着哈希值无法转换回原始数据。 Nest.js 中常用的散列算法包括: bcrypt sha2…

    node js 2023年6月8日
    00
  • nodejs爬虫遇到的乱码问题汇总

    Node.js爬虫遇到的乱码问题汇总 近些年来,Node.js的用户数量急剧增长,因为它可以作为一个强大的后端服务器,但它还可以从网站上抓取数据以及爬取网站。然而,在使用Node.js进行爬取操作时,遇到的最常见问题之一是乱码问题。本文将对Node.js爬虫遇到的乱码问题进行总结,并给出解决方案。 1. 编码格式不同 乱码问题的主要原因之一是编码格式不同。网…

    node js 2023年6月8日
    00
  • WebStorm 发布2021.3重大更新新功能介绍

    WebStorm 发布2021.3重大更新新功能介绍 WebStorm 已经发布了 2021.3 的重大更新版本,并且增加了许多强大的新功能,本文将详细介绍这些新功能以及如何使用它们。 1. 在 JS/TS 模板文字中使用虚拟变量 新版本的 WebStorm 已经支持在 JavaScript 和 TypeScript 的模板文字中使用虚拟变量。这样可以使代码…

    node js 2023年6月8日
    00
  • 详解在不使用ssr的情况下解决Vue单页面SEO问题

    下面是”详解在不使用ssr的情况下解决Vue单页面SEO问题”的完整攻略。 为什么需要在不使用ssr的情况下解决Vue单页面SEO问题 Vue单页面应用(SPA)在开发过程中非常方便,但是它并不适用于搜索引擎优化(SEO)。因为SPA是运行在浏览器中的,它在服务器端只返回一个HTML文件,而网页内容都是通过ajax动态加载的。这种方式使得搜索引擎很难获取到页…

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