赞
踩
React官方在2022年3月29日React18版本正式发布了。
可以在官网看到,react 17 的发布时间是 2020 年 10 月 20 号,距离 React 18 发布足足间隔一年半,并且v17中只有三个小版本,分别是17.0.0、17.0.1、17.0.2:
并且一直到React18发布,React17都没有任何更新,可以说React17只是作为React18的垫脚石。
2. 提高项目的兼容性
在项目使用React17的时候,如果依赖的组件使用的React18版本可能导致项目因为版本不匹配而发生错误甚至奔溃(亲测有效)。而项目如果使用了React18,依赖的其他组件使用React18或者React17都可以很好的兼容。
npm install react react-dom --save yarn install react react-dom --save
再执行npm install即可。
项目中的需要修改的地方:
// React 17 import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; const root = document.getElementById('root')!; //React 18 // React 18 import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; const root = document.getElementById('root')!; ReactDOM.createRoot(root).render(<App />);
// React 17 ReactDOM.unmountComponentAtNode(root); // React 18 root.unmount();
如果项目中有在render中用到了回调函数,迁移到对应组件的useEffect中即可。
// React 17 const root = document.getElementById('root')!; ReactDOM.render(<App />, root, () => { console.log('渲染完成'); }); // React 18 const AppWithCallback: React.FC = () => { useEffect(() => { console.log('渲染完成'); }, []); return <App />; }; const root = document.getElementById('root')!; ReactDOM.createRoot(root).render(<AppWithCallback />);
在项目中定义props类型的时候,如果需要获取字组件children需要显示的定义它。
什么是批处理?
批处理就是多个状态更新合并成一个次更新。(视图层将多次渲染合并成一次渲染)
在React18以前
我们只在React18中进行批处理。默认情况下,在promise、setTimeout、原生事件处理函数中、或任何其它事件内的更新都不会进行批处理。
在React18以后
所有更新都会自动进行批处理。多次更新将会合并成一次更新,从而降低渲染次数提高性能。
改动点:
如果项目中有通过promise、setTimeout、原生事件处理函数去解决批处理的问题在React18中将不会生效了。
如果再想退出批处理,需要使用flushSync
import React, { useState } from 'react'; import { flushSync } from 'react-dom'; const App: React.FC = () => { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); return ( <div onClick={() => { flushSync(() => { setCount1(count => count + 1); }); // 第一次更新 flushSync(() => { setCount2(count => count + 1); }); // 第二次更新 }} > <div>count1: {count1}</div> <div>count2: {count2}</div> </div> ); }; export default App;
在react17中,如果你需要返回一个空组件,ract只允许你返回null。如果你显示的返回了undifined控制台则会在运行时抛出一个错误。
在react18中,不在见出啊因返回undifined而导致奔溃。即能返回null,也能返回undifned。
并发模式可帮助应用保持响应,并根据用户的设备性能和网速进行适当的调整,该模式通过使渲染可中断来修复阻塞渲染限制。在 Concurrent 模式中,React 可以同时更新多个状态。
react17 和 react18的区别就是:从同步不可中断更新变成了异步可中断更新。
开启并发模式:
在React18中提供了新的root Api,我们只需要把render改成ReactDOM.createRoot(root).render(<App />) 就可以开启并发模式。
开启并发模式就一定开启并发更新吗?
No!在React18中开启并发模式不一定开启并发更新,而是否开启并发更新的依据是是否使用并发特性。
并发特性指的是开启并发模式才能使用的特性,比如下面介绍的:
结论:
并发更新的意义就是交替执行不同的任务,当预留的时间不够用时,React将线程控制交给浏览器,等待下一帧时间的到来,然后继续被中断的工作。
Transition:
在大屏幕视图更新的时,startTransition 能够保持页面有响应,这个 api 能够把 React 更新标记成一个特殊的更新类型 transitions ,在这种特殊的更新下,React 能够保持视觉反馈和浏览器的正常响应。
startTransition(scope)
使用:
startTransition(()=>{ /* 更新任务 */ func() })
startTranstion的回调包裹的setState触发的渲染标记为不紧急渲染。这些渲染可能被其他紧急渲染所抢占。
在低优先级还没执行的时候,怎么知道过渡任务处于什么状态,这时候就可以使用useTranstion这个Hooks。useTranstion执行返回一个数组,数组有两个状态值。
第一个状态值: 当处于过渡状态的标记。
第二个状态值: 可以理解为startTranstion,将任务标记为过渡任务。
import { useTranstion } from 'react'; const [isPending, startTrastion] = useTranstion();
返回一个延时响应的值可以让一个state延时生效,只有当前没有紧急更新的任务时,该值才会变为最新的值。和startttanstion一样都是标记为非紧急更新。
相同点: useDeferredValue本质上和内部实现与useTranstion一样都是标记成了过度更新任务。
不同点:useTranstion是把startTranstion内部的更新任务变成了过度任务transtion,而useDeferredValue是把原值通过过度任务得到新的值,这个值作为延时状态,一个是处理逻辑,一个是生产一个新的状态。
输入框输入一个内容,更新10000条列表内容。在chrome仓库看执行栈。
使用并发特性:
我们可以看到使用并发特性
此时我们的任务被拆分到每一帧不同的task中,JS脚本执行的时间大体在5ms左右,这样浏览器就有剩余的时间执行页面的样式布局和页面重绘。
使用普通更新:
我们可以看到只有一次页面的重绘,其他几次输入都因为输入触发js的执行而阻塞了更新。
const id = useId();
支持同一个组件在客户端和服务端生成相同的唯一的 ID,避免 hydration 的不兼容,这解决了在 React 17 及 17 以下版本中已经存在的问题。因为我们的服务器渲染时提供的 HTML 是无序的,useId 的原理就是每个 id 代表该组件在组件树中的层级结构。
useSyncExternalStore是一个新的api,经历了一次修改,由useMutableSource改变而来,主要用来解决外部数据撕裂的问题。
在dom生成之后,useLayoutEffect之前,它的工作原理大致合useLayoutEffect相同,只是此时无法访问DOM节点的引用,一般用于提前注入<style>脚本。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。