React17

官方文档

  • https://legacy.reactjs.org/blog/2020/10/20/react-v17.html
  • https://github.com/facebook/react/releases/tag/v17.0.0
  • https://github.com/facebook/react/releases/tag/v17.0.2

历程介绍

React17正式版,共历经三个正式版更新,进入React18阶段,主要目的是为了过渡,渐进式的去升级

版本名称版本类型发布时间
react17 RC版本RC版本(准正式版)2020年8月11日
react17.0.0正式版2020年10月20日
react17.0.1正式版2020年10月22日
react17.0.2正式版2021年03月22日

官方更新内容

来自于github tag的描述

17.0.0

React

  • 添加 react/jsx-runtimereact/jsx-dev-runtime 支持新的 JSX 转换

  • 从原生错误帧中构建组件堆栈

    • React 17 通过原生错误帧来构建组件堆栈信息。这种改进增强了错误调试的体验,使开发者能够获得更加准确的错误堆栈信息,从而更快定位问题
  • 允许为 Context 指定 displayName 以改进堆栈信息

    • 在 React 17 中,可以为 Context 设置 displayName,使得在调试时的堆栈信息更易读。这对大型应用中的调试特别有帮助,使得组件树和上下文的关联更加直观
  • 防止 'use strict' 泄漏到 UMD 包中

    • React 17 修复了一个问题,防止 'use strict' 作用域意外扩展到 UMD 打包文件中,从而避免影响全局代码。这有助于确保模块在使用不同打包方式时保持一致的行为
  • 停止使用 fb.me 进行重定向

    • eact 17 停止了对 fb.me 重定向的使用。这一改动是为了减少对 Facebook 专有域的依赖,提供更可靠的文档链接体验

React DOM

  • 将事件委托到根节点而不是 document

  • 运行下一个effects之前,清理所有旧的effects

  • useEffect 的清理函数改为异步运行

  • 使用浏览器的 focusinfocusout 事件来处理 onFocusonBlur

    • 但是onFocus 事件总是冒泡的,在 React17 中会继续保持
  • 使所有捕获事件使用浏览器的捕获阶段

  • 不再模拟 onScroll 事件的冒泡

  • 如果 forwardRefmemo 组件返回 undefined,则抛出错误

  • 移除事件池化

    • React 17 移除了事件池化机制
  • 停止暴露不必要的内部 API 给 React Native Web

  • 在根节点挂载时附加所有已知事件监听器

    • 提升了事件监听器的挂载效率,减少了运行时的额外开销
  • 在开发模式的第二次渲染传递中禁用控制台console

  • 弃用未公开且容易误导的 ReactTestUtils.SimulateNative API

    • 因为其行为与语义不符,如果你想要一种简便的方式来触发测试中原生浏览器的事件,可直接使用 React Testing Library
  • 重命名内部使用的私有字段

  • 在开发模式下不再调用User Timing API

  • 在严格模式下双重渲染没有 Hooks 的组件

  • 允许在生命周期方法中调用 ReactDOM.flushSync

  • 为键盘事件对象添加 code 属性

  • 为视频元素添加 disableRemotePlayback 属性

    • 该属性允许禁用远程播放,提供更细粒度的播放控制
  • 为输入元素添加 enterKeyHint 属性

    • 增加对输入法回车行为的提示。这个属性会影响虚拟键盘的 enter 键的样式和行为,主要用于移动端和平板电脑等设备上,让用户清楚地知道 enter 键将执行何种动作。这个属性的典型值有 “enter”, “done”, “go”, “next”, “previous”, “search”, 和 “send” 等。

      例如,如果你在一个搜索框中使用 enterkeyhint="search" 属性,当用户在移动设备上使用这个搜索框时,enter 键将会变为"搜索",用户点击这个键就能提交搜索。

    • 好文推荐:https://cloud.tencent.com/developer/article/2312900

  • <Context.Provider> 无值时给出警告

  • memoforwardRef 组件返回 undefined 时发出警告

  • 改进无效更新的错误信息

  • 在堆栈帧中排除 forwardRefmemo

  • 改进切换受控和非受控输入时的错误信息

  • 保持 onTouchStartonTouchMoveonWheel 事件的被动性

    • 保持这些触摸事件为被动事件,优化了滚动和触摸操作的性能,避免了不必要的阻塞
  • 修复在关闭的 iframe 中开发模式下 setState 挂起的问题

  • 修复懒加载组件在设置 defaultProps 时渲染的退出

    • 解决了懒加载组件在设置 defaultProps 时可能引起的不渲染问题,确保懒加载组件正确处理默认属性
  • 修复当 dangerouslySetInnerHTMLundefined 时的误报警告

  • 修复在非标准 require 实现中的测试工具问题

  • 修复 onBeforeInput 报告错误的 event.type

  • 修复 Firefox 中 event.relatedTarget 被报告为 undefined 的问题

  • 修复 IE11 中的“不明确错误”问题

  • 修复渲染到 Shadow DOM 时的问题

    • 解决了在 Shadow DOM 中渲染时遇到的问题,增强了 React 对 Web Components 的支持。
  • onSubmitonReset 事件使用委托机制

  • 改进内存使用

React DOM Server

  • 使 useCallback 的行为在服务端渲染器中与 useMemo 保持一致
  • 修复函数组件抛出错误时的状态泄漏问题

React17更新重点总结

具体来说,React 17 是一个“垫脚石”版本,它使得将由一个版本的 React 管理的树嵌入到由不同版本的 React 管理的树中更加安全

1.移除事件池

  • 什么叫事件池呢?

  • 事件池:合成事件对象会被放入池中统一管理。这意味着合成事件对象可以被复用,当所有事件处理函数被调用之后,其所有属性都会被回收释放置空。

  • 也就说你在React17 之前如果异步的方式直接去获取事件 e 对象显然是行不通的,他会被清除释放掉,所以在React17 之前你需要在异步方法中获取事件 e 得使用e.persist()方法将当前的合成事件从事件池中删除,并允许保留对事件的引用,才可以在异步方法中引用 e

  • 但是React17 就没有这个事件池东西了,也就不会去清空释放动作,你可以直接在异步中调用

function App() {
  // v17 去除了 React 事件池,异步方式使用e不再需要 e.persist()
  const handleClick = (e: React.MouseEvent) => {
    console.log(e.target) // <button>React事件池</button>
 
    setTimeout(() => {
      // 如果是 v16 返回的是null
      console.log(e.target) // <button>React事件池</button>
    })
  }
  return (
    <div className="App">
      <button onClick={handleClick}>React事件池</button>
    </div>
  )
}

2.事件委托委托到更节点

  • 在 React 16 及更早版本中,React 可以处理document.addEventListener()大多数事件。React 17 将rootNode.addEventListener()在后台进行调用。

  • 在 React 17 中,React 将不再在document底层附加事件处理程序。相反,它会将它们附加到渲染 React 树的根 DOM 容器中:

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

    该图展示了 React 17 如何将事件附加到根元素而不是文档

3.onScroll 事件调整

  • onScroll 事件不再冒泡

4.onFocus 和 onBlur 事件调整

  • React 的 onFocus 和 onBlur 事件已在底层切换为原生的 focusin 和 focusout 事件,但是onFocus 事件总是冒泡的,在 React17 中会继续保持

5.useEffect 的清理函数运行调整

在 React17 之前组件被卸载时,useEffect中清理函数都是同步运行的,而 React17 改为了异步执行

因此有一些点要注意,就是改成异步后一些ref.current的值可能为 null 了,对此要么你在它变为 null 之前先把它用变量存起来,要么用useLayoutEffect,useLayoutEffect可以保证回调函数同步执行,这样就能确保 ref 此时还是最后的值

useLayoutEffect(() => {
  demoRef.current.someSetupMethod()
  return () => {
    demoRef.current.someCleanupMethod()
  }
})

6.改进堆栈信息

  • React16 中错误调用栈的缺点:

    • 缺少源码位置追溯,在控制台无法点击跳转到到出错的地方,无法适用于生产环境
    • 整体来说不如原生的 JavaScript 调用栈,不同于常规压缩后的 JavaScript 调用栈,它们可以通过 sourcemap 的形式自动恢复到原始

    函数的位置,而使用 React 组件栈,在生产环境下必须在调用栈信息和 bundle 大小间进行选择

  • React对堆栈错误信息进行了改进

    • 从原生错误帧中构建组件堆栈

      • React 17 通过原生错误帧来构建组件堆栈信息。这种改进增强了错误调试的体验,使开发者能够获得更加准确的错误堆栈信息,从而更快定位问题
    • 允许为 Context 指定 displayName 以改进堆栈信息

      • 在 React 17 中,可以为 Context 设置 displayName,使得在调试时的堆栈信息更易读。这对大型应用中的调试特别有帮助,使得组件树和上下文的关联更加直观

7.移除部分API

  • 移除部分给 React Native Web用的API
  • 还删除了ReactTestUtils.SimulateNative工具方法,因为其行为与语义不符,如果你想要一种简便的方式来触发测试中原生浏览器的事件,可直接使用 React Testing Library

8.支持新的 JSX 变换

相关官方文档:https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html

当你使用 JSX 时,编译器会将其转换为浏览器可以理解的 React 函数调用。旧版 JSX 转换将 JSX 转换为React.createElement(...)调用。

例如,假设您的源代码如下所示:

import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

在底层,旧的 JSX 转换将其转换为常规 JavaScript:

import React from 'react';

function App() {
  return React.createElement('h1', null, 'Hello world');
}

**您的源代码无需进行任何更改。**我们正在描述 JSX 转换如何将您的 JSX 源代码转换为浏览器可以理解的 JavaScript 代码。

但这并不完美:

  • 因为 JSX 被编译成了React.createElement,所以React如果使用 JSX 的话就需要在范围内。
  • 有一些性能改进和简化React.createElement不允许的。

为了解决这些问题,React 17 在 React 包中引入了两个新的入口点,它们仅供 Babel 和 TypeScript 等编译器使用。新的 JSXReact.createElement转换不会将 JSX 转换为,而是会自动从 React 包中的新入口点导入特殊函数并调用它们。

假设您的源代码如下所示:

function App() {
  return <h1>Hello World</h1>;
}

这是新的 JSX 转换将其编译为的内容:

// Inserted by a compiler (don't import it yourself!)
import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}

请注意,我们的原始代码不再需要导入 React即可使用 JSX!(但我们仍然需要导入 React 才能使用 Hooks 或 React 提供的其他导出。)

9.启发式更新算法(Lane模型)

React17增加更新优先级的功能lane模型

优先级使用lane取代expirationTime

https://juejin.cn/post/7248982532728602681

https://juejin.cn/post/7282962960212639805

https://juejin.cn/post/6860275004597239815

  • React中用lane(车道)模型来表示任务优先级
  • 一共有31条优先级,数字越小优先级越高,某些车道的优先级相同
  • clz32函数返回开头的 0 的个数

1

img

//一共有16种优先级
//同步优先级
const SyncLanePriority = 15;
const SyncBatchedLanePriority = 14;
//离散事件优先级
const InputDiscreteHydrationLanePriority = 13;
const InputDiscreteLanePriority = 12;
//连续事件优先级
const InputContinuousHydrationLanePriority = 11;
const InputContinuousLanePriority = 10;
//默认优先级
const DefaultHydrationLanePriority = 9;
const DefaultLanePriority = 8;
//渐变优先级
const TransitionHydrationPriority = 7;
const TransitionPriority = 6;
const RetryLanePriority = 5;
const SelectiveHydrationLanePriority = 4;
//空闲优先级
const IdleHydrationLanePriority = 3;
const IdleLanePriority = 2;
//离屏优先级
const OffscreenLanePriority = 1;
//未设置优先级
const NoLanePriority = 0;
/**
 * 一共有31条车道
 */
const TotalLanes = 31;
//没有车道,所有位都为0
const NoLanes = 0b0000000000000000000000000000000;
const NoLane = 0b0000000000000000000000000000000;
//同步车道,优先级最高
const SyncLane = 0b0000000000000000000000000000001;
const SyncBatchedLane = 0b0000000000000000000000000000010;
//离散用户交互车道 click
const InputDiscreteHydrationLane = 0b0000000000000000000000000000100;
const InputDiscreteLanes = 0b0000000000000000000000000011000;
//连续交互车道 mousemove
const InputContinuousHydrationLane = 0b0000000000000000000000000100000;
const InputContinuousLanes = 0b0000000000000000000000011000000;
//默认车道
const DefaultHydrationLane = 0b0000000000000000000000100000000;
const DefaultLanes = 0b0000000000000000000111000000000;
//渐变车道
const TransitionHydrationLane = 0b0000000000000000001000000000000;
const TransitionLanes = 0b0000000001111111110000000000000;
//重试车道
const RetryLanes = 0b0000011110000000000000000000000;
const SomeRetryLane = 0b0000010000000000000000000000000;
//选择性水合车道
const SelectiveHydrationLane = 0b0000100000000000000000000000000;
//非空闲车道
const NonIdleLanes = 0b0000111111111111111111111111111;
const IdleHydrationLane = 0b0001000000000000000000000000000;
//空闲车道
const IdleLanes = 0b0110000000000000000000000000000;
//离屏车道
const OffscreenLane = 0b1000000000000000000000000000000;

/**
 * 分离出所有比特位中最右边的1
 * 分离出最高优先级的车道
 * @param {*} lanes 车道
 * @returns 车道
 */
function getHighestPriorityLane(lanes) {
    return lanes & -lanes;
}

//console.log(getHighestPriorityLane(InputDiscreteLanes));//8 0b1000
//console.log('0000000000000000000000000011000');
//console.log('1111111111111111111111111101000');

/**
 * 分离出所有比特位中最左边的1
 * 分离出最低优先级的车道
 * @param {*} lanes 车道
 * @returns 车道
 */
function getLowestPriorityLane(lanes) {
    const index = 31 - Math.clz32(lanes);
    return index < 0 ? NoLanes : 1 << index;
}
console.log(getLowestPriorityLane(InputDiscreteLanes));//16 0b10000
console.log('0000000000000000000000000011000');
console.log(Math.clz32(InputDiscreteLanes));//27
console.log(31 - Math.clz32(InputDiscreteLanes));//4

10.规划了concurrent mode 并发模式,并在React18正式版使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值