HTML怎么做网站目录,网站建设期末作业,移动商城积分兑换官网,网络平台营销的特点NextJs - 服务端/客户端组件之架构多样性设计 前言一. 架构设计1.1 SSR流式渲染常见错误设计之 - 根页面同步阻塞1.2 架构设计之 - 客户端组件依赖于服务端组件数据① 使用 Redux 完成数据共享 1.3 架构设计之 - 单页内的分步骤跳转① 如何做到服务端组件和客户端组件之间的切换… 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 ParentRoutePage组件专门用来做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切换的时候初始化状态覆盖当前状态的问题了。