cover_image

React18带来了哪些新特性

一点资讯 赛博大象技术团队
2022年01月11日 00:30

一、前言

React18预计在2022年初正式发布的版本,但现在,React18已与最终版无异,现在就可以应用React18创建新项目,或者通过以下方式来对React17及以前版本进行升级:

npm install react@beta react-dom@beta

现在就可以直接安装React18的预发布版而不是测试版了。需要注意的一点是,React18的挂载点和之前版本有所不同:

之前版本:

const container = document.getElementById("root");ReactDOM.render(<App/>,container);
React18版本:
const container = document.getElementById("root");const root = ReactDOM.createRoot(container);root.render(<App/>);

更改完挂载方式后就可以正常使用React18啦!


二、升级策略

React18的更新没有忽略对以前版本的兼容。
开发者可以直接升级到 React 18,对应用程序代码进行最少的更改或不更改就可以将React进行升级,其兼容性和每次发布的最新正式版一样稳定,所以我们可以直接引入React18进行开发就可以了。


三、React18的新特性

3.1 startTransition 将某一状态改变成过渡更新

在日常开发中,我们可能会遇到搜索关键字进行列表查询的功能,而这种功能一般会有两种状态:
1)输入关键字时,需要及时更新state中输入关键字用来回显的紧急更新
2)等待关键字输入完成后,筛选完列表更新state中搜索列表的过渡更新
在 React 18 之前,所有更新都被紧急更新。所以就代表着输入关键字和等待渲染列表会一起执行,并且仍然会阻止用户看到他们交互的反馈,直到所有内容都呈现出来。
我们缺少一种告诉 React 哪些更新是紧急的,哪些不是的方法。
新的API: startTransition 通过让您能够将更新标记为“过渡”来解决此问题:
import {  startTransition }  from  'react' ;
// 紧急:显示输入的内容setInputValue(input);// 将内部的任何状态更新标记为转换startTransition(() => { // Transition: 显示结果 setSearchQuery(input);});
此时我们将状态更新分为两类:

紧急更新(setInputValue)反映直接交互,例如键入、单击、按下等,直接将状态更新到state中。

过渡更新(setSearchQuery)待数据返回后,再将数据更新到state中进行渲染。

诸如:输入框输入值,鼠标点击,按下等操作,我们需要立刻返回给用户一个及时响应的状态,不然就会使用户产生"页面bug"的不良好体验。但是,如果有异步请求的时候,可能会有等待数据返回后延迟渲染的结果。如果是一个数据量很小的异步请求可能延迟会让用户感觉到微乎其微,但如果异步请求返回的数据量大,延迟就会很高。在典型的 React 应用程序中,大多数更新在概念上都是过渡更新。但出于向下兼容性的原因,过渡是可选的。默认情况下,React 18 仍然将更新处理为紧急更新,我们可以通过将更新后,将异步请求返回数据量大的请求包装到startTransition中。


它与 setTimeout 有何不同?

对于过渡更新问题,常见的另一种解决方案是将异步请求返回的结果包装到setTimeout中再进行状态更新:
// 显示你输入的内容setInputValue(input);// 显示结果setTimeout(() => {  setSearchQuery(input);}, 0);
这将延迟异步请求后的更新,这种方法比较常见的例子是对程序进行节流或防抖但是startTransition不同于setTimeout的是:
1)startTransition是同步执行的,也就是说当有数据返回时,startTransition更新数据会比setTimeout要快。
2)startTransition是可以中断的,它不会像setTimeout一阻止用户与页面进行交互。
3)setTimeout只是延迟加载,需要更新页面还要编写异步代码。

过渡期间我该怎么办?
在过渡时期,我们需要通知用户后台正在工作。为此,React提供了一个带有过渡标志的isPending的Hook :
import {  useTransition }  from  'react' ; const [ isPending , startTransition ] = useTransition() ;
isPending值为true的时候,允许加载组件:
{isPending && <Spinner/>}
isPending判定渲染的不用都源自同一个组件,例如在搜索输入中使用一个显示搜索进度的组件,就可以使用isPending来进行渲染判定。

3.2 Suspense和服务器组件 

我们已经可以在React17中使用Suspense,它的作用是实现对代码块的拆分实现按需加载:
const MyComponent = React.lazy( () => import("./MyComponent") );
const App = () => ( <Suspense fallback={ <Loading/> }> // <Loading/> 组件是加载Suspense包裹的懒加载组件中的展示loading的组件 <MyConponent /> </Suspense>)
但在React17中,这种使用Suspense进行代码拆分的使用只发生在客户端,而在最新的React18中,是在服务端实现Suspense。
目前 SSR (服务端渲染) 是一种"全有或全无的方法"。如果有一个页面,其中包含使用SSR生成的标题、导航、内容和评论。那么为该页面提供服务所需的时间将等于其各个组件中最慢的时间。如果其他组件只需要0.1秒而评论组件需要1秒,那么仍需等待评论组加载完成后才能将应用程序发送到客户端。
对于React服务器组件而言,将不再像上述情况一样。所有的组件形成都会在服务器端实现,但可以将组件<Comments />包装在一个<Suspense />中,并且所有组件会在准备好的时候与回调函数一起发送。一旦准备就绪,包含渲染评论的包就将被发送到客户端以替换Suspense回调组件。如果一个挂起的组件在它仍在加载时被点击,React 将立即停止它在其他地方正在做的事情并优先加载该组件!
服务器组件的关键是它们总是在服务器上呈现,而永远不会发送到客户端上呈现。有两个关键的结论:
  • 服务器组件不会影响程序包大小,因为它们总是在服务器上呈现。

  • 服务器组件可以直接访问数据库。

3.3 React最新文档

React发布了最新的文档的测试版 React18文档 预计大概在React18正式版的时候发布最新文档以替换当前的文档。

3.4 不同的开发环境

在我们日常开发时,可能会有因为依赖下载不全或者依赖版本不对的情况下导致项目启动都不成功,从而不能对一些新特性达到快速快发上手的目的,codeandbox glitch repl.it 等在线编码环境可以解决掉这个问题,它们可以让开发者在几秒内开始React中编写真正的程序。

3.5 React Native 跨平台

React团队正在逐步向着对开发者友好的方向迈进,他们在尝试一个应用平台上(Android)的优化如何最同等化的应用于其他应用平台上(IOS)。而且ReactNative团队不仅用在移动平台上开发原生应用,还讨论了Windows(Microsoft Office套件的一部分)和Xbox(新Xbos Series X的仪表盘)等操作系统的一系列研究。

3.6 开发工具

React的开发人员工具也即将到来更新,此次更新的重点:
  • 集成分析器和时间线用来协同工作

  • React Native 的支持

  • CPU和内存分析

3.7 React未来的记忆化功能

记忆化主要用于向 React 指示哪些组件需要或不需要根据某种状态重新渲染。通常对于组件,可以明确声明,如果 props 没有改变,则不需要重新渲染组件。
使用 useMemo 钩子,我们可以创建一个记忆值,如果依赖数组中的值没有改变,则不会重新计算。
useMemo可以和useEffect副作用函数进行比较:
钩子名作用时机返回
useMemo依赖数组更新后函数
useEffectDOM渲染更新后any
import { useEffect,useMemo,useState } from "react"
export default () => { const [params, setParams] = useState(""); const [testParams,setTestParams] = useState(""); const memo_test = useMemo(()=>{ console.log("触发了memo") // 这里写一些操作params的数值 return () => params // 将params作为返回函数的返回值带出 }, [params] )
useEffect( () => { console.log("触发了effect"); }, [params] ) return ( <div> <p>{memo_test()}</p> <Button onClick={()=>setParams("changeParams")}>点击触发Memo更新</Button> <Button onClick={()=>setTestParams("changeTestParams")}>点击触发Effect更新</Button> </div> )}// 无论点击哪个按钮,始终会显示useEffect中的 "触发了effect"// 只有在更改了params的值后,才会输出 "触发了memo" 而且是先触发后渲染// p标签内部的值只有在 点击了 "点击触发Memo更新" 按钮后才显示 changeParams
它还有一个不幸的副作用,就是让你的代码流变得不那么线性(不太可能从上到下在逻辑上遵循)。

3.8 Shopify Hydrogen 服务器组件

Shopify Hydrogen是一个新的服务端框架,Hydrogen 包含一个内置的抽象层,无论 React Server 组件的状态如何,它都能提供稳定性。
使用React服务端组件有以下限制:
  • .client.jsx 在客户端文件的命名方式。

  • .server.jsx在服务端的文件命名方式,此文件中的依赖项不会与客户端依赖项通用。

  • 组件文件不应该以客户端文件名 .client.jsx 和服务端文件名 .server.jsx 来命名。

  • 客户端组件无法访问仅限服务端组件的功能,如查看文件,只能导入其他客户端组件。

  • 服务端组件无法访问客户端的独有功能,如状态:

    图片

上图展示了服务器端组件和客户端组件的不互通性:我们可以创建服务端组件并在服务器端渲染运行,也可以在创建客户端组件并在客户端运行,而且有服务端组件无法访问客户端组件/客户端组件无法访问服务端组件的功能,而服务端/客户端组件不可互相访问内部状态或查看文件功能进行共同开发。
除了特定于服务器和特定的客户端组件之外,还可以创建在服务端和客户端组件上运行的组件,钩子函数和实例,允许环境共享,只要组件,钩子函数和实例满足服务端和客户端组件的所有约束就可以。

图片

当我们需要将服务端组件和客户端组件进行连接使用的时候,可以创建一个中间碎片式文件来连接客户端和服务端,使服务端可以访问客户端内数据路径而服务端可以访问客户端内状态等。当从服务器组件向客户端组件发送 props 参数时, props 一定是要JSON格式或者可被转化为JSON格式的,函数或回调不能作为参数传递。

3.9 批处理

在React18以前的状态更新中,若一个状态在一次调用中更改了很多次;或者在一次调用中,更改了多个状态时,除非本次调用更改状态的函数是异步函数,否则不会批量更新。React18版本前的以下代码实例将触发两个单独的状态更新,18版本中他们将被批处理:

fetchFromApi().then(()=>{  setLoading(false);  setError(false);})


3.10 延迟状态

一个新的API钩子 useDeferredValue,它是将一个已知即将要延迟很长时间后获取的状态做一个标记,在加载组件和状态的时候先跳过对当前标记状态组件的状态渲染,当状态得到结果后,将状态再渲染到组件中。

举个例子,大列表通常和假设情景相符合,当deferredText加载完毕之后,才会将deferredText的值传递给MySlowList组件:

function App () {  const [text,setText] = useState("hello");  const deferredText = useDeferredValue(text,{timeoutMs:2000})    return (  <div className="App">    <input value={text} onChange={handleChange} />    <MySlowList text={deferredText} />  </div> )}


四、总结

1)更加激进的「自动 batching」,React 17 只在事件回调中 batch,React 18 则会对任何来源的 setState 做尽可能多的 batching。对于 Hooks 来说你是没有办法拿到中间状态的 state 而 Class 可以拿 this.state
2)新的startTransition 与 useDeferredValue API,质上都是允许你将 UI 的一部分标记为「较低的更新优先级」。
3)Suspense SSR。完全用 React 重写的新 Facebook 官网是用 Hermes 做 SSR 来优化首屏渲染,所以 SSR 的优化就成为了 React 团队的优先级之一啦……但是传统 SSR 的一个问题是,全量渲染话延迟太高了。而 CM + Suspense 就可以做到将应用分片,然后以此为单位做流式 SSR。
4)StrictMode 在既 double-render 之后加入了 double-effect,进一步帮助你在开发时抓出 CM-unsafe 的代码。
5)React 成立了一个工作组(WG,Working Group)来向整个社区公开有关 18 的讨论与进度。这里的重点是帮助社区的主要框架、类库作者在 18 Alpha 阶段开始迁移,这样当 18 真正发布时社区能够准备充分一些,React 团队一向把开发者体验放在非常高的位置。

文章来自一点资讯MBG团队

继续滑动看下一个
赛博大象技术团队
向上滑动看下一个