react18-ssr

一、服务端渲染

我们的页面DOM结构由服务端产生的,就是服务端渲染。

const express = require("express");const app = express();app.get("/", (req, res) => {res.send("<html><body><h2>hello</h2></body></html>");
});
app.listen(8300, () => {console.log("程序运行在8300接口");
});

二、客户端渲染

  • 页面上的内容由于浏览器执行js脚本而渲染到页面上。

    • 浏览器访问服务器,服务器返回空的HTML页面,里边有一个js脚本client
    • 浏览器下载js代码,并在浏览器中运行。
    • 内容呈现在页面上。
1、客户端的缺点
  • 首屏速度加载慢。
  • 不支持SEO和搜索引擎优化。
  • 首页需要请求来初始化数据。

三、同构渲染

1、什么是同构渲染?
  • 同构渲染的项目支持服务端渲染和客户端渲染。

  • 第一次访问是服务端渲染(ssr),后边的路由切换访问是客户端渲染(SPA),可以支持爬虫(SEO)。

  • 客户端和服务端同构可以复用一部分代码。

四、创建项目

1、安装依赖
npm install react react-dom webpack webpack-cli babel-loader @babel/core  @babel/preset-env @babel/preset-react --save
  • babel-loader 的作用是相对于一个交通指挥, 只是在webpack打包时遇到js文件,交给babel处理 。
  • @babel/core是我们使用Bable进行转码的核心npm包
  • @babel/preset-env 这是一个智能预设,它允许你使用最新的 JavaScript 语法,而无需对目标环境需要哪些语法转换进行管理。
  • @babel/preset-react 将react转换成js文件包。
2、配置webpck.config.js文件
const path = require("path");module.exports = {mode: "development",devtool: false,entry: "./src/index.js",output: {filename: "main.js",path: path.resolve(__dirname, "build"),},watch: true,module: {rules: [{test: /\.(js|jsx)$/,exclude: /node_modules/,loader: "babel-loader",options: {presets: ["@babel/preset-env", "@babel/preset-react"],},},],},
};
3、配置编译命令
"scripts": {"build": "webpack"}

五、水合(hydrateRoot)

  • 水合是指其他物质溶于水发生化学反应
  • 此处水合是指后端数据达到前端后,js绑定事件,才能够响应用户的操作或者DOM的更新。
  • 工作流程(将事件比作水)
    • 组件在服务器拉取数据,并在服务端首次渲染。
    • 脱水,对组件进行脱水,变成HTML字符串,脱去交互事件,成为风干标本快照。
    • 注水,发送到客户端后,重新注入数据(交互的事件),重新变成可交互组件。

此过程类似于银耳,长成后晒干,然后加入水再泡发。

import React from "react";
import App from "./page/App/index.jsx";
import { hydrateRoot } from "react-dom/client";const root = document.getElementById("root");
const element = <App />
hydrateRoot(root, element);

六、以前的服务端渲染

1、工作流程
  • 服务器内部获取数据。
  • 服务器内部渲染HTML.
  • 客户端从远程加载代码。
  • 客户端开始水合。
2、缺点
  • 一切都是串行的,一个没有结束,后边都要等待。
  • 这个操作必须是整体性的,而水合的过程比较慢,会引起卡顿。
3、项目代码
  • service.js文件
const express = require("express");
const register = require("@babel/register");
register({ignore: [/node_modules/],presets: ["@babel/preset-env", "@babel/preset-react"],plugins: ["@babel/plugin-transform-modules-commonjs"],
});
const static = require('serve-static');
const webpack = require("webpack");
const render = require("./oldRender");
const webpackConfig = require("./webpack.config");webpack(webpackConfig, (error, status) => {const statusJson = status.toJson({ assets: true });const assets = statusJson.assets.reduce((item, { name }) => {item[name] = `/${name}`;return item;}, {});console.log(assets, 'assets')const app = express();app.get("/", (req, res) => {render(req, res, assets);});app.use(static('build'));app.listen(8100, () => {console.log("运行在8100端口");});
});
  • render函数代码
import React from "react";
import App from "./src/page/App";
import { renderToString } from "react-dom/server";function render(req, res, assets) {const html = renderToString(<App />);res.statusCode = "200";res.setHeader("Content-Type", "text/html; charset=utf-8");res.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"><title>ssr</title></head><body><div id="root">${html}</div><script src="${assets["main.js"]}"></script></body></html>`);
}module.exports = render;

注意: 由于render函数中即使用了commonjs,又使用了es的代码,所以,servicejs文件中使用了@babel/register插件进行转换,将es转换成commonjs进行执行。

七、react18的ssr

1、特性
  • 选择性水合,可以在局部进行水合。

  • 像流水一样,打造一个从服务端到客户端的持续不断的渲染管线。而不是renderToString那样一次性渲染。

  • 服务端渲染把简单的res.send改为res.socket这样就把一次渲染转变为持续性行为。

  • 打破了以前串行的限制,优化前端的加载速度和可交互所需等待时间。

  • 服务器端的流式HTML使用 renderToPipeableStream

  • 客户端的水合使用 hydrateRoot ,需要调用接口组件使用<Suspense/>包裹。

  • 需要请求的组件会先返回一个<template></template>占位,然后再替换。

2、新版ssr代码:(加上react-router@6版本)
  • service.js文件不变,render函数做出如下修改:
import React from "react";
import { renderToPipeableStream } from "react-dom/server";
import AppPage from "./src/page/AppPage/index.jsx";
import { StaticRouter } from "react-router-dom/server";function newRender(req, res, assets) {const { pipe } = renderToPipeableStream(<StaticRouter location={req.url}><AppPage /></StaticRouter>,{bootstrapScripts: [assets["main.js"]],onShellReady() {res.statusCode = 200;res.setHeader("Content-Type", "text/html; charset=utf-8");res.write(`<!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"><title>ssr</title></head><body><div id="root">`);pipe(res);res.write(`</div></body></html>`);},});
}module.exports = newRender;
  • 客户端index.js修改如下:
import React from "react";
import AppPage from "./src/page/AppPage/index.jsx";
import { hydrateRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";const root = document.getElementById("root");
const element = (<BrowserRouter><AppPage /></BrowserRouter>
);
hydrateRoot(root, element);
  • AppPage文件如下
import React, { Suspense } from "react";
import NavList from "../NavList/index.jsx";
import routerConfig from "../../route/index.js";
import { useRoutes } from "react-router-dom";export default function AppPage() {return (<div><NavList /><Suspense fallback={<div>加载中。。。</div>}>{useRoutes(routerConfig)}</Suspense></div>);
}
  • routerConfig文件如下
import React from "react";
import Header from "../page/Header/index.jsx";
import Footer from "../page/Footer/index.jsx";
import User from "../page/User/index.jsx";
const routerConfig = [{path: "/",element: <Header />,index: true,},{path: "/footer",element: <Footer />,},{path: "/user",element: <User />,},
];export default routerConfig;
  • NavList组件如下:
import React, { startTransition } from "react";
import { useNavigate } from "react-router-dom";export default function NavList() {const navigate = useNavigate();function handleHistory(url) {startTransition(() => {navigate(url);});}return (<ul><li onClick={() => handleHistory("/")}>主页</li><li onClick={() => handleHistory("/footer")}>底部</li><li onClick={() => handleHistory("/user")}>用户页</li></ul>);
}

注意: 此处没有使用Link,原因是由于如果跳转过于频繁,Suspense中内容还没有渲染结束,会导致报错。需要使用startTransition来降低优先级。

八、useId

  • 客户端和服务端保持一致的id

九、项目搭建附录

1、安装插件
npm install @babel/core @babel/preset-env  @babel/preset-react babel-loader express react-router-dom webpack webpack-cli @babel/plugin-transform-modules-commonjs @babel/register cross-env nodemon react react-dom @babel/plugin-transform-runtime --save
2、webpack.config.js
const path = require("path");module.exports = {mode: "development",devtool: 'source-map',entry: "./index.js",output: {filename: "main.js",path: path.resolve(__dirname, "build"),},watch: true,module: {rules: [{test: /\.(jsx|js)$/,exclude: /node_modules/,use: {loader: "babel-loader",options: {presets: ["@babel/preset-env", "@babel/preset-react"],plugins: ["@babel/plugin-transform-runtime"],},},},],},
};

十、所用项目地址

  • 欢迎参考学习资料项目 项目用例地址

react18的SSR相关推荐

  1. 如何在 React 18中 利用Suspense 实现 服务端渲染(SSR)

    概述 React 18 将包括对 其服务器端渲染 (SSR) 性能的架构做了改进.这些改进带来了实质性的效果,是几年来其团队工作的结晶.大多数的改进点都是在幕后进行的,但您需要了解一些选择加入机制,尤 ...

  2. React + Koa 实现服务端渲染(SSR)

    ⚛️React是目前前端社区最流行的UI库之一,它的基于组件化的开发方式极大地提升了前端开发体验,React通过拆分一个大的应用至一个个小的组件,来使得我们的代码更加的可被重用,以及获得更好的可维护性 ...

  3. R语言计算回归模型的SST、SSR以及SSE指标实战

    R语言计算回归模型的SST.SSR以及SSE指标实战 目录 R语言计算回归模型的SST.SSR以及SSE指标实战 #仿真数据

  4. 路由ssr服务器信息不完整,基于Nuxt构建动态路由SSR服务

    大约两年前曾经分享过基于Vue的SSR框架Nuxt的简单使用<基于Vue.js的SSR方案之Nuxt.js>,今天因为有SSR需求又重新做了一些尝试. 由于目前在做的是一个能够动态构建页面 ...

  5. ssr面板_碧蓝航线:详解SSR里诺强不强 航母专用保姆 三流防空特化型轻巡

    碧蓝航线对于之称的新SSR轻巡里诺,作为金皮来说强度还是让很多指挥官期待的,不过很可惜这位舰娘真的没有想象中那么厉害,来看看对于其强度的具体评价以及分析吧. 基础能力 作为轻巡面板来说,找个最合适的参 ...

  6. 售卖ssr_博人传148集:SSR鸣人卡牌受追捧,“金钱之术”重现,太子被盯上

    <博人传>动画迎来第148集,剧情终于与漫画接轨,与漫画内容也没有什么太大变化,也许是早就看过漫画的原因,少了那么点激动.不过火之国大名圆市休与太子圆天斗的到来,为木叶忍者村带来一道别样的 ...

  7. vue+ssr+axios

    git地址:https://github.com/317482454/vue-axios-ssr 懒懒的撸了一个vue-ssr demo Vue在ssr模式需要注意第三方插件不一定支持,因为先在渲染n ...

  8. 图像增强算法四种,图示与源码,包括retinex(ssr、msr、msrcr)和一种混合算法

    from:http://blog.csdn.net/onezeros/article/details/6342661 两组图像:左边较暗,右边较亮 第一行是原图像,他们下面是用四种算法处理的结果 依次 ...

  9. ssr无法在win10使用_Nuxt SSR中使用WangEditor爬坑—把对象暴打出原型

    记录一下Nuxt SSR中使用WangEditor的问题,这也体现JS原型在架构场景下的重要性. 前提须知: Nuxt SPA 按照文档去使用WangEditor插件是没有问题的,因为页面渲染是在前端 ...

  10. ssr pac_阴阳师新SSR千姬什么时候上线 ssr千姬活动上线时间一览

    阴阳师新SSR千姬什么时候上线?阴阳师全新永生之海版本即将上线,千姬作为新版本的全新SSR式神,什么时候能与各位见面呢?下面为大家介绍一下ssr千姬上线时间,快来看看吧. 阴阳师相关推荐: 阴阳师×菜 ...

最新文章

  1. Mac MySQL配置环境变量的两种方法
  2. 《CCNP TSHOOT 300-135认证考试指南》——6.4节SVI故障检测与排除
  3. linux 内存管理 Transparent HugePages 透明大页 简介
  4. 15个JavaScript 编码小技巧
  5. Vi编辑器的使用方法
  6. 我对if(!this.IsPostBack)的理解
  7. vs2015打开慢的解决方法
  8. cfiledialog 保存 扩展名_吃不完的红薯很难保存?教你一招,放1年不烂不发芽,简单实用...
  9. 进程同步问题(二)——信号量机制
  10. Pyecharts 猎聘招聘数据可视化
  11. WebSocket实现简单多人聊天
  12. C# .net MVC 实战项目 使用wangEditor实现word在线编辑 + 导出到word文档(解决html图片导出到word是个大红叉问题) (六)
  13. 物联网(IoT)及其未来应用方向
  14. Google Perftools简介与使用
  15. Power Query (Power BI)傻瓜式合并资产负债表,史上最好用
  16. postgre 三种试图
  17. MinGW下载与安装配置
  18. 智慧旅游虚拟现实解决方案
  19. 命名实体识别_CodingPark编程公园
  20. 笔记本投屏连接三个显示器,连接三个显示器最终办法

热门文章

  1. 路由器密码破解:路由器登陆密码破解方法(仅用于丢失密码后
  2. Excel如何批量将图片插入到批注中
  3. .net c#通过Exif获取图片信息(参数)
  4. RISC-V MCU应用方案之Little VGL(LVGL)移植(一)
  5. 复杂句变简单句 java_【SAT写作】简单句如何变为复杂句?10招教你快速转变
  6. ios怎么ftp上传文件到服务器,Mac OS通过 FTP工具上传文件的方法
  7. 最大堆(创建、删除、插入和堆排序)图文详解
  8. 代码简洁之道,检测出你代码中的 bug、漏洞、异味
  9. Android TextView 字体颜色渐变
  10. 运筹学与管理科学著名期刊解读