赞
踩
【编者按】开源框架 react-admin 和 GreenFrame 创始人,Marmelab CEO François Zaninotto 针对 React 引进的服务器组件的新方法发布了自己的观点,虽然这是构建 Web 应用程序员的新方法,但并不适合现有大多数 React ,意味着现有应用引入该方法就得对应用进行重写,因此他希望React 团队认识到单页应用架构是一个正确的选择,并且短期内不会消失。希望团队能想一个更加折中的方法。
链接:https://marmelab.com/blog/2023/06/05/react-angularjs-moment.html
未经允许,禁止转载!
作者 | François Zaninotto 译者 | 弯月
出品 | CSDN(ID:CSDNnews)
2012年,Angular.js 一经推出便改变了前端开发的格局,迅速取得成功。仅在两年之后,Angular 团队推出了 Angular 2,然而他们根据一组不同的范例重写了整个原始库。许多开发人员,包括我自己,都不想重写现有的应用程序来适应这些新想法。对于新项目来说,Angular.js 不再是首选,因为其他框架也同样出色。
2015 年,我们开始使用 React 来构建前端。React 不仅拥有简单的架构,而且还注重组件,再加上稳定的生产力,无论代码库大小如何都是一个很好的选择。React 大受欢迎,社区发展迅速。最近,React 和 Next.js 团队一直在推广服务器组件,这是一种构建 Web 应用程序的新方法,但不适合大多数现有的 React 应用。
这种变化是否堪比从 Angular.js 到 Angular 2 的转变?React 是否正在重蹈 Angular.js 的覆辙?
注意:在本文中,我将讨论 React 和 Next.js 团队引入的新功能。由于这两个团队密切合作,因此很难说哪个团队负责哪个功能。因此,我将使用“React”来指代这两个团队。
重新学习一切
- function Playlist({ name, tracks }) {
- return (
- <div>
- <h1>{name}</h1>
- <table>
- <thead>
- <tr>
- <th>Title</th>
- <th>Artist</th>
- <th>Album</th>
- <th>Duration</th>
- </tr>
- </thead>
- <tbody>
- {tracks.map((track, index) => (
- <tr key={index}>
- <td>{track.title}</td>
- <td>{track.artist}</td>
- <td>{track.album}</td>
- <td>{track.duration}</td>
- </tr>
- ))}
- </tbody>
- </table>
- </div>
- );
- }
然而,其他一切都随着服务器组件而发生了变化。数据获取不再依赖 useEffect 或 react-query;相反,你应该在异步组件中使用 fetch:
- async function PlaylistFromId({ id }) {
- const response = await fetch(`/api/playlists/${id}`);
- if (!response.ok) {
- // This will activate the closest `error.js` Error Boundary
- throw new Error('Failed to fetch data');
- }
- const { name, tracks } = response.json();
- return <Playlist name={name} tracks={tracks} />;
- }
这个 fetch 函数不是浏览器 fetch。React 对其进行了加强,能够自动去除重复的请求。为什么说这是必要的?如果你需要通过组件树更深入地访问获取的数据,就不能将其放置在 React 上下文中,因为服务器组件中不能使用 useContext。因此,在组件树中的不同点访问同一个获取数据的推荐方法是,根据需要重新获取,并依靠 React 来避免重复请求。
默认情况下,无论响应缓存的标头如何,这个 fetch 函数都会缓存数据。实际的获取过程发生在构建时。
如果你想通过一个按钮来启动一个 POST 动作,就必须在表单中添加 fetch 函数,并使用服务器动作,这意味着使用一个带有 use server 的函数:
- export function AddToFavoritesButton({ id }) {
- async function addToFavorites(data) {
- 'use server';
-
- await fetch(`/api/tracks/${id}/favorites`, { method: 'POST' });
- }
- return (
- <form action={addToFavorites}>
- <button type="submit">Add to Favorites</button>
- </form>
- );
- }
常见的 React 钩子:useState、useContext、useEffect 将导致服务器组件出错。如果你需要这些钩子,就必须使用 use client,强制 React 在客户端渲染组件。注意,现在这个功能是默认启用的,而在Next.js中,甚至在服务器组件出现之前,它就是默认行为了。
CSS-in-JS与服务器组件不兼容。如果你习惯了使用 sx 或 css 属性直接设置组件样式,则必须学习 CSS Modules、Tailwind 或 Sass。在我看来,这种变化感觉像是倒退了一步:
- // in app/dashboard/layout.tsx
- import styles from './styles.module.css';
-
-
- export default function DashboardLayout({
- children,
- }: {
- children: React.ReactNode,
- }) {
- return <section className={styles.dashboard}>{children}</section>;
- }
- /* in app/dashboard/styles.module.css */
- .dashboard {
- padding: 24px;
- }
那么,调试呢?我只能祝你好运。React DevTools 不显示 React 服务器组件的详细信息。你无法在浏览器中检查组件,查看它使用的 props 或其子组件。目前,调试 React 服务器组件只能求助于 console.log。
虽然基础的 JSX 保持不变,但服务器组件的基本思想与客户端 JS 完全不同。即便你是一名精通 React 的开发人员,也不一定对掌握服务器组件有很大帮助。基本上,你必须重新学习所有内容,除非你熟悉 PHP。
注意:公平地说,我提出的大多数问题都涉及到带有“alpha”标签的功能。这些问题可能会在稳定版本发布之前得到解决。
如上所述,获取数据不能再使用 react-query。事实证明,这不是唯一无法结合 React 服务器组件一起使用的库。如果你依赖下面的工具,那么就要开始寻找替代品了:
material-ui
chakra-ui
Emotion
styled-components
React-query
swr
react-hook-form,
适用于大多数SaaS 提供商的 SDK
以及其他等等。
这些库都使用了标准的 React 钩子,从服务器组件调用时会失败。
如果你需要这些库,则必须使用 use client 指令将它们封装在组件中,强制客户端渲染。
强调一下:React 服务器组件破坏了几乎所有现有的 React 第三方库,这些库的作者必须修改代码才能兼容。也许有些作者会修改代码,但有些不会。即使他们愿意修改,也需要一定的时间。
因此,如果你使用 React 服务器组件启动应用,则不能依赖现有的 React 生态系统。
更糟糕的是:客户端 React 提供了很多日常工作需要的工具,但服务器组件中并没有提供。例如,React Context 是管理依赖注入的绝佳解决方案。如果没有 React Context,服务器组件就需要一个依赖注入容器,类似于Angular 的方法。如果核心团队不提供,则不得不依靠社区。
与此同时,你必须手动编写很多代码。在没有 UI Kit、表单框架、智能 API 客户端以及你喜欢的 SaaS 的 React 集成的情况下构建 React 应用会变得非常具有挑战性。
当前的 React 生态系统是其最大的优势。这也是 React 如此广泛使用的原因。而 React 服务器组件打破了这一优势。
服务器端渲染是一个已解决的问题。服务器端脚本接收请求、获取数据并生成 HTML。客户端渲染同样成熟。浏览器检索数据,客户端脚本更新 DOM。
React建议混合服务器端和客户端渲染,感觉就像是在使用黑魔法。你可以在服务器组件中使用客户端组件,反之亦然。
当客户端组件渲染服务器组件时,React 服务器不会发送 HTML,而是发送组件树的文本表示。然后由客户端脚本在客户端渲染组件树。
如果你习惯使用 HTML 或 JSON 调试 AJAX请求,就会大吃一惊。这里由一个 RSC Wire 格式的例子(https://www.plasmic.app/blog/how-react-server-components-work#the-rsc-wire-format),React 使用这种格式将更新从服务器组件流式传输到客户端:
- M1:{"id":"./src/ClientComponent.client.js","chunks":["client1"],"name":""}
- S2:"react.suspense"
- J0:["$","@1",null,{"children":[["$","span",null,{"children":"Hello from server land"}],["$","$2",null,{"fallback":"Loading tweets...","children":"@3"}]]}]
- M4:{"id":"./src/Tweet.client.js","chunks":["client8"],"name":""}
- J3:["$","ul",null,{"children":[["$","li",null,{"children":["$","@4",null,{"tweet":{...}}}]}],["$","li",null,{"children":["$","@4",null,{"tweet":{...}}}]}]]}]
这种格式没有文档,因为它是一个实现细节。
HTTP、JSON 和 JSX 如此流行,可读性功不可没。然而,React 服务器组件破坏了这种模式。
React服务器组件看似非常神秘,对大多数开发人员来说都很难理解或调试。不确定这种变化究竟是会提高生产力,还是会阻碍生产力。
从第一原则触发考虑 Web 开发,可以合理地得出这样的结论:使用少量 AJAX 的服务器端渲染是构建 Web 应用程序的有效方法。Dan Abramov 在 2023年 Remix 大会的演讲中对React 服务器组件背后的动机做出了解释(https://www.youtube.com/watch?v=zMf_xeGPn6s&ab_channel=Remix):
这种架构非常适合电子商务网站、博客以及其他对 SEO 有强烈要求的、以内容为中心的网站。
然而,这并不是一个新概念。多年来, 这种架构一直用于 Rails 中的 Hotwire 或 Symfony 应用程序等工具。
此外,服务器组件旨在解决的一些问题(如数据获取、部分渲染等)已被一些单页应用程序框架解决了,比如我们自己的 react-admin。还有一些问题,比如管理面板、SaaS、B2B 应用程序、内部应用程序、CRM、ERP、长期应用程序等,都算不上真正的问题。
这就是为什么许多 React 开发人员,包括我自己,都非常满意单页应用程序架构。当需要做一些服务器端渲染时,我可能会选择一个生态系统比 React 服务器组件更成熟的工具。
那么,如果我并不需要React 服务器组件,那么又何必如此担忧呢?
我担心的第一个问题是,React 的这种变化是在阻止人们使用单页应用程序架构。或者说,React不鼓励人们在没有框架的情况下使用 React,然而React 框架本身却开始推崇服务器端渲染。
我担心的第二个问题是,官方 React.js 文档建议使用 Next.js。而 Next.js 官方文档建议从 13.4 版开始使用 React 服务器组件。
换句话说,根据官方文档,React 服务器组件是构建 React 应用程序的默认方式。React 生态系统的新手自然会使用它们。
我认为这还为时过早。 Dan Abramov 也认为:
“为了让新范式发挥作用,我们还需要付出大量努力。”
React服务器组件需要新一代路由器、新一代捆绑器。但这些范式仍处于 alpha 阶段,尚未准备好投入生产。
那么,为什么 Next.js 如此激进呢?
我不得不认为,Next.js 的新方向并不是为了帮助开发者,而是为了帮助 Vercel 销售 React。你无法真正围绕 SPA 销售服务:一旦编译,SPA 就是一个可以在任何地方免费托管的单个 JS 文件。但是服务器端渲染的应用需要服务器才能运行。服务器是可以销售的产品。也许我是一个阴谋论者,但我看不出还有其他理由像这样破坏 React 生态系统。
与 Angular.js 到 Angular 2 的过渡不同,React 服务器组件的引入并不是重大变化。现有的单页应用程序仍然可以使用最新版本的 React。使用 Pages 路由器构建的现有 Next.js应用程序也可以正常工作。
那么,问题:“React 会重蹈 Angular.js 的覆辙吗?”答案是,不会。
但如今开始一个新项目,你会选择什么?具有成熟工具和生态系统(单页应用)的健壮架构,还是 React 团队正在强推的新框架(服务器组件)?这是一个艰难的选择,人们可能会寻找替代方案,而不是冒险做错误的选择。
我个人认为,React 希望通过一个工具满足所有 Web 开发人员需求的愿景过于激进,或者说当前的解决方案不正确。Next.js 文档中有一个下拉列表,允许读者在App 路由器(服务器组件)和 Pages 路由器之间进行选择。如果一个工具提供两种截然不同的方法来做同一件事,那么它还是同一个工具吗?
所以,问题:“React 是否会因为过于激进而损害整个社区?”我认为答案是肯定的。
总结也许服务器组件代表服务器端框架的进步,或者至少,等到准备好投入生产,它们真的能够发挥积极的作用。但对于更广泛的 React 社区来说,我害怕这一变化会导致社区分裂,并有可能危及 React 多年来建立的优势。
我希望 React 和 Next.js 团队采用更加折中的方法。我希望 React 团队认识到单页应用架构是一个正确的选择,并且短期内不会消失。我更希望看到 Next.js 在他们的文档中淡化服务器组件,或者至少明确标注“alpha”功能。
也许是我过于急躁,也许这种变化代表了未来的趋势。也许开发人员注定要不断地在范式之间来回转换,这就是这个行业的本质。
推荐阅读:
▶“上4休3”公司不建议新公司模仿;甲骨文组队,欲挑战微软OpenAI组合;Qt Creator 11 Beta发布|极客头条
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。