React18新特性尝鲜

React18新增的特性

1. Automatic batching

在React中多次的setState合并到一次进行渲染。

也就是说,setState并不是实时修改State的, 而是将多次的setState调用合并起来仅出发一次渲染,即可以减少数据状态存在中间值导致的不稳定性,也可以提升渲染性能,可以理解为如下代码:

  const handleClick = () => {
    setName("小帅")
    setAge(22)
    // 仅触发一次渲染 
  }
复制代码

但是,在React18以前,异步函数中的setState并不会执行合并,由于丢失上下文,无法做到合并处理,所以每次的setState调用都会立即出发一次重新渲染,除了重复setState,React18带来的优化就是可以再任何情况下进行渲染优化了(异步回调函数,promise, 定时器)的回调函数中调用多次的setState也进行了合并渲染。

当然如果你非要setState调用后立即重新渲染也行,只需要用flushSync包裹:

  import {flushSync} from "react-dom"

  const handleClick = () => {
    flushSync(() => {
      setName("小帅") // 立即渲染
    })
    setAge(22)
  }
复制代码

开启这个特性的前提是,将ReactDom.render 替换成 ReactDom。createRoot调用方式

2.新的ReactDom Render API

升级的方式很简单

import ReactDOM from 'react-dom/client';
const container = document.getElementById("app");

// 旧 render API
ReactDOM.render(<App tab="home" />, container);

// 新 createRoot API
const root = ReactDOM.createRoot(container);
root.render(<App tab="home" />);
复制代码

Concurrent APIS

Concurrent Mode 就是一种可中断渲染的设计架构,什么时候终端渲染呢?当一个更高优先级渲染到来时,通过放弃当前的渲染,立即执行更高优先级的渲染,换来视觉上更快的响应速度。

有人会说,不对呀,中端渲染后,之前的渲染CPU执行不就浪费了吗?换句话说,整体执行时长增加了。这句话是对的,但实际上用户对页面的交互及时性的感知是分为两种的,第一种是即时输入反馈。第二种是这个输入带来的副作用反馈,比如更新列表。其中,即使输入反馈只要能优先满足,即时副作用反馈慢一些,也会带来更好的体验,更不用说副作用反馈大部分情况会因为输入反馈的变化而作废。

由于React将渲染DOM树机制改为了两个双向链表,并且渲染树指针只有一个,指向其中一个链表,因此可以在更新完全发生后在切换指针指向,而在指针切换之前,随时可以放弃对另一棵树的修改。

startTransition

首先看一下他的基本用法

 import { startTransition } from "react";
   const handleClick = () => {
    // 标识为非紧急更新
    startTransition(() => {
      setName("小帅")
    })
    setAge(22)
  }
复制代码

简单来说, 就是被startTransition回调包裹的setState触发的渲染被标记为不紧急的渲染,这些渲染可能被其他的紧急渲染抢占。

举个例子,当setName更新的列表内容很多,导致渲染是CPU占用100%时,此时用户进行一个输入,即触发了由setAge引起的渲染,辞职seName引起的渲染会立即停止,转而对setAge进行渲染支持,这样用户的输入就能快速的反映在UI上,代价是设置name的UI响应会慢一点。而一个transition被打断状态可以听过isPending访问到:

import { useTransition } from "react";
const [isPending, startTransition] = useTransition();
复制代码

SSR for Suspense

完整名称是: Streaming SSR with selective hydration

其实就是像流水一样,打造一个从服务端到客户端持续不断的渲染管线,而不是renderToString那样一次性渲染机制,selective hydration 标识选择性水合,水合指的是后端内容打到前端后,js需要将事件绑定,才能够响应用户的交互或者DOM的更新行为,而在React18之前,这个操作必须是整体性的,而水合的过程可能比较慢,会引起局部卡顿,所以选择性水合可以按序优先进行水合

所以这个特性其实是转为SSR做准备的,而功能启用的载体就是Supense(所以以后不要再认为Suspense只是一个loading的作用)。其实在Suspense设计之初,就是为了解决服务端渲染问题,只是一开始装载了客户端的按需加载的功能,后面你会逐渐发现React赋予了Suspense根多强大的功能。

SSR for Suspense解决了三个主要问题:

  • SSR模式下,如果不同的模块取数效率不同,会因为最慢的一个模块拖慢整体的HTML的吞吐时间,这可能导致体验还不如非SSR来的好,举一个极端情况,假设报表中一个组件依赖了慢查询,需要五分钟数据才能出来,那么SR的后果就是白屏时间拉长到5分钟。
  • 即使SSR内容打到了页面上,由于js没有执行完毕,所以根本无法进行hydration,整个页面处于无法交互状态。
  • 即使js加载完了,由于React18之前只能进行整体的hydration,可能还是会导致卡顿,导致首次交互响应不及时

最大的区别在于,服务端渲染有简单的res.send改为res.socket。这样的渲染就从单次行为转变为持续性行为。

那么总结一下,新版的SSR性能提高的秘诀:按需

总结

结合起来看,React18关注点在于更快的性能以及用户交互响应效率,其实设计理念处处包含了中断和抢占的概念

以后提到前端性能优化,我们其实也可能从中断和抢占这个视角去考虑问题

  • 随时中断框架的设计,第一优先级渲染策略,用户最关注的UI交互模块
  • 从后端到前端"顺滑"的管道式SSR,并将hydration过程按需化,且支持被更高优先级用户交互打断,第一优先水合用户正在交互的部分

猜你喜欢

转载自juejin.im/post/7082635159559749662