React SSR - 写个 Demo 一学就会

这篇具有很好参考价值的文章主要介绍了React SSR - 写个 Demo 一学就会。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

React SSR - 写个 Demo 一学就会

今天写个小 Demo 来从头实现一下 reactSSR,帮助理解 SSR 是如何实现的,有什么细节。

什么是 SSR

SSRServer Side Rendering 服务端渲染,是指将网页内容在服务器端中生成并发送到浏览器的技术。相比于客户端渲染(CSR),SSR 一般用于以下场景:

  1. SEO (搜索引擎优化):由于部分搜索引擎对 CSR 内容支持不佳,所以 SSR 可以提升网站在搜索引擎结果中的排名。
  2. 首屏加载速度:由于 SSR 可以在服务器端生成完整的 HTML 页面,用户打开网页时能够更快地看到内容,不会看到长时间的白屏,可以提升用户体验。
  3. 隐藏某些数据:由于 CSR 需要从服务器将数据下载下来进行动态渲染,所以一些数据很容易被他人获取,而 SSR 由于数据到渲染的过程在服务端实现,所以可以用来隐藏一些不想让他人轻易获得的数据。

如何实现

简单的 SSR 其实实现很简单,只需要在服务端导入要渲染的组件,然后调用 react-dom/server 包中提供的 renderToString 方法将该组件的渲染内容输出为字符串后返回客户端即可。

Server 端的组件

下面写一个简单的例子:

服务端代码:

import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';

import App from '../ui/App';

const app = express();

app.get('/', (_: unknown, res: express.Response) => {
    res.send(renderToString(<App />));
});

app.listen(4000, () => {
    console.log('Listening on port 4000');
});

此处要注意服务端需要支持 jsx 语法的解析,我这里直接使用 esno 执行 ts 代码,在 tsconfig.json 中配置 jsx 即可。

其实看到这里就能明白为什么在 SSR 的页面上使用 windowlocalstorage 等浏览器 API 需要放到 useEffect 里了,因为该页面的组件都会被 server 端读取解析,而 server 端并没有这些 API

然后看下 App 组件的代码:

import React, { useCallback } from 'react';

export default () => {
    const log = useCallback(() => {
        console.log('Hello world');
    }, []);

    return (
        <div>
            <p>react ssr demo</p>
            <button onClick={log}>Click me</button>
        </div>
    );
};

启动服务器后 server 端就会使用 renderToString<App /> 渲染成 html 字符串,然后通过 send 返回给前端,下面就是服务端返回的 html 内容:

<div>
    <p>react ssr demo</p>
    <button>Click me</button>
</div>

打开浏览器访问该地址即可看到服务端返回了该 html 片段:

hydrate 复活组件

如果你跟着上面的操作很快就会发现问题:为什么点按钮没法操作了?

其实原因很简单,因为我们只拿到了一个 html 并没有任何的 js,事件绑定等自然是无法实现的,要复活组件的交互我们还需要很重要的一步 - hydrate 也就是常说的水合。

hydrate 即通过 react 将对应的组件重新渲染到 SSR 渲染的静态内容上,类似于 render 差异点在于 render 会忽略 root 元素中现有的 domhydrate 则会复用并会进行内容匹配检查。

Hydration failed because the initial UI does not match what was rendered on the server.

如果遇到上述错误即表示在客户端执行 hydrate 时服务端返回的初始的 domhydrate 接收到的需要进行渲染的 dom 不匹配。

说了这么多我们再来看下代码如何编写,首先要进行 hydrate 我们需要客户端的代码来执行:

import React from 'react';
import { hydrateRoot } from 'react-dom/client';

import App from './App';

hydrateRoot(document.getElementById('root')!, <App />);

然后将该代码进行编译打包,我这里就直接使用 webpack 进行打包:

const path = require('path');

module.exports = {
    entry: './ui/index.tsx',
    output: {
        path: path.resolve(__dirname, 'static'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx']
    },
    module: {
        rules: [
            {
                test: /\.(t|j)sx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-react', '@babel/preset-typescript']
                    }
                }
            }
        ]
    }
};

打包完成后生成一个 bundle.js 即可在客户端使用它来进行 hydrate

然后我们再修改下 server 端的代码:

app.get('/', (_: unknown, res: express.Response) => {
    res.send(
        `
<div id="root">${renderToString(<App />)}</div>
<script src="/bundle.js"></script>
`
    );
});

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

我们在静态内容的外层套上 root 元素,然后在下方引入我们刚刚编译的脚本,然后就可以在客户端看到我们想要的结果:

可以看到事件可以正常触发了。

此处还有个注意点,在 server 端要注意将静态字符串包裹在 root 元素中不要添加换行空格等,不然 reacthydrate 时依旧会因为内容不匹配而提示 Hydration failed(仅在 hydrateRoot 时出现,如果使用 hydrate 不会报错,不过 18 中 hydrate 已经被弃用。)

动态数据

此时有些同学可能发现一些问题:前面的内容所渲染的内容都是静态的,如果要针对用户渲染出不同的内容比如用户信息等如何是好?

其实很简单,只需要在服务端将对应的信息作为 props 进行渲染即可,我们下面使用 userName 模拟一下:

app.get('/', (_: unknown, res: express.Response) => {
    const userName = ['张三', '李四', '王五', '赵六'][(Math.random() * 4) | 0];
    res.send(
        `
<div id="root">${renderToString(<App userName={userName} />)}</div>
<script src="/bundle.js"></script>
`
    );
});

可是客户端要如何与服务端匹配呢?此处有两种解决方案:

  1. 客户端获取对应的信息并在信息获取完成后再进行 hydrate 操作。
  2. 服务端将获取到的信息放在页面中。

可以看出方案 1 会带来明显的延时,所以一般会采用方案 2,实现一般可以使用全局变量或特定标签来实现:

app.get('/', (_: unknown, res: express.Response) => {
    const userName = ['张三', '李四', '王五', '赵六'][(Math.random() * 4) | 0];
    res.send(
        `
<div id="root">${renderToString(<App userName={userName} />)}</div>
<script>
window.__initialState = { userName: '${userName}' };
</script>
<script src="/bundle.js"></script>
`
    );
});
import React from 'react';
import { hydrateRoot } from 'react-dom/client';

import App from './App';

hydrateRoot(document.getElementById('root')!, <App {...window.__initialState} />);

总结

  1. React 中的 SSR 可以通过 renderToString 来实现,但是只能输出静态内容,要让页面支持交互需要搭配 hydrate 使用。
  2. 实现 SSR 时服务端需要支持 jsx 语法的解析,因为服务端也需要读取组件。
  3. hydrate 会检查服务端与客户端的内容是否匹配。
  4. 要实现动态数据需要在客户端与服务端之间做好如何使用初始 props 的约定。

最后

本文的 demo 代码放置在 React SSR Demo 中,可自行取阅。文章来源地址https://www.toymoban.com/news/detail-488881.html

到了这里,关于React SSR - 写个 Demo 一学就会的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • macOS安装Python&&Pycharm详解!保姆级教程,一学就会!

    有需要打包好的PycharmPython激活码和安装包也可直接扫下方 首先登陆python官网:www.python.org/ 点击Download按钮,选择自己电脑的系统,有些时候网页可以自动识别 下载最新的python 版本号是随着版本发展不断演进,上图中显示3.12.2。但都是python3 等待下载完成,打开文件,一直点

    2024年04月25日
    浏览(34)
  • 【一学就会】Facebook脸书视频下载一秒保存手机

    Facebook作为一家全球知名的社交媒体平台和在线社交网络服务公司,提供了一个在线平台,使用户分享照片、视频、状态更新和链接等内容,然而,令人遗憾的是,Facebook没有为用户提供直接将照片和视频保存到本地的功能,因此,无法保存自己喜欢的视频图片。 今天在这里

    2024年02月04日
    浏览(130)
  • postman导入请求到jmeter进行简单压测,开发同学一学就会

    这个事情也是最近做的,因为线上nginx被我换成了openresty,然后接入层服务也做了较大改动,虽然我们这个app(内部办公类)并发不算高,但好歹还是压测一下,上线时心里也稳一点。 于是用jmeter简单压测下看看,这里记录一下。 这次也就找了几个接口来压:登录接口、登录

    2024年04月25日
    浏览(31)
  • React SSG - 也写个 Demo 吧

    上次写了一个 SSR 的 DEMO ,今天写个小 Demo 来从头实现一下 react 的 SSG ,来理解下 SSG 是如何实现的。 SSG 即 Static Site Generation 静态站点生成,是指将在构建时就提前生成静态 HTML 页面,速度很快,一般用于以下场景: SEO (搜索引擎优化):由于部分搜索引擎对 CSR 内容支持不

    2024年02月10日
    浏览(39)
  • 详解二分算法(一学就懂,一写就会)

    小明:\\\"小张,我问你一个问题:在1,3,5,6,7,9这些数中5在那个位置?\\\" 小张:\\\"这还不简单,5在第三个位置!我是按顺序来找的:1,3,5。\\\" 小明:\\\"那我告诉你1~100这些数,让你找其中100这个数,你也从1~100来一个一个数吗?\\\" 小张:\\\"那我会从100~1来数。\\\" 小明:\\\"那如果你是一个机器

    2024年02月16日
    浏览(36)
  • RocketMQ-dockefile制作镜像过程(一学就废)

    下载rockermq包: https://rocketmq.apache.org/zh/download/ source开源和binary二进制安装 开始编写rmqnamesrv的dockerfile 开始编写rmqbroker的dockerfile 生成镜像 查看我实验生成的images 命令启动rockermq服务 这里NAMESRV_ADDR=1.1.1.1:9876是需要指定你安装的rmqnamesrv的ip+端口,-v挂载的日志以及对于的配置

    2024年02月11日
    浏览(42)
  • 一看就会的React入门

    目录 简介 入门案例 直接创建  函数式创建 类实例创建   组件实例三大核心属性 state 代码演示  props refs 1). Facebook开源的一个js库 2). 一个用来动态构建用户界面的js库 3). React的特点     Declarative(声明式编码)     Component-Based(组件化编码)     Learn Once, Write Anywhere(支持客户

    2024年02月05日
    浏览(43)
  • React 第一个Demo

    CTF 加解密合集 CTF Web合集 网络安全知识库 次笔记仅记录学习React过程中的笔记,因为有必要掌握一门前端的框架, 在vue和React中选择了React。 目标: 实现Demo: react.development.js 是react核心库 react-dom.development.js 是跨站库 babel.min.js 是转换库,主要作用是为了将jsx转为js 相对于正

    2024年02月09日
    浏览(27)
  • 在react中使用redux && react-redux的使用demo

    前言: redux是一种状态管理工具,可以存储和操作一些全局或者很多组件需要使用的公共数据。 平心而论,redux的使用对于新上手来说不太友好,多个依赖包的,多种api的结合使用,相对来说比做同样一件事的vuex用起来比较麻烦.不过,熟能生巧,用多了也就习惯了,下面是个人的一个d

    2024年02月06日
    浏览(45)
  • React ISR 如何实现 - 最后的 Demo

    之前写了两个 demo 讲解了如何实现 SSR 和 SSG ,今天再写个 demo 说在 ISR 如何实现。 ISR 即 Incremental Static Regeneration 增量静态再生,是指在 SSG 的前提下,可以在收到请求时判定页面是否需要刷新,如果需要则重新构建该页面,这样既拥有了静态页面的优势又可以避免页面长时间

    2024年02月11日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包