速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

React 困境与未来,何时迎来自己的“Angular.js 时刻”?

作者 | François Zaninotto

  • 2023-07-17
    北京
  • 本文字数:5047 字

    阅读完需:约 17 分钟

React困境与未来,何时迎来自己的“Angular.js时刻”?

2012 年,横空出世的 Angular.js 一举改变了前端开发的格局,在市场上迅速取得成功。仅仅两年之后,开发团队又推出了 Angular 2,基于新的范式对原始库进行全面重写。但包括我自己在内,不少开发者都不愿修改现有应用来适应新的设计思路。于是在新项目中,Angular.js 不再作为优先选项,市面上其他出色的框架开始迎来自己的机会空间。


29015 年,我们开始在前端开发中使用 React。更简单的架构、对组件的高度关注,以及在大小代码库上始终如一的稳定生产力,让 React 很快成为备受好评的新选择。旺盛的人气之下,React 社区也开始茁壮成长。可最近,React 和 Next.js 团队开始推广其服务端组件——这种新的 Web 应用程序构建方式虽有不少优势,但并不适合大部分现有 React 应用。


难道说 Angular.js 到 Angular 2 的故事又要重演?React 是否正在重蹈前辈的覆辙?


注意:本文主要讨论 React 和 Next.js 团队引入的新功能。由于双方的密切合作,这里难以区分各项功能具体出自哪支团队之手。因此,下文将以“React”统一指代双方团队。


重新学习一切


React 的核心是一套视图库,这一点仍然保持不变:使用 React Server 组件,大家仍可以使用 JSX 构建组件,并以 props 的形式渲染并传递动态内容:


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 对其进行了增强,能够自动请求重复数据删除。为什么一定要这样调整?如果我们需要在组件树中深入访问获取的数据,由于于 useContext 已在服务端组件中被禁用 ,所以无法将 fetch 放置在 React Context 当中。现在若需要在组件树内的不同点处访问获取的数据,推荐方法是在必要时执行重新获取,再通过 React 执行重复数据删除。


这个 fetch 函数还会默认缓存数据,无论响应缓存标头如何。实际获取过程发生在构建过程中。


如果大家希望创建一个按钮来启动 POST 操作,现在需要将其包含在表单内并使用服务端操作,也就是使用带有 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 hooks(包括 useState, useContext, useEffect)现在都会导致服务端组件出错。如果仍须使用,大家只能借助 use client escape 路由,也就是强制 React 在客户端渲染组件。请注意,这本是 Next.js 中的默认操作,但在引入服务端组件之后成了可选功能。



CSS-in-JS 跟服务端组件也不兼容。如果大家习惯了使用 sx 或者 css prop 直接设置组件样式,现在就必须学习 CSS Modules、Tailwind 或者 Sass。对我来说,这种调整更像是倒退:

// in app/dashboard/layout.tsximport 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。


服务端组件的认知模型与客户端 JS 完全不同,只有底层 JSX 保持不变。所以哪怕大家精通 React 开发,在面对服务端组件时还是得重新学习——除非您已经拥有丰富的 PHP 开发经验。


说实话,React 中的新功能大部分处于“Alpha”早期阶段,也许未来会在稳定版发布时得到解决。


缺少开发生态系统


如前所述,现在我们没法用 react-query 进行数据获取。事实证明,它绝不是唯一跟 React 服务端组件不兼容的库。如果各位用惯了以下工具,是时候寻找替代方案了:


  • material-ui,

  • chakra-ui,

  • Emotion,

  • styled-components

  • React-query,

  • swr,

  • react-hook-form,

  • 大部分 SaaS 提供商的 SDK

  • 还有更多。


这些库通通使用标准 React hooks,所以通过服务端组件调用时会出错。



如果大家需要这些库,就只能使用 use client 指令将它们封装在强制客户端渲染的组件当中。


强调一下:React 服务端组件几乎破坏了一切现有 React 第三方库,这些库的作者必须修改代码以重新兼容。有些人会出手,有些人可能放着不管。哪怕是前一种情况,这个过程也需要时间。


所以如果大家使用 React 服务端组件启动应用,那现有 React 生态系统将瞬间不复存在。更要命的是:客户端 React 还提供服务端组件尚未涵盖的多种日常工具。例如,React Context 就是管理依赖项注入的绝佳方案。如果没有 React Context,那服务端组件就需要单独的依赖项注入容器(Dependency Injection Container,类似 Angular 的办法)。如果核心团队不帮忙,这活就得靠技术社区完成。


与此同时,我们还需要手动编写大量代码。想象一下,在没有 UI Kit、表单框架、智能 API 客户端和 SaaS 集成的前提下搞开发,其难度可想而知。


原先的 React 生态系统已经成了该项目最显著的优势,也是 React 得到广泛的普及的根本原因。可如今,React 服务端组件可谓是自毁长城。


没有困难,硬要创造困难


服务端端渲染早就有其成熟方案。服务端端脚本接收请求,获取数据并生成 HTML。客户端渲染也是一样,浏览器检索数据、客户端脚本随后更新 DOM。


但 React 偏要力推服务端端加客户端混合渲染,属于没有困难硬是创造困难。这样大家既可以在服务端组件中使用客户端组件,又可以在客户端组件中使用服务端组件。



当客户端组件渲染服务端组件时,React 服务端不会发送 HTML,而是发送组件树的文本表示。之后,客户端脚本会在客户端上渲染该组件树。


如果大家习惯了使用 HTML 或者 JSON 来调试 AJAX 请求,肯定会对此大吃一惊。下面来看 React 如何用 RSC Wire 格式将更新从服务端组件流式传输至客户端:


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 服务端组件实在晦涩难懂,对大多数开发者而言都难以阅读或调试。这样设计真能提高生产力吗?还是说只会起反作用?


有必要这么折腾吗?


如果单从第一性原理角度出发,那这样修改确有其合理性:使用少量 AJAX 的服务端渲染,能够提高 Web 应用程序的构建效率。Dan Abramov 在 Remix Conf 2023 大会上对 React 服务端组件的开发动机做出了精彩的解释:



这种架构特别适合电子商务网站、博客及其他关注 SEO 的内容为中心类网站。


但这并不是什么新鲜概念。多年以来,Hotwire in Rails 和 Symfony 等应用工具一直在使用这种架构。


此外,服务端组件希望解决的不少问题(包括数据获取、分部渲染等)早已在某些单页应用中有了答案。至于管理面、SaaS、B2B 应用、内部应用、CRM、ERP、长周期运行应用等当中的其他问题(大捆绑包、首次加载缓慢、SEO 等)其实根本不算真正的问题。


正因为如此,大部分 React 开发者才对单页应用架构非常满意。如果真需要做服务端渲染,我们完全可以选择生态系统比 React 服务端组件更成熟的其他工具。


那既然用不上,我们为什么还要认真讨论?


构建 React 应用的标准方法


我想强调的第一点,就是 React 正阻止人们使用单页应用架构。或者更确切地讲,他们不鼓励开发者在不配合框架的前提下使用 React,而他们所推荐的框架会更多强调服务端渲染。



还有第二个问题。React.js 官方文档主要推荐使用 Next.js。


Next.js 官方文档则主要推荐使用 React 服务端组件的 13.4 及更高版本。


换句话说,React 服务端组件已经成为构建 React 应用的默认方式。React 生态系统的新手会习惯于直接使用,但在我看来这一切还没准备好。Dan Abramov 也承认这一点:


“要让新范式真正发挥作用,还需要进行大量工作。”


React 服务端组件要求 router 和 bundler 全面更新换代,但目前这些都处于 alpha 阶段,还远无法适应生产开发的要求。


所以,Next.js 在那急什么劲呢?


我不禁怀疑,Next.js 目前的作法并不是要帮助开发者,而是想帮 Vercel 宣传 React。SPA 本身没有营销空间——一旦编译完成,SPA 就是一个能在任何地方随意托管的 JS 文件。但服务端渲染的应用必须借助服务器才能运行,而服务器显然是可以营销的产品。也许我有点阴谋论的倾向,但除此之外我真的无法理解为什么要如此明目张胆地破坏 React 生态系统。‘


现有应用不受影响


与 Angular.js 到 Angular 2 的过渡不同,React 服务端组件的推出并不算是重大变化。现有单页应用仍可适配最新版本的 React,使用 Pages router 构建的现有 Next.js 应用同样可以正常运行。


因此,对于 React 是否将迎来自己的“Angular.js 时刻”这个问题,答案显然是否定的。但如果大家现在起打算新开一个项目,那会如何选择?是拥有成熟工具和生态系统的健壮架构(单页应用),还是 React 团队强烈推荐的新方案(服务端组件)?这是个艰难的选择,如果人们害怕自己选错,很可能会直接转投其他框架的怀抱。


我个人认为 React 靠单一工具满足所有 Web 开发需求的愿景太过激进——或者说,至少目前的解决思路是有问题的。在我看来,最典型的证明就是 Next.js 文档中的下拉列表——读者可以在 App router(服务端组件)和 Pages router 之间随意选择。如果一款工具为同一种功能提供两种截然不同的实现方法,但它真的还是同一款工具吗?



所以对于“React 过度膨胀的野心是否在损害社区”,我的答案是肯定的。


总结


服务端组件也许的确代表着服务端框架的进步——或者至少在达到生产就绪状态后,应该有其进步意义。但对于更广泛的 React 社区,我担心这股潮流会带来碎片化风险,甚至威胁 React 多年来辛苦建立的江湖地位。


如果开发团队能听到我的声音,那我真诚希望 React 和 Next.js 两家能采取更平衡的方法。希望 React 团队能意识到,单页应用架构是一种非常有效的选项,仍然拥有旺盛的生命力。我也希望看到 Next.js 能在自己的官方文档中淡化对服务端组件的强调,或者至少要明确标注其尚属于“Alpha”功能。


当然,也许事实证明我是错的,服务端组件才是未来。毕竟开发者命里注定要不断适应新的范式,持续变化才是软件行业的永恒本质……谁知道呢?


原文链接:


https://marmelab.com/blog/2023/06/05/react-angularjs-moment.html


相关阅读:

React 开发者们的 Solid.js 快速入门教程

从新 React 文档看未来 Web 的开发趋势

我被 React 劫持了,很痛苦又离不开

如何快速上手 angular.js

2023-07-17 15:277673

评论 1 条评论

发布
用户头像
hh

2023-07-17 22:30 · 陕西
回复
没有更多了
发现更多内容

定制+轻量级低代码:满足客户个性需求的最佳实践

天津汇柏科技有限公司

低代码 软件定制开发 软件开发定制

国产 Web 组态软件在玻璃生产线中的应用

图扑物联

百度智能云千帆 AppBuilder 构建 AI 原生应用开发新范式

百度Geek说

人工智能 百度智能云

AI大模型在电商商家端自定义报表分析中的应用与实践

百度开发者中心

人工智能 电商 大模型

万字图解|深入揭秘 (数据链路层、物理层) 工作原理

云舒编程

IP 物理层 路由 图解网络 数据链路层

WorkPlus移动应用管理平台,助力企业实现高效移动办公

WorkPlus

在游戏里开公司!基于ERNIE SDK的多智能体游戏应用

飞桨PaddlePaddle

百度 paddle 游戏开发 飞桨 飞桨国赛

热更新适配ibatis原理浅析

京东科技开发者

人工智能大模型多场景应用原理解析

百度开发者中心

人工智能 图像识别 大模型

测试管理 | 入班第二个月后拿到4个知名企业Offer,他是怎么做到的?

测吧(北京)科技有限公司

测试

源码交付:定制软件开发的重要保障

SoFlu软件机器人

租赁舞台LED屏的注意事项及问题排除

Dylan

活动 LED显示屏 led显示屏厂家 效果广告

用游戏盾会掉线吗,游戏出现掉线或者卡顿的可能有哪些原因

德迅云安全杨德俊

4个知名企业Offer拿到手软,他是怎么做到的?附面试真题

测试人

软件测试

三个方面浅析数据对大语言模型的影响

华为云开发者联盟

人工智能 华为云 华为云开发者联盟 大语言模型

阿里云 Flink 原理分析与应用:深入探索 MongoDB Schema Inference

Apache Flink

合合信息启信数据发布园区金融解决方案,助力银行精准服务“十四五”特色产业

合合技术团队

大数据 金融 合合信息 启信慧眼

隐私计算 互联互通又一成果,相关代码已在隐语社区发布!欢迎加入隐语标准生态

隐语SecretFlow

技术标准 数据安全 隐私计算 数据要素 互联互通

vivo 海量基础数据计算架构应用实践

vivo互联网技术

大数据

CES 2024的亮点仅仅聚焦AI深度赋能和产业创新吗?| DALL-E 3、Stable Diffusion等20+ 图像生成模型综述

GPU算力

AI大模型低成本快速定制秘诀:RAG和向量数据库

百度开发者中心

人工智能 数据库 大模型

跨境电商如何利用item_get-根据ID取商品详情(shopee.item_get)提升用户体验?

技术冰糖葫芦

API 编排

火山引擎VeDI:新增微信小程序广告A/B实验功能,助力企业降低获客成本

字节跳动数据平台

数据库 大数据 ab测试 企业号 1 月 PK 榜 对比实验

精彩推荐 | 【深入浅出Docker原理及实战】「原理实战体系」零基础+全方位带你学习探索Docker容器开发实战指南(实战技术总结)

洛神灬殇

Docker 容器 云原生 容器技术 2024年第二十一篇文章

申万宏源基于 StarRocks 构建实时数仓

StarRocks

数据仓库 数据分析 实时数仓 StarRocks

文心一言 VS 讯飞星火 VS chatgpt (186)-- 算法导论14.1 3题

福大大架构师每日一题

福大大架构师每日一题

软件测试/测试开发|学习两个个月后拿到4个知名企业Offer,他是怎么做到的?

霍格沃兹测试开发学社

活动回顾|分享成果&展望未来,一起走进隐语年度嘉年华精彩现场叭!

隐语SecretFlow

隐私计算 数据要素流通 隐语开源社区

荣耀开发者大会 2023 · 一张图读懂极致体验分论坛

荣耀开发者服务平台

React困境与未来,何时迎来自己的“Angular.js时刻”?_大前端_InfoQ精选文章