React18新特性?

文章目录

前言

hello world欢迎来到前端的新世界


😜当前文章系列专栏:react.js

🐱‍👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹

💖感谢大家支持!您的观看就是作者创作的动力

Automatic Batching

早在 React 18 之前,React 就已经可以对 state 更新进行批处理了:

javascript 复制代码
function App() {
  const [count, setCount] = useState(0);

  const [flag, setFlag] = useState(false);

  function handleClick() {
    setCount((c) => c + 1); // Does not re-render yet

    setFlag((f) => !f); // Does not re-render yet

    // React will only re-render once at the end (that's batching!)
  }

  return (
    <div>
      <div>{count}</div>
      <button onClick={handleClick}>Next</button>
    </div>
  );
}

上面这个例子中,用户点击按钮时会产生两次 state 的更新,按理来说每次 state 更新都会导致一次 re-render。但是,这两次更新完全可以合成一次,从而减少无谓的 re-render 带来的性能损失。

这种批处理只限于 React 原生事件内部的更新。

在 React 18 中,批处理支持处理的操作范围扩大了:Promise,setTimeout,native event handlers 等这些非 React 原生的事件内部的更新也会得到合并:

javascript 复制代码
// Before: only React events were batched.

setTimeout(() => {
  setCount((c) => c + 1);

  setFlag((f) => !f);

  // React will render twice, once for each state update (no batching)
}, 1000);

// After: updates inside of timeouts, promises,

// native event handlers or any other event are batched.

setTimeout(() => {
  setCount((c) => c + 1);

  setFlag((f) => !f);

  // React will only re-render once at the end (that's batching!)
}, 1000);

Transitions

Transitions 是 React 中一个用于区分高优更新和非高优更新的新概念。

  • 高优的更新/渲染:包括鼠标点击、打字等对实时交互性要求很高的更新场景,卡顿时会影响用户的交互行为,使用户明显感到整个页面卡顿。

  • 非高优的更新/渲染:普通的 UI 更新,不与用户的交互相关,一些对更新实时性要求没那么高的场景。

这里有一个 demo,上方是一个滑动条用于控制下方树的倾角,最顶上的扇区展示了当前的掉帧情况,当用户拉动滚动条时,下方的树的每一个节点都会重新渲染,这会带来明显的卡顿,不仅是下方树的渲染卡顿,上方的滚动条也会无法实时跟着用户的交互移动,这会给用户带来明显的卡顿感。

类似场景下常见的做法应该是 debounce 或 throttle ,React 18 为我们提供了原生的方式来解决这个问题:使用 starTransition 和 useTransition。

  • starTransition:用于标记非紧急的更新,用 starTransition 包裹起来就是告诉 React,这部分代码渲染的优先级不高,可以优先处理其它更重要的渲染。用法如下:
javascript 复制代码
import { startTransition } from "react";

// Urgent
setSliderValue(input);

// Mark any state updates inside as transitions
startTransition(() => {
  // Transition: Show the results, non-urgent
  setGraphValue(input);
});

useTransition:除了能提供 startTransition 以外,还能提供一个变量来跟踪当前渲染的执行状态:

javascript 复制代码
import { useTransition } from "react";

const [isPending, startTransition] = useTransition();

return isPending && <Spinner />;

在勾选了 Use startTransition 后 ,滑动条的更新渲染不会再被树的渲染阻塞了,尽管树叶的渲染仍然需要较多的时间,但是用户使用起来不再有之前那么卡顿了。

Suspense

Suspense 是 React 提供的用于声明 UI 加载状态的 API:

javascript 复制代码
<Suspense fallback={<Loading />}>
  <ComponentThatSuspends />
  <Sibling />
</Suspense>

上面这串代码里,组件 ComponentThatSuspends 在请求处理数据过程中,React 会在它的位置上展示 Loading 组件。

React 16 和 17 中也已经有 Suspense 了,但是它不是完全体,有许多功能仍未就绪。在 React 团队的计划中,Suspense 的完全体是基于 Concurrent React 的,所以在 React 18,Suspense 相较之前有了一些变化。

ComponentThatSuspends 的兄弟组件会被中断

还是上面那个例子:

javascript 复制代码
<Suspense fallback={<Loading />}>
  <ComponentThatSuspends />
  <Sibling />
</Suspense>
  • Legacy Suspense 中,同级兄弟组件会立即挂载(mounted)到 DOM,相关的 effects 和生命周期会被触发,最后会隐藏这个组件。具体可以查看 代码示例。

  • Concurrent Suspense 中,同级兄弟组件并不会从 DOM 上卸载,相关的 effects 和生命周期会在 ComponentThatSuspends 处理完成时触发

Suspense 边界之外的 ref

另一个差异是父级 ref 传入的时间:

javascript 复制代码
<Suspense fallback={<Loading />}>
  <ComponentThatSuspends />
  <Sibling />
</Suspense>
  • 在 Legacy Suspense 中,在渲染之初 refPassedFromParent.current 立即指向 DOM 节点,此时 ComponentThatSuspends 还未处理完成。
  • 在 Concurrent Suspense 中,在 ComponentThatSuspends 完成处理、Suspense 边界解除锁定之前 refPassedFromParent.current 一直为 null。

也就是说,在父级代码中访问此类 ref 都需要关注当前 ref 是否已经指向相应的节点。
Suspense for SSR

React 18 之前的 SSR, 客户端必须一次性的等待 HTML 数据加载到服务器上并且等待所有 JavaScript 加载完毕之后再开始 hydration, 等待所有组件 hydration 后,才能进行交互。即整个过程需要完成从获取数据(服务器)→ 渲染到 HTML(服务器)→ 加载代码(客户端)→ 水合物(客户端)这一套流程。这样的 SSR 并不能使我们的完全可交互变快,只是提高了用户的感知静态页面内容的速度。

React 18 的 Suspense:

服务器不需要等待被 Suspense 包裹组件是否加载到完毕,即可发送 HTML,而代替 Suspense 包裹的组件是 fallback 中的内容,一般是一个占位符(spinner),以最小内联

  • hydration 的过程是逐步的,不需要等待所有的 js 加载完毕再开始 hydration,避免了页面的卡顿。

  • React 会提前监听页面上交互事件(如鼠标的点击),对发生交互的区域优先进行 hydration。

New Hooks

  • useTransition:见上

  • useDeferredValue

    • startTransition 可以用来标记低优先的 state 更新;而useDeferredValue 可以用来标记低优先的变量。

    • 下方代码的具体效果是当 input 的值改变时,返回的 graphValue 并不会立即改变,会首先返回上一次的 input 值,如果当前不存在更紧急的更新,才会变成最新的 input,因此可以通过 graphValue 是否改变来进行一些低优先级的更新。可以在渲染比较耗时的情况下把优先级滞后,在多数情况不会存在不必要的延迟。在较快的机器上,滞后会更少或者根本不存在,在较慢的机器上,会变得更明显。但不论哪种情况,应用都会保持可响应。

javascript 复制代码
import { useDeferredValue } from "react";

const Comp = (input) => {
  const graphValue = useDeferredValue(input); // ...updating depends on graphValue
};

不常用的 hooks

以下的新 hook 主要用于解决 SSR 相关的问题或者是为第三方库的开发设计的,对于普通 React 应用开发者来说几乎用不到:

  • useId 用于解决 SSR 时客户端与服务端难以生成统一的 ID 的问题。

  • useSyncExternalStore 是一个为第三方库编写提供的新 hook,主要用于支持 React 18 在 concurrent rendering 下与第三方 store 的数据同步问题。

  • useInsertionEffect 主要用于提高第三方 CSS in JS 库渲染过程中样式注入的性能。

后言

创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力

相关推荐
秃头女孩y4 小时前
【React中最优雅的异步请求】
javascript·vue.js·react.js
前端小小王10 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发11 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
不是鱼15 小时前
构建React基础及理解与Vue的区别
前端·vue.js·react.js
飞翔的渴望18 小时前
antd3升级antd5总结
前端·react.js·ant design
╰つ゛木槿21 小时前
深入了解 React:从入门到高级应用
前端·react.js·前端框架
用户30587584891251 天前
Connected-react-router核心思路实现
react.js
哑巴语天雨2 天前
React+Vite项目框架
前端·react.js·前端框架
初遇你时动了情2 天前
react 项目打包二级目 使用BrowserRouter 解决页面刷新404 找不到路由
前端·javascript·react.js
码农老起2 天前
掌握 React:组件化开发与性能优化的实战指南
react.js·前端框架