React18带来了几个非常好用的新特性,同时也没有额外的升级成本,值得细细品味。以下是一些关键信息:React18工作组。使用社区讨论React18发布节奏和新功能。发布计划。尚未正式发布,但@alpha版本已经可用,请安装alpha版本。React18新特性介绍,虽然还没有正式发布,但是可以先做特性介绍。本周的精读主要是解读这份文件。精读总的来说,React18带来了3大新特性:自动批处理。并发API。悬念的SSR。同时,为了启用新功能,需要进行简单的渲染功能升级。自动批处理批处理是指React可以将回调函数中的多个setState事件合并到一个渲染中。也就是说,setState不会实时修改State,而是组合多个setState调用只触发一次渲染,这样既可以减少程序数据状态中中间值带来的不稳定,又可以提高渲染性能。可以理解为如下代码:functionhandleClick(){setCount((c)=>c+1);设置标志((f)=>!f);//只触发一次渲染}但不幸的是,React18以前,如果在回调函数的异步调用中执行setState,由于上下文丢失,合并过程无法完成,所以每次setState调用都会立即触发重新渲染:functionhandleClick(){//Fetch(/*...*/).then(()=>{setCount((c)=>c+1);//立即重新渲染setFlag((f)=>!f);//立即重新渲染});}而React18带来的优化是任何情况都可以合并渲染!即使在promise、timeout或事件回调中多次调用setState,它们也会合并为一个渲染:functionhandleClick(){//React18+fetch(/*...*/).then(()=>{setCount((c)=>c+1);setFlag((f)=>!f);//只触发一次渲染});}当然,如果你坚持在调用setState后立即重新渲染,只需使用flushSyncWrapper:functionhandleClick(){//React18+fetch(/*...*/).then(()=>{ReactDOM.flushSync(()=>{setCount((c)=>c+1);//立即重新渲染setFlag((f)=>!f);//立即重新渲染});});}启用此功能的前提是将ReactDOM.render替换为ReactDOM。创建根调用。新的ReactDOMRenderAPI升级方法非常简单:constcontainer=document.getElementById("app");//oldrenderAPIReactDOM.render(,container);//newcreateRootAPIconstroot=ReactDOM.createRoot(container);root.render();API修改的主要原因是语义,即我们多次调用render时,不再需要重复传入container参数,因为在新的API中,container已经提前绑定到root上了。ReactDOM.hydrate也被ReactDOM.hydrateRoot取代:constroot=ReactDOM.hydrateRoot(container,);//注意这里不调用root.render()的好处是如果你后面调用root.render()重新渲染,我们不需要关心root是来自createRoot还是hydrateRoot,因为后续的API行为都是一样的,减少了理解成本。ConcurrentAPIS首先要了解什么是ConcurrentMode。简单的说ConcurrentMode是一种可以中断渲染的设计架构。什么时候中断渲染?当更高优先级的渲染到达时,通过放弃当前渲染立即执行更高优先级的渲染,以换取视觉上更快的响应速度。可能有人会说,不行,渲染中断后,浪费了之前渲染的CPU执行。换句话说,整体执行力往往会增加。这句话没错,但实际上,用户对页面交互时效性的感知分为两种。第一个是即时的输入反馈,第二个是这个输入带来的副作用反馈,比如更新列表。其中,即使能先满足输入反馈,即使副作用反馈慢一些,也会带来更好的体验,更何况大部分副作用反馈都会因为输入反馈的变化而失效。由于React将渲染DOM树机制改为两个双向链表,并且渲染树只有一个指针,指向其中一个链表,因此可以在更新完成后和指针之前切换指针已切换,您可以随时放弃另一个。树修改。以上是后台输入。React18提供了三个新的API来支持这种模式,分别是:startTransition。使用延迟值。<悬念列表>。后两个文档还没有发布,所以本文只介绍第一个API:startTransition。先看用法:import{startTransition}from"react";//紧急更新:setInputValue(input);//将回调函数中的更新标记为非紧急更新:startTransition(()=>{setSearchQuery(输入);});简单来说就是将startTransition回调包裹的setState触发的渲染标记为非紧急渲染,这些渲染可能会被其他紧急渲染抢占。比如本例中,当setSearchQuery更新列表内容较多,导致渲染时CPU占用100%,此时用户再次输入,触发了setInputValue引起的渲染。此时setSearchQuery引起的渲染会立即停止。相反,支持setInputValue呈现,以便用户输入可以快速反映在UI上,代价是搜索列表响应稍慢。可以通过isPending访问转换的中断状态:import{useTransition}from"react";const[isPending,startTransition]=useTransition();其实这更符合操作系统的设计理念。我们知道操作系统是通过中断响应底层硬件事件的中断非常紧急(因为硬件可以存储的消息队列非常有限,即使硬件输入可能丢失操作系统也无法响应),所以它是支持抢占式内核所必需的,当中断到达时立即执行中断(可能将不太紧急的操作移至后半部分)。对于前端交互来说,用户视角的“中断”一般来自键盘或鼠标操作,但遗憾的是,前端框架甚至JS都太高级了,无法自动识别哪些代码是紧急生成的中断。比如onClick一定是用户鼠标点击产生的?不一定,可能是xxx.onClick而不是用户触发的。用户触发是否一定是紧急中断?不一定,比如键盘输入后setInputValue是紧急的,但是更新查询列表的setSearchQuery是非紧急的。我们需要了解前端场景对用户操作感知的局限性,从而理解为什么必须手动指定更新的紧急程度,不像操作系统,上层程序不需要感知存在中断。Suspense的SSR全称是:StreamingSSRwithselectivehydration。即像水流一样,创建一条从服务端到客户端的连续渲染管道,而不是像renderToString那样的一次性渲染机制。选择性水合是指选择性水合。Hydration是指后端内容命中前端后,JS需要为其绑定事件,以响应用户交互或DOM更新行为。在React18之前,这个操作必须是整体的,水化过程可能比较慢,会造成全局停滞,所以选择性水化可以根据需要优先水化。所以这个功能其实是为SSR准备的,功能赋能载体就是Suspense(所以以后不要以为Suspense只是一个加载功能)。其实Suspense在设计之初就是为了解决服务端渲染问题,只是一开始只安装了客户端测试的按需加载功能。后面你会逐渐发现,React组逐渐赋予了Suspense更强大的能力。SSRforSuspense解决了三个主要问题:在SSR模式下,如果不同的模块具有不同的检索效率,最慢的模块将减慢整体HTML吞吐时间,这可能导致比非SSR更差的体验。举个极端的例子,假设报表中某个组件依赖慢查询,需要5分钟才能拿到数据,那么SSR的后果就是白屏时间延长到5分钟。即使SSR内容打到页面,由于JS还没有加载,根本无法进行水化,整个页面处于交互状态。即使加载JS,由于React18之前只能进行整体水化,可能会造成卡顿,导致第一次交互响应延迟。在React18的服务端渲染中,只需要使用pipeToNodeWritable代替renderToString,配合Suspense就可以解决以上三个问题。请参阅使用pipeToNodeWriteable的示例。最大的区别是服务端渲染从简单的res.send变成了res.socket,从而让渲染从单一的行为变成了连续的行为。那么React18的SSR有什么作用呢?推荐看一下这个介绍文档中的图片。这是非常直观的。这里简单描述一下:包裹的block在服务端渲染时不会阻塞第一次吞吐量,block准备好后(包括异步抓取)再实时命中页面(HTML模式下),此时没有水化),然后再返回fallback的内容。水化的过程也是一步步的,这样才不会导致一次执行完所有的完整js而导致页面卡死(水化其实就是回调注册和React写的各种Hooks,量整个应用程序非常大)。因为水合是分成多个部分的,React也会提前监听鼠标点击,提前对点击区域的优先级进行水合,甚至抢占其他区域已经在进行的水合。所以总结一下,新版SSR性能提升的秘诀就在于两个字:随需应变。难点在于SSR需要后端到前端的配合。在React18之前,从后端到前端的流程根本没有优化。现在SSRHTML的吞吐量改成倍数,按需,水化过程中也支持。抢占,因此性能进一步提高。总结综合来看,React18更侧重于更快的性能和用户交互响应效率,其设计理念处处包含中断和抢占的概念。未来在前端性能优化上,我们会有更多应用端的角度(不仅仅是工程角度),从以下两个应用优化角度有效提升交互反馈速度:任何时候,优先渲染用户最关注的UI交互模块。从后端到前端的“平滑”流水线SSR使得水合过程按需进行,并支持更高优先级的用户交互行为中断。首要任务是滋润用户正在与之交互的部分。讨论地址为:Jingdu《React 18》·Issue#336·dt-fe/weekly想参与讨论的请点这里,每周都有新话题,周末或周一发布。前端精读——帮你过滤靠谱的内容。关注前端精读微信公众号版权声明:免费转载-非商业-非衍生保留属性(CreativeCommons3.0License)