React16、17、18版本新特性

这篇具有很好参考价值的文章主要介绍了React16、17、18版本新特性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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的预期,但是由于编码错误 ,forwardRefmemo 组件的返回值是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 将知道它应该在后台准备新视图,同时仍保持当前视图可见。文章来源地址https://www.toymoban.com/news/detail-446053.html

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>
    </>
  );
};

到了这里,关于React16、17、18版本新特性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • qiankun:react18主应用 + 微应用 react18 + vue3

    一:主应用 搭建react项目 安装Antd 在 index.js中引入 安装react-router : 在 index.js中引入 安装 qiankun : 在主应用中注册微应用,在 index.js中引入 注:子应用嵌入到主应用的地方,id要跟index.js下registerMicroApps里面的container设置一致 修改App.js文件,将如下代码放入App.js 修改App.css样

    2024年02月16日
    浏览(38)
  • react15与react16的本质区别

    React 15 和 React 16 在架构和一些核心特性上存在本质性的区别。 React 15: React 15 使用了递归的协调算法,即采用深度优先遍历整个组件树来协调更新。这种方式在处理大型组件树或深度嵌套组件时可能导致性能问题。 React 16: React 16 引入了 Fiber 架构,使用了一种更灵活的协调算

    2024年01月18日
    浏览(39)
  • React16源码: React中的reconcileChildren的源码实现

    reconcileChildren 1 )概述 在更新了一个节点之后,拿到它的props.children 要根据这个children里面的 ReactElement 来去创建子树的所有的 fiber 对象 要根据 props.children 来生成 fiber 子树,然后判断 fiber 对象它是否是可以复用的 因为我们在第一次渲染的时候,就已经渲染了整个 fiber 子树

    2024年01月20日
    浏览(37)
  • React16源码: React中的renderRoot的源码实现

    renderRoot 1 )概述 renderRoot 是一个非常复杂的方法 这个方法里处理很多各种各样的逻辑, 它主要的工作内容是什么? A. 它调用 workLoop 进行循环单元更新 遍历整个 Fiber Tree,把每一个组件或者 dom 节点对应的 Fiber 节点拿出来单一的进行更新,这是一个循环的操作 把整棵 Fiber T

    2024年01月17日
    浏览(39)
  • React16源码: React中的unwindWork的源码实现

    unwindWork 1 )概述 在 renderRoot 的 throw Exception 里面, 对于被捕获到错误的组件进行了一些处理 并且向上去寻找能够处理这些异常的组件,比如说 class component 里面具有 getDerivedStateFromError 或者 componentDidCatch 这样的生命周期方法 这个class component 就代表它可以处理它的子树当中渲

    2024年01月25日
    浏览(34)
  • React16源码: React中的updateHostRoot的源码实现

    HostRoot 的更新 1 )概述 HostRoot 是一个比较特殊的节点, 因为在一个react应用当中 它只会有一个 HostRoot , 它对应的 Fiber 对象是我们的 RootFiber 对象 重点在于它的更新过程 2 )源码 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js#L612 HostRoot 创建更新的过程就是在 ReactFiberReconcile

    2024年01月22日
    浏览(41)
  • React16源码: React中的updateClassComponent的源码实现

    ClassComponent 的更新 1 ) 概述 在 react 中 class component,是一个非常重要的角色 它承担了 react 中 更新整个应用的API setState forceUpdate 在react当中,只有更新了state之后,整个应用才会重新进行渲染 在 class component 中, 它的逻辑相对复杂 2 )源码 在 packages/react-reconciler/src/ReactFiberB

    2024年01月21日
    浏览(36)
  • React16源码: React中的IndeterminateComponent的源码实现

    IndeterminateComponent 1 )概述 这是一个比较特殊的component的类型, 就是还没有被指定类型的component 在一个fibrer被创建的时候,它的tag可能会是 IndeterminateComponent 在 packages/react-reconciler/src/ReactFiber.js 中,有一个方法 createFiberFromTypeAndProps 中,一开始就声明了 在最终调用 createFibe

    2024年01月21日
    浏览(39)
  • React16源码: React中的beginWork的源码实现

    beginWork 1 )概述 在 renderRoot 之后,要对我们的 Fiber 树每一个节点进行对应的更新 更新节点的一个入口方法,就是 beginWork 这个入口方法会有帮助我们去优化整棵树的更新过程 react 它的节点其实是非常多的,如果每一次子节点的一个更新 就需要每一个节点都执行一遍更新的话

    2024年01月20日
    浏览(43)
  • React16源码: React中的completeUnitOfWork的源码实现

    completeUnitOfWork 1 )概述 各种不同类型组件的一个更新过程对应的是在执行 performUnitOfWork 里面的 beginWork 阶段 它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式 在 performUnitOfWork 里面,还有一个方法叫做 completeUnitOfWork 在

    2024年01月23日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包