各大网站每日热点汇总/微信朋友圈推广文案
NextJs - 服务端/客户端组件之架构多样性设计
- 前言
- 一. 架构设计
- 1.1 SSR+流式渲染常见错误设计之 - 根页面同步阻塞
- 1.2 架构设计之 - 客户端组件依赖于服务端组件数据
- ① 使用 Redux 完成数据共享
- 1.3 架构设计之 - 单页内的分步骤跳转
- ① 如何做到服务端组件和客户端组件之间的切换
- ② 进行UI切换的时候如何做到状态保持
前言
本篇文章主要讲解不同场景下,我们怎样去设计客户端和服务端组件的交互,或者是怎么去写代码。本篇文章建立于:使用SSR
渲染+Suspense
流式渲染,并且服务端/客户端组件混合使用的基础上讲解的。
一. 架构设计
我们知道,NextJs
的APP
路由模式下,在对应目录下创建一个page.tsx
文件,他就会生成对应的路由,我们可以称page.tsx
为根页面。
在此基础上,我们说下基本准则:
- 根页面(
page.tsx
)一般作为服务端组件,我们常用于获取一些上下文变量。 - 切记不可让根页面作为同步请求获取数据的地方,否则整个页面就会同步阻塞,等待请求返回才能开始渲染。
我们接下来先做个简单的讲解。
1.1 SSR+流式渲染常见错误设计之 - 根页面同步阻塞
在刚开始接触Nextjs
这类具备SSR
渲染的框架的时候,可能容易写出这样的代码:
- 我们在
page.tsx
根页面中同步阻塞获取接口数据,然后将数据通过Props
的形式传递给子组件 - 子组件可能是服务端组件、客户端组件。如图:
这种写法,从逻辑上它并没有任何问题,但是在Suspense
流式渲染的场景下,就没有任何意义。因为阻塞的动作发生在服务端,也就是说:
- 必须阻塞所有的异步接口返回,我们的服务器才会开始渲染组件。
- 哪怕我们的子组件使用
Suspense
包装,也没有任何作用。 - 我们的页面打开来就会白屏阻塞,阻塞时间取决于这个异步接口的等待返回时间。
正确设计如下:
- 我们让异步请求的逻辑,封装在一个粒度尽可能小的服务端组件中,然后使用
Suspense
包装这个服务端组件。 - 这样我们的页面,就不会因为这个请求发生阻塞。就会从上到下,依次渲染相关的组件,而使用
Suspense
包装的,就会返回对应的fallback
效果。
倘若在此基础上,我们的客户端组件,需要用到服务端组件中获取的数据,怎么交互?
1.2 架构设计之 - 客户端组件依赖于服务端组件数据
在上述架构图中,我们可以发现,我们的服务端组件是和客户端组件同一层级的。那么同一层级的就无法采用Props
的方式传递数据。
那么就可能有读者想:那如果我的客户端组件封装到服务端组件中不就好啦?如图:
如果这么做:我们的客户端组件就会随着服务端组件同时具备Suspense
效果,也就是客户端组件必须等待异步请求返回后才能完成渲染。 但是这样的设计是不合理的,因为我们的客户端组件的渲染不应该等待数据返回再完成渲染。
大家别忘了,我们的客户端组件是可以具备State
动态效果的,也就是可以使用useState
这样的勾子函数。因此我们可以做到立刻渲染客户端组件,让相关的数据通过State来传递,完成动态渲染。
那么我们如何做到服务端和客户端组件的数据共享呢?
① 使用 Redux 完成数据共享
我们服务端组件,拿到接口数据后,可以将它丢给一个专门的用于存储State
的客户端组件,这里我们称之为Context Compoent
。它的作用就是:
- 接收服务端传递的接口数据。
- 将接口数据保存在
Redux
中。
这么做的好处:
- 服务端组件的内部渲染,可以直接依赖于接口数据编译为
HTML
,但是切记服务端组件往往只用来做展示,不具备任何的交互(onChange
事件),同时服务端组件一般又通过Suspense
封装,可以完成loading
效果。 - 客户端组件几乎不受服务端组件影响,可以立刻完成渲染,将最基本的UI呈现给用户,而页面相关的数据来自于
Redux
。当ContextComponent
将服务端数据存储到Redux
中后,客户端组件自动完成动态渲染。
备注:这样的架构设计一般能满足大多数的开发需求,当然可能有更好的设计,这里只不过提供一种思路。
1.3 架构设计之 - 单页内的分步骤跳转
那么在这个架构设计基础上,倘若我的页面有这样的功能:
- 页面加载完毕之后,呈现第一页。
- 第一页可以点击:“下一步”,跳转到第二页(同一个
URL
) - 第二页还能够返回到:第一页。同时保持第一页的状态(例如
Checkbox
的勾选、Input
框的内容)
这个功能也就是单页内的分步骤跳转,说白了就是使用同一个URL
,但是具有多页效果。下一页的时候,上一页的状态还要保持。只不过UI呈现的是第二页。
但是想要实现单页内的分步骤跳转,有好几个问题需要解决:
- 我的首屏UI(第一页)是通过
SSR
渲染的,怎么做到下一步的时候,把第一页UI
切换到第二页的UI
?(别忘了,服务端组件是不具备State
效果的) - 如何控制
Redux
的初始化动作只做一次?
① 如何做到服务端组件和客户端组件之间的切换
1.我们在根页面下引入一个RoutePage
页面(客户端组件),然后将服务端组件通过Props
传递下去:
import ServerComponent from "./ServerComponent";
import RoutePage from "./RoutePage";const Parent = () => {return <><RoutePage slot={<ServerComponent/>}/></>
}export default Parent
RoutePage
组件专门用来做UI
切换的,也就是控制渲染第一页还是第二页,然后使用Redux
来获取全局的状态,我们用一个变量来代表当前是第几页(因为本案例只有两页,就用isServer
来表达了)
'use client';
import ClientComponent from "./ClientComponent";
import { ReactNode } from "react";const RoutePage = ({ slot }: { slot: ReactNode }) => {// 假代码const context = useRedux(testState);return <>{/* 如果当前是第一页,就渲染服务端组件,否则渲染客户端组件 */}{context.isServer ? { slot } : <ClientComponent />}</>
}export default RoutePage;
那么isServer
的初始值我们设定为true
,就做到首屏渲染服务端组件了。我们只要在客户端组件和服务端组件中维护这个State
即可完成UI
的切换。
设计结构如下:
备注:
- 服务端组件中需要引入额外的一个客户端组件,专门用来控制
State
。不能在服务端组件中控制State
哦。
② 进行UI切换的时候如何做到状态保持
试想一下,第一页首屏加载的时候,数据必定来自于服务端,服务端组件里面会引用一个ContextComponent
组件,每次渲染的时候都会初始化一遍数据。 假设这里是数据A
倘若第一页有个按钮:加载更多数据。它会发送请求,拉取更多的数据然后呈现在页面上,假设这里获取的数据是:数据B
。
那么此时第一页呈现的数据是 数据A
和 数据B
的一个并集:数据C
。那么问题来了:当我们点击下一步,呈现第二页,再次返回第一页的时候,会做什么操作?
- 第一页重新触发渲染(但是这里不会触发服务器的
SSR
渲染),此时服务端组件通过Props
传递的初始数据:数据A 还在,会重新赋值给Redux
。即导致数据A
会覆盖数据C
。 - 那么回到第一页后,之前的数据就被覆盖了,状态也就被刷掉了。
因此我们需要控制,Redux
的初始化赋值动作只执行一次。
这个就比较好解决了,我们只需要在Redux
中增加一个变量:hasLoadedSSR
一类的标识,代表我们已经SSR
渲染过一次了,在Redux
赋值的时候加个判断即可,以下是ContextComponent
伪代码:
'use client';const ContextComponent = (props)=>{const context = useRedux(testState)const dispatch = useDispatch();const {data} = props;// Redux初始化,如果没有经历过SSR,就完成初始化赋值if(!context.hasLoadedSSR){dispatch({context : {...data,// 再将标识赋值为truehasLoadedSSR: true}})}
}
这样就能防止每次UI切换的时候,初始化状态覆盖当前状态的问题了。
相关文章:

NextJs - 服务端/客户端组件之架构多样性设计
NextJs - 服务端/客户端组件之架构多样性设计 前言一. 架构设计1.1 SSR流式渲染常见错误设计之 - 根页面同步阻塞1.2 架构设计之 - 客户端组件依赖于服务端组件数据① 使用 Redux 完成数据共享 1.3 架构设计之 - 单页内的分步骤跳转① 如何做到服务端组件和客户端组件之间的切换…...
使用 Python 进行 PDF 文件加密
使用 Python 解密加密的 PDF 文件-CSDN博客定义一个名为的函数,该函数接受三个参数:输入的加密 PDF 文件路径input_pdf、输出的解密 PDF 文件路径output_pdf和密码password。https://blog.csdn.net/qq_45519030/article/details/141256661 在数字化时代…...

Spring Boot集成RabbitMQ
目录 1.RabbitMQ简介2.添加依赖3.配置RabbitMQ连接4.DirectExchange4.1 消费者4.2 生产者4.3 测试4.4 一个交换机对多个队列4.5 一个队列对多个消费者 5.FanoutExchange5.1 消费者5.2 生产者5.3 测试 6.TopicExchange6.1 消费者6.2 生产者 1.RabbitMQ简介 RabbitMQ是一个由Erl…...

OLED屏幕制造工艺流程
OLED屏幕制造工艺流程是一个复杂且精细的过程,涉及多个关键步骤以确保最终的显示效果和性能。以下是OLED屏幕制造工艺流程的主要步骤: 1. 衬底制作与准备 材料选择:OLED器件需要一个透明的导电衬底,通常使用玻璃或塑料材料。 清…...

knowLedge-VueCLI项目中环境变量的定义与使用
1. env 1.1简介 在 Vue CLI 创建的项目中,你可以通过 .env 文件来定义环境变量。Vue CLI 支持多种 .env 文件,它们根据文件名中的前缀来决定何时加载和使用这些环境变量。 以下是一些常见的 .env 文件及其用途: .env:在任何环境…...

【C#】 接口 继承
简介 继承是面向对象编程的核心特性之一,它允许我们创建一个类(称为子类)来继承另一个类(称为基类)的属性和方法。 作用 这样,我们可以重用代码,减少重复,并使我们的代码更加模块…...

Self-Supervised Learning(李宏毅老师系列)
自学参考: BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding BERT 论文逐段精读 视频课 课件资料 笔记 一、概述 自监督学习模型与芝麻街~ 参数量 ELMO:94MBERT:340MGPT-2:1542MMegatron&…...

8月16日笔记
只有DNS协议出网场景 DNS 协议是一种请求、应答协议,也是一种可用于应用层的隧道技术。DNS 隧道的工作原理很简单,在进行 DNS 查询时,如果查询的域名不在 DNS 服务器本机缓存中,就会访问互联网进行查询,然后返回结果。…...

苹果Mac电脑——装macOS和Windows双系统的方法
一、实验环境 在Windows系统电脑上制作MacOS启动U盘。准备一个大于16G的U盘。 二、实验步骤 2.1 在Windows系统电脑上制作MacOS启动U盘 MacOS镜像下载 在Windows系统电脑上制作MacOS启动U盘的方法 2.2 U盘插上苹果电脑,安装macOS系统 U盘插上苹果电脑…...

【C++ 面试 - 基础题】每日 3 题(十五)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏&…...

数学建模学习笔记
数学建模学习笔记 现学现卖,随缘更新QwQ 主要根据b站大师兄的视频整理而成,有不懂的可以去看原视频 List 数学建模学习笔记一、 层次分析法1.1 矩阵的一致性及其检验1.2 权重计算1.3 具体流程 二、模糊综合评测2.1 隶属函数2.2 隶属函数的确定方法2.3 模…...

个人可识别信息(PII) AI 去除 API 数据接口
个人可识别信息(PII) AI 去除 API 数据接口 ai / 隐私保护 基于 AI 模型自动去除个人识别信息(PII) 个人信息保护 / AI 模型 。 1. 产品功能 基于自有专业模型进行 PII 自动去除高效处理敏感信息全接口支持 HTTPS(TLS v1.0 / v1.1 / v1.2 /…...

【Python-办公自动化】1秒提取PPT文本内容形成目录保存至WORD
欢迎来到"花花 Show Python",一名热爱编程和分享知识的技术博主。在这里,我将与您一同探索Python的奥秘,分享编程技巧、项目实践和学习心得。无论您是编程新手还是资深开发者,都能在这里找到有价值的信息和灵感。 自我介绍: 我热衷于将复杂的技术概念以简单易懂…...

maven介绍与安装
一. maven概述 1. 关于项目依赖的jar包管理 问题描述: 直接在每个项目的lib文件夹中复制jar包会导致多个问题,包括jar包的重复存放、版本冲突以及手动管理带来的不便和错误。 问题分析: 重复存放:每个项目都保存一份相同的jar…...

瑞友科技项目经理认证负责人杨文娟受邀为第四届中国项目经理大会演讲嘉宾︱PMO评论
全国项目经理专业人士年度盛会 北京瑞友科技股份有限公司项目经理认证负责人杨文娟女士受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾,演讲议题为“瑞友科技项目经理人才培养体系落地实践”。大会将于10月26-27日在北京举…...

Ubuntu基础使用
Ubuntu是一种流行的Linux操作系统。它提供了一个友好的图形界面和许多强大的功能,适用于个人电脑和服务器。一般来说使用Ubuntu都是在虚拟机上运行的。 一、虚拟机的安装 VMware是一家专门提供虚拟化解决方案的公司,而VMware Workstation是该公司开发的…...

知识图谱结构的提示
文章介绍了一种名为“知识图谱结构作为提示”(KG Structure as Prompt)的新方法,该方法旨在增强小型语言模型(SLMs)在知识驱动的因果发现任务中的能力。通过将知识图谱中的结构信息融入到基于提示的学习中,…...

(计算机网络)网络层
目录 一.网络层提供哪种服务 二.两种服务的比较 三.ip协议 四.ip地址 五.ip地址的分类 六.子网掩码 七.路由器介绍 一.网络层提供哪种服务 1.ip地址--唯一的标识互联网上的某一台主机 2. 虚电路:虚拟的电路 二.两种服务的比较 ip数据报,不需要建…...

[upload]-[GXYCTF2019]BabyUpload1-笔记
尝试上传.htaccess和图片和一句话木马提示 php文件提示 响应头可以看到 构造一句话图片木马如下: <script languagephp>eval($_POST[cmd]);</script> 上传成功 必须增加文件夹下jpg后缀解析php .htaccess如下 <FilesMatch "jpg">Set…...

2023卫星视频综述论文Recent Advances in Intelligent Processing of Satellite Video
2023卫星视频综述论文Recent Advances in Intelligent Processing of Satellite Video 1.摘要2.引言3. 文章的定量分析4 难点与挑战5 方法论系统A. 卫星视频观察的特点B. 卫星视频目标跟踪与运动估计C. 卫星视频目标检测D. 卫星视频超分辨率 (VSR)E. 卫星视频目标分割ÿ…...

Mysql的Binlog的数据样例
Binlog(Binary Log)是 MySQL 中的二进制日志,记录了所有更改数据库的操作,包括数据的插入、更新和删除,它是主从复制、数据恢复和审计的重要来源。 以下是一些常见的 Binlog 数据样本和它们的结构: 1. 基…...

基于VS2022+Qt5+C++的串口助手开发
目录 一、前言 二、环境准备 三、创建QT串口项目 编辑 四、串口项目实现 1.ui界面设计 2.添加QT串口模块 3.功能实现 ①串口扫描 ②波特率、停止位等设置 ③接收数据 ④发送数据 五、最终效果 六、总结 一、前言 如果有人之前看过我文章的话应该知道…...

Mysql之视图
视图 创建语法:create [or replace] view 视图名称 as select * from where [with check option] 查询:show create view 视图名称 查看视图数据:select * from 视图名称 修改: 1,可以使用创建的语法更新…...

【开端】Java 分页工具类运用
一、绪论 Java系统中,分页查询的场景随处可见,本节介com.baomidou.mybatisplus.core.metadata.IPage;来分页的工具类 二、分页工具类 public class PageUtils implements Serializable { private static final long serialVersionUID 1L; /**…...

leetcode每日一题48
143.环形链表ii 快慢指针 至于入环点的计算 设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为 an(bc)ba(n1)bnc。 任意时刻,fast 指针走过…...

源码工具文档手册
手册文档工具 TinaSDK开发文档:https://tina.100ask.net/ 开发板使用文档:https://allwinner-docs.100ask.net/ 教程示例 一板懂百板通:https://www.bilibili.com/video/BV1Nx4y1w7AF/?spm_id_from333.999.0.0 T113 LVGLUI开发࿱…...

hive之greatest和least函数
1、greatest函数: greatest(col_a, col_b, ..., col_n)比较n个column的大小,过滤掉null或对null值进行处理,当某个column中是string,而其他是int/double/float等时,返回null; 举例: select g…...

C:数组传参的本质
1、一维数组传参的本质 数组传参是指在函数调用时将数组作为参数传递给函数。 int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };test(arr);return 0;}数组传参只需要写数组名就可以了。注意:数组名是arr,而不是arr[10] 数组传参形参该怎么写呢&am…...

excel 2019版本的index match搜索功能
{TEXTJOIN("", TRUE, IF((sheet2!A:A"文字")*(sheet2!C:CC5), sheet2!G:G, ""))} excel单元格输入公式后: TEXTJOIN("", TRUE, IF((sheet2!A:A"文字")*(sheet2!C:CC5), sheet2!G:G, "")) 按CtrlShi…...

【问题解决】apache.poi 3.1.4版本升级到 5.2.3,导出文件报错版本无法解析
【问题解决】apache.poi 3.1.4版本升级到 5.2.3,导出文件报错无法解析 3.1.4版本代码: /*** 创建workbook* param inp* return* throws Exception*/public Workbook createworkbook(InputStream inp) throws Exception {if (!inp.markSupported()) {inp…...