首页IT科技react17.0(React16、17、18版本新特性)

react17.0(React16、17、18版本新特性)

时间2025-09-19 10:37:55分类IT科技浏览9289
导读:react-16版本新特性...

react-16版本新特性

一                   、hooks

import { useState } from react function App() { // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0 // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState) const [count, setCount] = useState(0) return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App

二                          、memo          、lazy               、Suspense

import React, { Suspense } from react; const OtherComponent = React.lazy(() => import(./OtherComponent)); function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense> </div> ); }

三                          、Profiler

Profiler 能添加在 React 树中的任何地方来测量树中这部分渲染所带来的开销

function onRenderCallback( id, // 发生提交的 Profiler 树的 “id                   ” phase, // "mount" (如果组件树刚加载) 或者 "update" (如果它重渲染了)之一 actualDuration, // 本次更新 committed 花费的渲染时间 baseDuration, // 估计不使用 memoization 的情况下渲染整棵子树需要的时间 startTime, // 本次更新中 React 开始渲染的时间 commitTime, // 本次更新中 React committed 的时间 interactions // 属于本次更新的 interactions 的集合 ) { // 合计或记录渲染时间                   。                          。 console.log( id, // 发生提交的 Profiler 树的 “id                          ” phase, // "mount" (如果组件树刚加载) 或者 "update" (如果它重渲染了)之一 actualDuration, // 本次更新 committed 花费的渲染时间 baseDuration, // 估计不使用 memoization 的情况下渲染整棵子树需要的时间 startTime, // 本次更新中 React 开始渲染的时间 commitTime, // 本次更新中 React committed 的时间 interactions // 属于本次更新的 interactions 的集合 ); } //Navigation update 0 0 57313.90000009537 57314.5 Set(0) {size: 0} <Profiler id="Navigation" onRender={onRenderCallback}> <div onClick={() => { setNumber((e) => e + 1); }} > test </div> </Profiler>

四               、createContext          、createRef                          、forwardRef                   、生命周期函数的更新     、Strict Mode

// 父组件中 const ThemeContext = React.createContext("light"); const ParentComponent = () => { const inputRef = createRef(); return ( <ThemeContext.Provider value="dark"> <ChildComponent ref={inputRef} /> <button onClick={() => { inputRef.current.focus(); }} > 获取焦点 </button> </ThemeContext.Provider> ); } //子组件 const ChildComponent = forwardRef((props, inputRef) => { const value = useContext(ThemeContext); return ( <> <input id="input" type="text" ref={inputRef} /> <button style={{ background: "yellow" }} onClick={() => { console.log(value); }} > 测试 </button> </> ); });

五                           、Fragment

<React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment>

六                       、createPortal

ReactDOM.createPortal(child, container)

react-17版本新特性

一、全新的 JSX 转换

React 17以前                   ,React中如果使用JSX                          ,则必须像下面这样导入React          ,否则会报错               ,这是因为旧的 JSX 转换会把 JSX 转换为 React.createElement(…) 调用          。

import React from react; export default function App(props) { return <div>app </div>; }

二                       、事件委托的变更

在 React 16 或更早版本中                          ,React 会由于事件委托对大多数事件执行 document.addEventListener()               。但是一旦你想要局部使用React               ,那么React中的事件会影响全局          ,当把React和jQuery一起使用                          ,那么当点击input的时候                   ,document上和React不相关的事件也会被触发     ,这符合React的预期                           ,但是并不符合用户的预期                          。

React React17不再将事件添加在document上                       ,而是添加到渲染 React 树的根 DOM 容器中:

const rootNode = document.getElementById(root); ReactDOM.render(<App />, rootNode);

三                           、事件系统相关更改

除了事件委托这种比较大的更改,事件系统上还发生了一些小的更改                       ,与以往不同                           ,React 17中onScroll 事件不再冒泡     ,以防止出现常见的混淆                。

React 的 onFocus 和 onBlur 事件已在底层切换为原生的 focusin 和 focusout 事件          。它们更接近 React 现有行为                   ,有时还会提供额外的信息                          。

捕获事件(例如                          ,onClickCapture)现在使用的是实际浏览器中的捕获监听器                   。这些更改会使 React 与浏览器行为更接近          ,并提高了互操作性     。

注意:尽管 React 17 底层已将 onFocus 事件从 focus 切换为 focusin               ,但请注意                          ,这并未影响冒泡行为                           。在 React 中               ,onFocus 事件总是冒泡的          ,在 React 17 中会继续保持                          ,因为通常它是一个更有用的默认值                       。请参阅 sandbox                    ,以了解为不同特定用例添加不同检查。

四     、去除事件池

在React 17 以前     ,如果想要用异步的方式使用事件e                           ,则必须先调用调用 e.persist() 才可以                       ,这是因为 React 在旧浏览器中重用了不同事件的事件对象,以提高性能                       ,并将所有事件字段在它们之前设置为 null                       。如下面的例子:

function FunctionComponent(props) { const [val, setVal] = useState(""); const handleChange = e => { // setVal(e.target.value); // React 17以前                           ,如果想用异步的方式使用事件e     ,必须要加上下面的e.persist()才可以 // e.persist(); // setVal(data => e.target.value); }; return ( <div className="border"> <input type="text" value={val} onChange={handleChange} /> </div> ); }

但是这种使用方式有点抽象                   ,经常会让对React不太熟悉的开发者懵掉                          ,但是值得开心的是          ,React 17 中移除了 “event pooling(事件池)“               ,因为以前加入事件池的概念是为了提升旧浏览器的性能                          ,对于现代浏览器来说               ,已经不需要了                           。因此          ,上面的代码中不使用e.persist();也能达到预期效果     。

五                   、副作用清理时间

React 17以前                          ,当组件被卸载时                   ,useEffect和useLayoutEffect的清理函数都是同步运行     ,但是对于大型应用程序来说                           ,这不是理想选择                       ,因为同步会减缓屏幕的过渡(例如,切换标签)                       ,因此React 17中的useEffect的清理函数异步执行                           ,也就是说如果要卸载组件     ,则清理会在屏幕更新后运行                   。如果你某些情况下你仍然希望依靠同步执行                   ,可以用 useLayoutEffect                          。

当然React 17中的useEffect的清理函数异步执行之后                          ,有一个隐患:

useEffect(() => { someRef.current.someSetupMethod(); return () => { someRef.current.someCleanupMethod(); }; });

问题在于 someRef.current 是可变的          ,因此在运行清除函数时               ,它可能已经设置为 null          。解决方案是在副作用内部存储会发生变化的值:

useEffect(() => { const instance = someRef.current; instance.someSetupMethod(); return () => { instance.someCleanupMethod(); }; });

我们不希望此问题对大家造成影响                          ,我们提供了 eslint-plugin-react-hooks/exhaustive-deps 的 lint 规则(请确保在项目中使用它)会对此情况发出警告               。

六                          、返回一致的 undefined 错误

在 React 16 及更早版本中               ,返回 undefined 始终是一个错误          ,当然这是React的预期                          ,但是由于编码错误                    ,forwardRef 和 memo 组件的返回值是undefined的时候没有做为错误     ,React 17中修复了这个问题                          。React中要求对于不想进行任何渲染的时候返回 null

七          、原生组件栈

在 React 17 中                           ,使用了不同的机制生成组件调用栈                       ,该机制会将它们与常规的原生 JavaScript 调用栈缝合在一起               。这使得你可以在生产环境中获得完全符号化的 React 组件调用栈信息          。

八               、移除私有导出

React 17删除了一些以前暴露给其他项目的 React 内部组件                          。特别是,React Native for Web 过去常常依赖于事件系统的某些内部组件                       ,但这种依赖关系很脆弱且经常被破坏                   。

在 React 17 中                           ,这些私有导出已被移除     。据我们所知     ,React Native for Web 是唯一使用它们的项目                   ,它们已经完成了向不依赖那些私有导出函数的其他方法迁移                           。

九                          、启发式更新算法更新

React 16开始替换掉了Stack Reconciler                          ,开始使用启发式算法架构的的Fiber Reconciler                       。那么为什么要发生这个改变呢?

React的killer feature: virtual dom

React15.x - Stack Reconciler React16 - Fiber Reconciler React17 - Fiber Reconciler (进阶版 - 优先级区间)

1               、为什么需要fiber:对于大型项目          ,组件树会很大               ,这个时候递归遍历的成本就会很高                          ,会造成主线程被持续占用               ,结果就是主线程上的布局          、动画等周期性任务就无法立即得到处理          ,造成视觉上的卡顿                          ,影响用户体验。

2                          、任务分解的意义:解决上面的问题

3                   、增量渲染(把渲染任务拆分成块                   ,匀到多帧)

4     、更新时能够暂停     ,终止                           ,复用渲染任务

5                           、给不同类型的更新赋予优先级

6                       、并发方面新的基础能力

7、更流畅

React 17中更新了启发式更新算法                       ,具体表现为曾经用于标记fiber节点更新优先级的expirationTime换成了为lanes,前者为普通数字                       ,而后者则为32位的二进制                           ,了解二进制运算的都比较熟悉了     ,这种二进制的lanes是可以指定几个优先级的                   ,而不是像以前expirationTime只能标记一个                       。

之所以做这种改变                          ,原因就是在于expirationTimes模型不能满足IO操作(Suspense)          ,Suspense用法如下:

<React.Suspense fallback={<Loading />}> <Content /> </React.Suspense>

react-18版本新特性

一                       、客户端渲染 API

带有 createRoot() 的 root API               ,替换现有的 render() 函数                          ,提供更好的人体工程学并启用新的并发渲染特性                           。

import { createRoot } from "react-dom/client"; import { useState } from react const App = () => { const [A, setA] = useState(1); const [B, setB] = useState(1); const handleClick = () => { setA((a) => a + 1); setB((b) => b - 1); }; setTimeout(() => { setA((a) => a + 1); setB((b) => b - 1); }, 1000); return (<div> <p>{A}</p> <p>{B}</p> </div>); }; const container = document.getElementById("app"); const root = createRoot(container); root.render(<App />); root.unmount(); //卸载组件

如果你的项目使用了ssr服务端渲染               ,需要把ReactDOM.hydrate升级为ReactDOM.hydrateRoot

import { hydrateRoot } from "react-dom/client"; const container = document.getElementById("app"); const root = hydrateRoot(container, <App tab="home" />);

在某些场景下 我们可能不需要批处理状态更新, 此时我们需要用到 react-dom 提供的flushSync函数, 该函数需传入一个回调, 并且会同步刷新回调中的状态更新

import { useState } from react import { flushSync } from react-dom function App() { const [num1, setNum1] = useState(1) const [num2, setNum2] = useState(1) const add = () => { setTimeout(() => { flushSync(() => { setNum1((pre) => pre + 1) }) flushSync(() => { setNum2((pre) => pre + 1) }) }) } console.log(渲染了) console.log(num1, num2) return ( <div className="App"> <header className="App-header">react 18</header> <p>num1 : {num1}</p> <p>num2 : {num2}</p> <button onClick={add}>+1</button> </div> ) } export default App

二                           、严格模式更新

React 18 带来了大把新特性          ,此外还有很多新特性正在路上     。为了让你的代码为此做好准备                          ,StrictMode 变得更加严格了                   。最重要的是                   ,StrictMode 将测试组件对可重用状态的弹性     ,模拟一系列的挂载和卸载行为                          。它旨在让你的代码为即将推出的特性(可能以组件的形式)做好准备                           ,这将在组件的挂载周期中保留这个状态          。

虽然它肯定会在未来提供更好的性能                       ,但就目前而言,启用 StrictMode 时必须要考虑这个事情               。

除了以上提到的更改之外                       ,根据你的 React 代码库                           ,你可能还会发现其他一些更改                          。

值得一提的是     ,React 18 将不再支持 IE 浏览器                   ,因为 React 18 现在依赖很多现代浏览器特性                          ,如 Promise 或 Object.assign               。

其余的更改与一些 React 行为的稳定性和一致性有关          ,不太可能影响你的代码库          。

三     、Transition

Transition 是由并发渲染提供支持的新特性之一                          。它旨在与现有状态管理 API 一起使用               ,以区分紧急和非紧急状态更新                   。通过这种方式                          ,React 知道哪些更新需要优先考虑               ,哪些更新需要在后台通过并发渲染准备     。

import { useTransition, useState } from "react"; const App = () => { const [isPending, startTransition] = useTransition(); const [value, setValue] = useState(0); function handleClick() { startTransition(() => { setValue((value) => value + 1); }); } return ( <div> {isPending && <Loader />} <button onClick={handleClick}>{value}</button> </div> ); };

在 startTransition() 回调中提交的任何状态更新都将被标记为 transition          ,从而使其他更新具有优先权                           。如果你不能使用这个钩子                          ,还有一个单独的 startTransition() 函数可用——虽然它不会通知你转换的进度                       。

import { startTransition } from "react"; // ... startTransition(() => { // Transition updates }); // ...

四                   、Suspense更新

在状态改变时                   ,lazy() 加载的组件将触发 Suspense     ,导致 fallback 元素的渲染。如果你将状态更改标记为一个 transition                           ,React 将知道它应该在后台准备新视图                       ,同时仍保持当前视图可见                       。

import { Suspense, useTransition } from "react"; const App = () => { const [value, setValue] = useState("a"); const [isPending, startTransition] = useTransition(); const handleClick = () => { startTransition(() => { setValue("b"); }); }; return ( <> <Suspense fallback={<Loader />}> <div style={{ opacity: isPending ? 0.7 : 1 }}> {value === "a" ? <A /> : <B />} </div> </Suspense> <Button onClick={handleClick}>B</Button> </> ); };

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
redisson分布式锁问题(Redisson源码解读-分布式锁)