NextJs - 服务端/客户端组件之架构多样性设计
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. 卫星视频目标分割ÿ…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
