React API 和代码重用的演变!( 三 )


将状态和逻辑耦合到 React 中许多状态管理库,如 Redux 或 MobX 将 React 应用的状态和视图分开处理 。这与 React 最初作为MVC 中的“视图”标语保持一致 。随着时间的推移,从全局的单块式存储向更多的位置迁移,特别是使用 render props 的“一切皆为组件”的想法,这也随着转向 hooks 得到了巩固 。
React 演进背后的原则我们可以从这些模式的演变中学到什么呢?哪些启发式可以指导我们做出有价值的权衡?
API 的用户体验框架和库必须同时考虑开发者体验和最终用户体验 。为开发者体验而牺牲用户体验是一种错误的做法,但有时候一个会优先于另一个 。
例如,css in JS库 styled-components,在处理大量动态样式时使用起来非常棒,但它们可能以最终用户体验为代价,我们需要对此进行权衡 。
我们可以将 React 18 和 RSC 中的并发特性视为追求更好的最终用户体验的创新 。这些就意味着更新用来实现组件的 API 和模式 。函数的“snapshotting”属性(闭包)使得编写在并发模式下正常工作的代码变得更加容易,服务端的异步函数是表达服务端组件的好方法 。
API 优于实现上面讨论的 API 和模式都是从实现组件内部的角度出发的 。虽然实现细节已经从 createClass 发展到了 ES6 类,再到有状态函数 。但“组件”这个更高级别的API概念,它可以是有状态的并具有 effect,已经在整个演进过程中保持了稳定性:
return (<ImplementedWithMixins><ComponentUsingHOCs><ThisUsesHooks><ServerComponentWoah /></ThisUsesHooks></ComponentUsingHOCs></ImplementedWithMixins>)专注于正确的原语在React中,组件模型让我们可以用声明式的方式来编写代码,并且可以方便地在本地进行处理 。这使得代码更加易于移植,可以更轻松地删除、移动、复制和粘贴代码,而不会意外破坏其中的任何隐藏的连接 。遵循这个模型的架构和模式可以提供更好的可组合性,通常需要保持局部化,让组件捕获相关的关注点,并接受由此带来的权衡 。与这个模型不符的抽象化会使数据流变得模糊,并使跟踪和调试变得难以理解和处理,从而增加了隐含的耦合 。一个例子就是从类到 hooks 的转换,将分布在多个生命周期事件中的逻辑打包成可组合的函数,可以直接放置在组件中的相应位置 。
小结考虑 React 的一个好方法是将其视为一个库,它提供了一组可在其上构建的低级原语 。React非常灵活,可以按照自己的方式来设计架构,这既是一种福音,也可能带来一些问题 。这也解释了为什么像 Remix 和 Next 这样的高级应用框架如此受欢迎,它们会在React基础之上添加更强烈的设计意图和抽象化 。
React 的扩展心智模型随着 React 将其范围扩展到客户端之外,它提供了允许开发人员构建全栈应用的原语 。在前端编写后端代码开辟了一系列新的模式和权衡 。与之前的转变相比,这些转变更多的是对现有心智模型的扩展,而不是需要忘记之前的范式转变 。
在混合模型中,客户端和服务端组件都对整体计算架构有所贡献 。在服务端做更多的事情有助于提高 web 体验,它允许卸载计算密集型任务并避免通过网络发送臃肿的包 。但是,如果我们需要比完整的服务端往返延迟少得多的快速交互,则客户端驱动的方法会更好 。React 就是从该模型的仅客户端部分演变而来的,但可以想象 React 首先从服务器开始,然后再添加客户端部分 。
了解全栈 React混合客户端和服务端需要知道边界在模块依赖图中的位置 。这样就能够更好地理解代码在何时、何地以及如何运行 。
为此,我们开始看到一种新的React模式,即指令(或类似于“use strict”、“use asm”或React Native中的“worklet”的编译指示),它们可以改变其后代码的含义 。
理解“use client”将此代码放置在导入代码之前的文件顶部,可以表明以下的代码是“客户端代码”,标志着与仅在服务端上运行的代码进行区分 。其中导入的其他模块(及其依赖项)被认为是客户端包,通过网络传输 。
使用“use client”组件也可以在服务端运行 。例如,作为生成初始 html 或作为静态网站生成过程的一部分 。
“use server”指令Action 函数是客户端调用在服务端存在的函数的方式 。可以将“use server”放置在服务器组件的 Action 函数顶部,以告诉编译器应该在服务端保留它 。
// 在服务器组件内部// 允许客户端引用和调用这个函数// 不发送给客户端// server (RSC) -> client (RPC) -> server (Action)async function update(formData: FormData) {'use server'awAIt db.post.update({content: formData.get('content'),})}


推荐阅读