V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
houzhenhong
V2EX  ›  前端开发

React 18 发布了

  •  3
     
  •   houzhenhong ·
    skywalker512 · 2022-03-30 08:46:31 +08:00 · 4191 次点击
    这是一个创建于 748 天前的主题,其中的信息可能已经有所发展或是发生改变。

    以下为 React 18 官方博客 的翻译

    React 18 现在可以在 npm 上使用了!

    上一篇文章中,我们分享了将你的应用程序升级到 React 18 的分步说明。在这篇文章中,我们将概述 React 18 的新内容,以及它对未来的意义。

    我们最新的主要版本包括开箱即用的改进,如自动批处理,新的 API 如 startTransition ,以及支持 Suspense 的 streaming server-side rendering (流式服务器渲染)。

    React 18 中的许多功能都是建立在我们新的并发渲染器之上的,这种幕后变化释放了强大的新功能。Concurrent React 是可选的 - 只有当你使用并发功能时才会启用 - 但我们认为它将对人们构建应用程序的方式产生重大影响。

    我们花了数年时间研究和开发对 React 并发的支持,我们还特别为现有用户提供一个渐进的采用路径。去年夏天,我们成立了React 18 工作组,以收集来自社区专家的反馈,并确保整个 React 生态系统的顺利升级体验。

    如果你错过了,我们在 2021 年 React Conf 上分享了很多这个愿景:

    下面是对这个版本的完整概述,从并发渲染开始。

    React Native 用户请注意。React 18 将与新的 React Native 架构一起提供给 React Native 。更多信息,请看React Conf 的主题演讲

    什么是并发 React (Concurrent React)?

    React 18 中最重要的一点是,我们希望您永远不必考虑:并发性。我们认为这对应用程序开发人员来说基本上是正确的,尽管对库维护者来说,故事可能有点复杂。

    并发本身并不是一个功能。它是一种新的幕后机制,使 React 能够同时准备多个版本的 UI 。你可以把并发看作是一个实现细节-它的价值在于它所解锁的功能。React 在其内部实现中使用了复杂的技术,比如优先级队列和多重缓冲。但你不会在我们的公共 API 中看到这些概念。

    当我们设计 API 时,我们试图向开发者隐藏实现细节。作为一个 React 开发者,你专注于你想要的用户体验,而 React 处理如何提供这种体验。因此,我们不期望 React 开发者知道并发性在幕后是如何工作的。

    然而,Concurrent React 比典型的实现细节更重要 - 它是 React 核心渲染模型的基础性更新。因此,虽然知道并发是如何工作的并不是非常重要,但在高层次上了解它可能是值得的。

    Concurrent React 的一个关键特性是渲染是可中断的。首次升级到 React 18 时,在添加任何并发功能之前,更新的呈现方式与 React 的早期版本相同——在一个单一、不间断、同步的事务中。使用同步渲染,一旦更新开始渲染,在用户可以在屏幕上看到结果之前,任何东西都不能中断它。

    在并发渲染中,情况并非总是如此。React 可能会开始渲染一个更新,在中间停顿一下,然后再继续。它甚至可能完全放弃一个正在进行的渲染。React 保证,即使渲染被打断,用户界面也会显示一致。为了做到这一点,React 要等整个树完成评估( evaluated ),它他才会执行 DOM 突变。有了这种能力,React 可以在后台准备新的屏幕而不阻塞主线程。这意味着 UI 可以立即响应用户的输入,即使它正处于一个大型的渲染任务中,创造一个流畅的用户体验。

    另一个例子是可重用状态。Concurrent React 可以从屏幕上删除部分用户界面,然后在以后重新使用的状态时将它们添加回来。例如,当用户按 Tab 键离开当前页面并返回时,React 能够将页面恢复到与之前相同的状态。在即将到来的次要版本中,我们计划添加一个名为 <OffScreen> 的新组件来实现此模式。类似地,您将能够使用屏幕外在后台准备新的 UI ,以便在用户显示它之前准备好。

    并发渲染是 React 中一个强大的新工具,我们的大多数新功能都是为了利用它而建立的,包括 Suspense 、transitions 和 streaming server rendering 。但 React 18 只是我们在这个新基础上所要做的事情的开始。

    逐步采用并发功能

    从技术上讲,并发渲染是一个突破性的变化。因为并发渲染是可中断的,当它被启用时,组件的行为会略有不同。

    在我们的测试中,我们已经将成千上万的组件升级到 React 18 。我们发现,几乎所有的现有组件都能在并发渲染下 "正常工作",没有任何变化。然而,其中一些可能需要一些额外的迁移工作。虽然这些变化通常很小,但你仍然有能力按照自己的节奏进行。React 18 中的新渲染行为只在你的应用程序中使用新功能的部分启用。

    整体的升级策略是让你的应用在 React 18 上运行而不破坏现有的代码。然后你可以按照自己的节奏逐渐开始添加并发功能。你可以使用 来帮助在开发过程中发现与并发相关的错误。严格模式不影响生产行为,但在开发过程中,它将记录额外的警告,并重复调用那些预计是空闲的函数。它不会发现一切,但它能有效地防止最常见的错误类型。

    在你升级到 React 18 之后,你将能够立即开始使用并发功能。例如,你可以使用 startTransition 在屏幕之间进行导航,而不阻止用户输入。或者使用 DeferredValue 来节制昂贵的重新渲染。

    然而,从长远来看,我们希望你为你的应用程序添加并发功能的主要方式是使用一个支持并发的库或框架。在大多数情况下,你不会直接与并发的 API 交互。例如,开发者在导航到一个新的屏幕时不再调用 startTransition ,路由器库会自动将导航包裹在 startTransition 中。

    库升级到兼容并发可能需要一些时间。我们已经提供了新的 API ,使库更容易利用并发功能。同时,在我们努力逐步迁移 React 生态系统的过程中,请对维护者保持耐心。

    更多信息,请看我们之前的文章。如何升级到 React 18

    Suspense in Data Frameworks

    在 React 18 中,你可以开始在 Relay 、Next.js 、Hydrogen 或 Remix 等 opinionated 框架中使用 Suspense 进行数据获取。使用 Suspense 进行 hoc data fetching 在技术上是可行的,但仍不建议作为一般策略。

    在未来,我们可能会公开更多的原语( primitives ),让你更容易用 Suspense 访问你的数据,也许不需要使用 opinionated 框架。然而,当 Suspense 被深度整合到你的应用程序的架构中时,它的效果是最好的:你的路由器、你的数据层和你的服务器渲染环境。因此,从长远来看,我们预计库和框架将在 React 生态系统中发挥关键作用。

    和以前的 React 版本一样,你也可以用 Suspense 在客户端用 React.lazy 进行代码分割。但我们对 Suspense 的愿景一直是远远超过加载代码 - 目标是扩展对 Suspense 的支持,以便最终,同样的 Suspense 可以处理任何异步操作(加载代码、数据、图像等)。

    Server Components 仍在开发中

    Server Components 是一个即将推出的功能,它允许开发人员建立跨越服务器和客户端的应用程序,将客户端应用程序的丰富互动性与传统服务器渲染的性能相结合。服务器组件在本质上并不与 并发 React 耦合,但它被设计为与并发功能(如 Suspense 和 streaming server rendering )配合使用效果最佳。

    服务器组件仍然是实验性的,但我们希望在 18.x 小版本中发布一个初始版本。同时,我们正在与 Next.js 、Hydrogen 和 Remix 等框架合作,以推进该提案,并使其准备好被广泛采用。

    React 18 的新内容

    新功能: 自动批处理

    批处理是指 React 将多个状态更新分组到一个重新渲染中,以获得更好的性能。如果没有自动批处理,我们只对 React 事件处理程序内的更新进行批处理。默认情况下,React 不会对 promises 、setTimeout 、native event handlers 或任何其他事件中的更新进行批处理。有了自动批处理,这些更新将被自动批处理。

    // 之前: 只有 React 事件被批处理
    setTimeout(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
      // React 将渲染两次,每次状态更新一次(无批处理)
    }, 1000);
    
    // 之后: 在 promises 、setTimeout 、native event handlers 中都会被批处理
    setTimeout(() => {
      setCount(c => c + 1);
      setFlag(f => !f);
      // React 在最后只会重新渲染一次(这就是批处理!)
    }, 1000);
    

    更多信息,请看这篇关于 React 18 中自动批处理以减少渲染的文章。

    新功能:Transitions

    Transitions (过渡) 是 React 的一个新概念,用于区分紧急和非紧急更新。

    • 紧急更新反映了直接的互动,如输入文字、点击、按压等等。
    • 过渡更新则是将用户界面从一个视图过渡到另一个视图。

    像 输入文字、点击或按压 这样的紧急更新,需要立即响应,以符合我们对物理对象行为方式的直觉。否则他们就会感觉 "不对劲"。然而,转换是不同的,因为用户并不期望在屏幕上看到每个中间值。

    例如,当您在下拉列表中选择一个筛选器时,您希望当您单击时,筛选器按钮本身会立即响应。然而,实际结果可能会单独过渡。一个小小的延迟将是不可察觉的,而且通常是意料之中的。如果在渲染结果之前再次更改过滤器,则只需查看最新结果。

    通常情况下,为了获得最佳的用户体验,一个用户的输入应该同时导致一个紧急的更新和一个非紧急的更新。你可以在输入事件中使用 startTransition API 来告知 React 哪些是紧急更新,哪些是 "过渡"。

    import {startTransition} from 'react';
    
    // 紧急:显示键入的内容
    setInputValue(input);
    
    // 将内部的任何状态更新标记为 过渡
    startTransition(() => {
      // 过渡:显示结果
      setSearchQuery(input);
    });
    

    被 startTransition 包裹的更新被当作非紧急事件来处理,如果有更紧急的更新,如点击或按键,则会被打断。如果一个过渡被用户打断(例如,连续输入多个字符),React 会扔掉未完成的的渲染工作,只渲染最新的更新。

    • useTransition:一个用于启用 过渡 的钩子,包括一个跟踪待定状态的值。
    • startTransition:当钩子不能被使用时,启用 过渡 的方法。

    过渡 将选择进入并发渲染,这允许更新被中断。如果内容重新暂停,过渡 也会告诉 React 继续显示当前内容,同时在后台渲染过渡内容(详见Suspense RFC)。

    See docs for transitions here

    新的 Suspense 功能

    如果组件树的某个部分还没有准备好被显示,Suspense 可以让你声明性地指定它的加载状态。

    <Suspense fallback={<Spinner />}>
      <Comments />
    </Suspense>
    

    Suspense 使 "UI 加载状态 "成为 React 编程模型中的第一类声明性概念。这让我们可以在它上面建立更高层次的功能。

    几年前,我们推出了一个有限的 Suspense 版本。然而,唯一支持的用例是用 React.lazy 拆分代码,而且在服务器上渲染时根本不支持。

    在 React 18 中,我们增加了对服务器上的 Suspense 的支持,并使用并发渲染功能扩展了其功能。

    React 18 中的 Suspense 在与过渡 API 结合时效果最好。如果你在过渡期间暂停,React 将防止已经可见的内容被 fallback 取代。相反,React 会延迟渲染,直到有足够的数据加载,以防止出现糟糕的加载状态。

    更多内容请参见 React 18 中的 RFC for Suspense

    新的客户端和服务器渲染 API

    在这个版本中,我们利用机会重新设计了我们为客户端和服务器上的渲染所暴露的 API 。这些变化允许用户继续使用 React 17 模式下的旧 API ,同时升级到 React 18 的新 API 。

    React DOM 客户端

    这些新的 API 现在都是从 react-dom/client 导出的。

    • createRoot: 新的方法来创建一个根来渲染或卸载。使用它代替 ReactDOM.render 。没有它,React 18 的新功能就不能工作。
    • hydrateRoot: 新的方法来给服务器渲染 hydrate 。使用它代替 R eactDOM.hydrate 与新的 React DOM Server APIs 一起使用。没有它,React 18 的新功能就不能工作。

    createRoot 和 hydrateRoot 都接受一个新的选项,叫做 onRecoverableError ,以防你想在 React 从渲染或 hydrate 过程中发生错误时得到通知,以便 log 。默认情况下,React 会使用reportError,或者在旧的浏览器中使用 console.error 。

    请看 React DOM Client 的文档

    React DOM 服务器

    这些新的 API 现在从 react-dom/server 导出,并且完全支持服务器上的 streaming Suspense 。

    • renderToPipeableStream:用于 Node 环境下的 streaming 。
    • renderToReadableStream:用于现代边缘运行环境,如 Deno 和 Cloudflare Worker 。

    现有的 renderToString 方法继续工作,但不鼓励使用。

    请看 React DOM 服务器的文档。

    新的严格模式行为

    在未来,我们希望增加一个功能,允许 React 在保留状态的同时增加和删除 UI 的部分。例如,当用户从一个屏幕切换到另一个屏幕时,React 应该能够立即显示之前的屏幕。要做到这一点,React 将使用与之前相同的组件状态来卸载和重新装载树。

    这个功能将给 React 应用带来更好的开箱即用的性能,但需要组件对效果被多次挂载和销毁有弹性。大多数 effects 将在没有任何变化的情况下工作,但有些 effects 假设它们只被挂载或销毁一次。

    为了帮助发现这些问题,React 18 为严格模式引入了一个新的仅用于开发的检查。这个新的检查将自动卸载并重新挂载每个组件,每当一个组件第一次挂载时,在第二次挂载时恢复之前的状态。

    在这个,React 会挂载组件并创建 effects 。

    * React 挂载组件。
      * Layout effects 被创建
      * Effects 被创建
    

    在 React 18 的严格模式下,React 将模拟在开发模式下卸载和重新安装组件:

    * React 挂载组件。
      * Layout effects 被创建
      * Effects 被创建
    * React 模拟卸载该组件。
      * Layout effects 被销毁
      * Effects 被销毁
    * React 模拟用以前的状态挂载组件。
      * Layout effects 被创建
      * Effects 被创建
    

    See docs for ensuring resusable state here.

    新 Hooks

    useId

    useId 是一个新的 hooks ,用于在客户端和服务器上生成唯一的 ID ,同时避免了 hydrate 不匹配。它主要适用于与需要唯一 ID 的可访问性 API 集成的组件库。这解决了一个在 React 17 及以下版本中已经存在的问题,但在 React 18 中更加重要,因为新的 streaming server renderer 是不按顺序地渲染 HTML 的。请看这里的文档

    useTransition

    useTransition 和 startTransition 让你把一些状态更新标记为不紧急。其他状态更新在默认情况下被认为是紧急的。React 将允许紧急状态更新(例如,更新一个文本输入)打断非紧急状态更新(例如,渲染搜索结果列表)。请看这里的文档

    useDeferredValue

    useDeferredValue 让你延迟重新渲染树的一个非紧急部分。它类似于 debouncing ,但与之相比有一些优势。没有固定的时间延迟,所以 React 会在第一次渲染反映在屏幕上后立即尝试延迟渲染。延迟渲染是可中断的,不会阻止用户输入。请看这里的文档

    useSyncExternalStore

    useSyncExternalStore 是一个新的 hooks ,它允许外部存储支持并发读取,强制更新到存储是同步的。在实现对外部数据源的订阅时,它消除了对 useEffect 的需求,并被推荐给任何与 React 外部状态集成的库。请看这里的文档

    useSyncExternalStore 的目的是供库使用,而不是供应用程序代码使用。

    useInsertionEffect

    useInsertionEffect 是一个新的 hooks ,允许 CSS-in-JS 库解决在渲染中注入样式的性能问题。除非你要写一个 CSS-in-JS 库,否则我们不希望你使用这个。这个钩子将在 DOM 被突变后运行,但在布局效果读取新的布局之前。这解决了一个在 React 17 及以下版本中已经存在的问题,但在 React 18 中更加重要,因为 React 在并发渲染时向浏览器让步,给它一个重新计算布局的机会。请看这里的文档

    useInsertionEffect 的目的是供库使用,而不是供应用程序代码使用。

    22 条回复    2022-04-12 21:14:47 +08:00
    linshuizhaoying
        1
    linshuizhaoying  
       2022-03-30 09:19:55 +08:00
    好的 不学 新特性全部用不到
    irytu
        2
    irytu  
       2022-03-30 09:26:19 +08:00 via iPhone
    好的 不学 新特性全部用不到
    zythum
        3
    zythum  
       2022-03-30 10:29:25 +08:00
    起项目,别装错版本。
    sweetcola
        4
    sweetcola  
       2022-03-30 10:34:28 +08:00
    好耶 终于出来了
    Bazingal
        5
    Bazingal  
       2022-03-30 10:41:27 +08:00
    不支持乌克兰能用吗
    TWorldIsNButThis
        6
    TWorldIsNButThis  
       2022-03-30 10:42:55 +08:00
    什么时候改名叫 ReactOS
    cxe2v
        7
    cxe2v  
       2022-03-30 11:34:07 +08:00
    Server Components? 服务器控件?这不是又搞回去了吗?当年 ASP.NET 的服务器控件被抛弃,现在又捡回来?实在是没得事干了吧
    linglin0924
        8
    linglin0924  
       2022-03-30 11:41:24 +08:00
    @cxe2v 历史是个轮回
    yyfearth
        9
    yyfearth  
       2022-03-30 11:54:05 +08:00
    @cxe2v 而现在的服务器渲染 其实是服务器“预渲染” 实际上是一种服务器端加速的功能 而不是所有 UI 都让服务器去渲染

    技术从来就是一个轮回一个轮回的
    但是想法和解决方案类似 实现和效果却很不同
    不同时代技术的基础不同
    服务器控件的想法是很好而且很成熟的 但是当时浏览器还是 IE 根本没有完整的 Webapp 的体验
    服务器渲染都需要不停刷新浏览器 在 Ajax 成熟 加上 html5 之后就没有优势了
    zythum
        10
    zythum  
       2022-03-30 12:36:57 +08:00
    @cxe2v 其实不是服务端控制。你就认为是一个高级的 支持异步数据渲染的模版引擎(厉害的 ejs )。
    VikingX
        11
    VikingX  
       2022-03-30 12:57:21 +08:00
    @cxe2v 螺旋上升
    muzuiget
        12
    muzuiget  
       2022-03-30 13:11:03 +08:00
    越来越复杂了,我基本上只用了入门教程那几个特性,其它特性一点都用不上,所以我倒希望它出个 lite 版。
    rrni
        13
    rrni  
       2022-03-30 13:15:18 +08:00
    小白感觉莫大的压力,17 还没学明白呢,18 又出来了
    bigsma11
        14
    bigsma11  
       2022-03-30 13:29:42 +08:00 via iPhone
    @Bazingal 不能用,别用了
    cxe2v
        15
    cxe2v  
       2022-03-30 13:36:48 +08:00
    @yyfearth #9
    @zythum #10
    你们的意思是 react 这个 server component 是将需要渲染的东西由服务端渲染后,然后发回浏览器进行局部更新吗?
    zythum
        16
    zythum  
       2022-03-30 13:53:54 +08:00
    @cxe2v server component 他不更新。静态的。输出过程有 js runtime 处理,但是输出完就是静态的了。没有二次的事情。
    rioshikelong121
        17
    rioshikelong121  
       2022-03-30 14:24:27 +08:00
    关于 useInsertionEffect 的执行时机:


    API 文档里面说: https://reactjs.org/docs/hooks-reference.html#useinsertioneffect

    The signature is identical to useEffect, but it fires synchronously before all DOM mutations.

    但是 Blog 上说在 DOM 突变以后执行.

    This hook will run after the DOM is mutated, but before layout effects read the new layout.


    --------------
    kimown
        18
    kimown  
       2022-03-30 14:27:41 +08:00
    之前强制推 hooks 就弃了,更新了一堆没用的 api ,但是在最终页面没任何区别
    yyfearth
        19
    yyfearth  
       2022-03-30 16:28:53 +08:00
    @cxe2v 其实就是把前端的 React 代码先在服务器预渲染一遍 变成一个包含内容的静态文件
    这样效率高 也利于缓存
    你可以理解这样就相当于把 JSX 当成了一个模版了 和 php jsp 类似

    如果是纯静态页面 这样就结束了 服务器吧 jsx 渲染成了 html 丢给浏览器
    当然 里面还是可以有 JS 的动态部分的 这样就和普通的 React webapp 一样了 只不过服务器已经把一部分内容渲染完了

    不过不会第二次再去服务器渲染 这一点和 ASP.net 还是很不一样的
    cxe2v
        20
    cxe2v  
       2022-03-30 16:48:19 +08:00
    @yyfearth #19 你这说的是 SSR 啊,应该不是这次提出的 Server Component
    IvanLi127
        21
    IvanLi127  
       2022-03-30 21:02:15 +08:00 via Android
    感觉挺有吸引力的
    sjhhjx0122
        22
    sjhhjx0122  
       2022-04-12 21:14:47 +08:00
    越来越喜欢 angular 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3213 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 14:26 · PVG 22:26 · LAX 07:26 · JFK 10:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.