React 实验室:我们一直在做的工作 – 2022年6月
2022年6月15日,由Andrew Clark、Dan Abramov、Jan Kassens、Joseph Savona、Josh Story、Lauren Tan、Luna Ruan、Mengdi Chen、Rick Hanlon、Robert Zhang、Sathya Gunasekaran、Sebastian Markbåge 和 Xuan Huang撰写
🌐 June 15, 2022 by Andrew Clark, Dan Abramov, Jan Kassens, Joseph Savona, Josh Story, Lauren Tan, Luna Ruan, Mengdi Chen, Rick Hanlon, Robert Zhang, Sathya Gunasekaran, Sebastian Markbåge, and Xuan Huang
React 18 花了多年时间才完成,同时也给 React 团队带来了宝贵的经验教训。它的发布是多年来研究和探索多条路径的结果。其中一些路径是成功的;更多的则是死胡同,但这些死胡同又带来了新的见解。我们学到的一个教训是,对于社区来说,在不了解我们正在探索的这些路径的情况下等待新功能是令人沮丧的。
我们通常同时进行多个项目,从比较实验性的到明确界定的都有。展望未来,我们希望开始定期与社区分享我们在这些项目中所做的工作。
🌐 We typically have a number of projects being worked on at any time, ranging from the more experimental to the clearly defined. Looking ahead, we’d like to start regularly sharing more about what we’ve been working on with the community across these projects.
为了设定预期,这不是一个具有明确时间表的路线图。许多项目仍处于积极研究阶段,很难确定具体的发布日期。根据我们所学,它们可能甚至永远不会以当前的形式发布。相反,我们希望与你分享我们正在积极思考的问题字段,以及迄今为止所学到的内容。
🌐 To set expectations, this is not a roadmap with clear timelines. Many of these projects are under active research and are difficult to put concrete ship dates on. They may possibly never even ship in their current iteration depending on what we learn. Instead, we want to share with you the problem spaces we’re actively thinking about, and what we’ve learned so far.
服务器组件
🌐 Server Components
我们在 2020 年 12 月宣布了一个 React 服务器组件的实验演示(RSC)。从那时起,我们一直在完成 React 18 中的依赖,并根据实验反馈进行改进。
🌐 We announced an experimental demo of React Server Components (RSC) in December 2020. Since then we’ve been finishing up its dependencies in React 18, and working on changes inspired by experimental feedback.
特别是,我们正在放弃使用分叉的 I/O 库(例如 react-fetch)的想法,而是采用 async/await 模型以获得更好的兼容性。这在技术上并不会阻碍 RSC 的发布,因为你也可以使用路由进行数据获取。另一个变化是,我们也正在放弃文件扩展名的方法,转而采用标注边界的方式。
🌐 In particular, we’re abandoning the idea of having forked I/O libraries (eg react-fetch), and instead adopting an async/await model for better compatibility. This doesn’t technically block RSC’s release because you can also use routers for data fetching. Another change is that we’re also moving away from the file extension approach in favor of annotating boundaries.
我们正在与 Vercel 和 Shopify 一起合作,以统一 webpack 和 Vite 中共享语义的打包器支持。在发布之前,我们希望确保 RSC 的语义在整个 React 生态系统中保持一致。这是实现稳定版本的主要障碍。
🌐 We’re working together with Vercel and Shopify to unify bundler support for shared semantics in both webpack and Vite. Before launch, we want to make sure that the semantics of RSCs are the same across the whole React ecosystem. This is the major blocker for reaching stable.
资源加载
🌐 Asset Loading
目前,像脚本、外部样式、字体和图片这样的资源通常通过外部系统进行预加载和加载。这可能会使在新的环境中进行协调变得棘手,例如流式传输、服务器组件等。我们正在考虑添加 API,通过 React API 在所有 React 环境中预加载和加载去重的外部资源。
🌐 Currently, assets like scripts, external styles, fonts, and images are typically preloaded and loaded using external systems. This can make it tricky to coordinate across new environments like streaming, Server Components, and more. We’re looking at adding APIs to preload and load deduplicated external assets through React APIs that work in all React environments.
我们也在考虑让这些支持 Suspense,这样你可以让图片、CSS 和字体在加载完成之前阻止显示,但不会阻止流式传输和并发渲染。这可以帮助避免视觉元素弹出和布局移动时的“爆米花效应”。
🌐 We’re also looking at having these support Suspense so you can have images, CSS, and fonts that block display until they’re loaded but don’t block streaming and concurrent rendering. This can help avoid “popcorning“ as the visuals pop and layout shifts.
静态服务器渲染优化
🌐 Static Server Rendering Optimizations
静态站点生成(SSG)和增量静态再生(ISR)是获取可缓存页面性能的好方法,但我们认为可以添加功能以提高动态服务器端渲染(SSR)的性能——特别是当大部分内容可缓存但并非全部时。我们正在探索利用编译和静态流程优化服务器渲染的方法。
🌐 Static Site Generation (SSG) and Incremental Static Regeneration (ISR) are great ways to get performance for cacheable pages, but we think we can add features to improve performance of dynamic Server Side Rendering (SSR) – especially when most but not all of the content is cacheable. We’re exploring ways to optimize server rendering utilizing compilation and static passes.
React 优化编译器
🌐 React Optimizing Compiler
我们在 2021 年的 React Conf 上提供了 React Forget 的早期预览。它是一个编译器,可以自动生成相当于 useMemo 和 useCallback 的调用,以最小化重新渲染的成本,同时保留 React 的编程模型。
🌐 We gave an early preview of React Forget at React Conf 2021. It’s a compiler that automatically generates the equivalent of useMemo and useCallback calls to minimize the cost of re-rendering, while retaining React’s programming model.
最近,我们完成了编译器的重写,以使其更加可靠和强大。这种新架构使我们能够分析和记忆更复杂的模式,例如使用本地修改,并开启了许多新的编译时优化机会,而不仅仅是与记忆化 Hooks 保持同等水平。
🌐 Recently, we finished a rewrite of the compiler to make it more reliable and capable. This new architecture allows us to analyze and memoize more complex patterns such as the use of local mutations, and opens up many new compile-time optimization opportunities beyond just being on par with memoization Hooks.
我们也在开发一个用于探索编译器各个方面的试验场。虽然该试验场的目标是让编译器的开发更容易,我们认为它也会让尝试使用编译器和建立对编译器工作原理的直觉变得更容易。它揭示了编译器内部运作的各种见解,并在你输入时实时呈现编译器的输出。它将在编译器发布时与编译器一起推出。
🌐 We’re also working on a playground for exploring many aspects of the compiler. While the goal of the playground is to make development of the compiler easier, we think that it will make it easier to try it out and build intuition for what the compiler does. It reveals various insights into how it works under the hood, and live renders the compiler’s outputs as you type. This will be shipped together with the compiler when it’s released.
屏幕外
🌐 Offscreen
今天,如果你想隐藏或显示一个组件,你有两个选择。一个是将它完全添加到树中或从树中移除。这种方法的问题是,每次卸载时你的界面状态都会丢失,包括存储在 DOM 中的状态,比如滚动位置。
🌐 Today, if you want to hide and show a component, you have two options. One is to add or remove it from the tree completely. The problem with this approach is that the state of your UI is lost each time you unmount, including state stored in the DOM, like scroll position.
另一种选择是保持组件挂载状态,并使用 CSS 从视觉上切换其外观。这可以保留 UI 的状态,但会带来性能成本,因为每当接收到新更新时,React 必须继续渲染隐藏组件及其所有子组件。
🌐 The other option is to keep the component mounted and toggle the appearance visually using CSS. This preserves the state of your UI, but it comes at a performance cost, because React must keep rendering the hidden component and all of its children whenever it receives new updates.
Offscreen 引入了第三种选项:在视觉上隐藏 UI,但降低其内容的优先级。这个想法在本质上类似于 content-visibility CSS 属性:当内容被隐藏时,它不需要与 UI 的其他部分保持同步。React 可以推迟渲染工作,直到应用的其他部分空闲,或者直到内容再次可见。
🌐 Offscreen introduces a third option: hide the UI visually, but deprioritize its content. The idea is similar in spirit to the content-visibility CSS property: when content is hidden, it doesn’t need to stay in sync with the rest of the UI. React can defer the rendering work until the rest of the app is idle, or until the content becomes visible again.
Offscreen 是一个低级功能,可以解锁高级功能。类似于 React 的其他并发功能,如 startTransition,在大多数情况下,你不会直接与 Offscreen API 交互,而是通过有特定意见的框架来实现如下模式:
🌐 Offscreen is a low level capability that unlocks high level features. Similar to React’s other concurrent features like startTransition, in most cases you won’t interact with the Offscreen API directly, but instead via an opinionated framework to implement patterns like:
- 即时过渡。 一些路由框架已经会预获取数据以加速后续导航,例如在悬停链接时。使用 Offscreen,它们还可以在后台预渲染下一个屏幕。
- 可重用状态。 同样,在路由或标签之间导航时,你可以使用 Offscreen 来保留前一个屏幕的状态,这样你就可以切换回去并从之前中断的地方继续。
- 虚拟化列表渲染。 在显示大量项目列表时,虚拟化列表框架会预渲染比当前可见更多的行。你可以使用 Offscreen 以低于列表中可见项目的优先级预渲染隐藏的行。
- 背景内容。 我们还在探索一个相关功能,用于在后台降低内容的优先级而不隐藏它,就像显示模态覆盖时那样。
过渡追踪
🌐 Transition Tracing
目前,React 有两个性能分析工具。原始 Profiler 显示分析会话中所有提交的概览。对于每个提交,它还显示所有渲染的组件以及它们渲染所花费的时间。我们还有一个在 React 18 中引入的 Timeline Profiler 测试版,它显示组件何时调度更新以及 React 何时处理这些更新。这两个分析工具都帮助开发者识别代码中的性能问题。
🌐 Currently, React has two profiling tools. The original Profiler shows an overview of all the commits in a profiling session. For each commit, it also shows all components that rendered and the amount of time it took for them to render. We also have a beta version of a Timeline Profiler introduced in React 18 that shows when components schedule updates and when React works on these updates. Both of these profilers help developers identify performance problems in their code.
我们意识到,开发者发现单独了解缓慢提交或组件的情况在脱离上下文时并不是特别有用。更有用的是了解实际导致缓慢提交的原因。而且开发者希望能够跟踪特定的交互(例如按钮点击、初始加载或页面导航),以监控性能回退,并了解交互为什么变慢以及如何修复它。
🌐 We’ve realized that developers don’t find knowing about individual slow commits or components out of context that useful. It’s more useful to know about what actually causes the slow commits. And that developers want to be able to track specific interactions (eg a button click, an initial load, or a page navigation) to watch for performance regressions and to understand why an interaction was slow and how to fix it.
我们之前曾尝试通过创建一个交互跟踪 API来解决这个问题,但它存在一些根本性的设计缺陷,降低了追踪交互为何缓慢的准确性,有时甚至导致交互无法结束。由于这些问题,我们最终移除了这个 API。
🌐 We previously tried to solve this issue by creating an Interaction Tracing API, but it had some fundamental design flaws that reduced the accuracy of tracking why an interaction was slow and sometimes resulted in interactions never ending. We ended up removing this API because of these issues.
我们正在为交互跟踪 API 开发一个新版本(暂定名为过渡跟踪,因为它是通过 startTransition 启动的),以解决这些问题。
🌐 We are working on a new version for the Interaction Tracing API (tentatively called Transition Tracing because it is initiated via startTransition) that solves these problems.
新的 React 文档
🌐 New React Docs
去年,我们宣布了新的 React 文档网站的测试版(后来发布为 react.dev)。新的学习材料首先教授 Hooks,并且有新的图表、插图,以及许多互动示例和挑战。我们暂停了这项工作,专注于 React 18 的发布,但现在 React 18 已经推出,我们正在积极完成并发布新的文档。
🌐 Last year, we announced the beta version of the new React documentation website (later shipped as react.dev) of the new React documentation website. The new learning materials teach Hooks first and has new diagrams, illustrations, as well as many interactive examples and challenges. We took a break from that work to focus on the React 18 release, but now that React 18 is out, we’re actively working to finish and ship the new documentation.
我们目前正在撰写关于副作用的详细部分,因为我们听说这是新手和有经验的 React 用户都比较具有挑战性的主题之一。同步副作用 是该系列中发布的第一篇页面,接下来的几周还会有更多内容。当我们开始撰写关于副作用的详细部分时,我们意识到许多常见的副作用模式可以通过向 React 添加一个新的原语来简化。我们在 useEvent RFC 中分享了一些初步想法。它目前处于早期研究阶段,我们仍在不断迭代这个想法。我们感谢社区到目前为止对 RFC 提出的评论,以及对正在进行的文档重写的 反馈 和贡献。我们特别感谢 Harish Kumar 提交和审核了许多关于新网站实现的改进。
🌐 We are currently writing a detailed section about effects, as we’ve heard that is one of the more challenging topics for both new and experienced React users. Synchronizing with Effects is the first published page in the series, and there are more to come in the following weeks. When we first started writing a detailed section about effects, we’ve realized that many common effect patterns can be simplified by adding a new primitive to React. We’ve shared some initial thoughts on that in the useEvent RFC. It is currently in early research, and we are still iterating on the idea. We appreciate the community’s comments on the RFC so far, as well as the feedback and contributions to the ongoing documentation rewrite. We’d specifically like to thank Harish Kumar for submitting and reviewing many improvements to the new website implementation.
感谢 Sophie Alpert 审阅这篇博客文章!