React中useEffect 与 useLayoutEffect的区别

 更新时间:2021年07月25日 16:26:25   作者:Alander  
本文主要介绍了React中useEffect与useLayoutEffect的区别,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前置知识

我们可以将 React 的工作流程划分为几大块:

  1. render 阶段:主要生成 Fiber节点 并构建出完整的 Fiber树
  2. commit 阶段:在上一个render 阶段中会在 rootFiber 上生成一条副作用链表,应用的DOM操作就会在本阶段执行

commit阶段的工作主要分为三部分,对应到源码中的函数名是:

  • commitBeforeMutationEffects阶段:主要处理执行DOM操作前的一些相关操作
  • commitMutationEffects阶段:执行DOM操作
  • commitLayoutEffects阶段:主要处理执行DOM操作后的一些相关操作

useEffect 和 useLayoutEffect 的区别主要就在体现在这三个阶段的处理上。结论是:useEffect 会异步地去执行它的响应函数和上一次的销毁函数,而useLayoutEffect 会同步地执行它的响应函数和上一次的销毁函数,即会阻塞住 DOM渲染。

useEffect

commitBeforeMutationEffects

在这个阶段中 useEffect 着重会经历一句话如下:

function commitBeforeMutationEffects() {
  while (nextEffect$1 !== null) {
    // 一系列的赋值操作省略,这里的flags应取自对应FunctionComponent的effect的flags,具体实现请看源码
    var flags = effect.flags;

  // 处理生命周期
    if ((flags & Snapshot) !== NoFlags) {
      setCurrentFiber(nextEffect$1);
      commitBeforeMutationLifeCycles(current, nextEffect$1);
      resetCurrentFiber();
    }

 // 这个if判断只有 useEffect 为 true,useLayoutEffect 为false
    if ((flags & Passive) !== NoFlags) {
      // If there are passive effects, schedule a callback to flush at
      // the earliest opportunity.
      if (!rootDoesHavePassiveEffects) {
        rootDoesHavePassiveEffects = true;
 // 这里就是 useEffect 异步的原因,DOM操作后React会调度 flushPassiveEffects
        scheduleCallback(NormalPriority, function () {
          flushPassiveEffects();
          return null;
        });
      }
    }

    nextEffect$1 = nextEffect$1.nextEffect;
  }
}

commitMutationEffects

在这个阶段中,React 会进行一系列的DOM节点更新 ,然后会执行一个方法: commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);

那么一个拥有 useEffect 的 Functional Component 在这个阶段是不符合 unmount 的判断逻辑的,所以在这个地方不会做 unmount 操作。

commitLayoutEffects

在这个阶段中,依然有一个很重要的方法存在:commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);

这个if判断和上一阶段的if判断是一样的,useEffec 在这个判断中不会做任何操作。

后续阶段

在完成了 commitLayoutEffects 后,还有一个操作:

if (rootDoesHavePassiveEffects) {
    // This commit has passive effects. Stash a reference to them. But don't
    // schedule a callback until after flushing layout work.
    rootDoesHavePassiveEffects = false;
    rootWithPendingPassiveEffects = root;
    pendingPassiveEffectsLanes = lanes;
    pendingPassiveEffectsRenderPriority = renderPriorityLevel;
  }

即把 rootWithPendingPassiveEffects 置为 root ,这么做的原因和第一阶段 commitBeforeMutationEffects 中 useEffect 注册的下一次 flushPassiveEffects 异步调度有关,我们看以下 flushPassiveEffects 的实现:

function flushPassiveEffectsImpl() {
 if (rootWithPendingPassiveEffects === null) {
    return false;
  }
 // 省略一系列的性能追踪等操作
 commitPassiveUnmountEffects(root.current);
  commitPassiveMountEffects(root, root.current);
}


从上述代码段可以看见,useEffect 在第一阶段注册的调度回调会在页面更新后进行 unmount 和 mount 操作。值得一提的是,这个回调中effect的注册时机就是在 commitLayoutEffects 阶段。

useLayoutEffect

其实根据我们对 useEffect 的解析来看,就是在 commitMutationEffects 和 commitLayoutEffects 阶段中各自的 if 判断中,useLayoutEffect 是通过if判断的,所以在 commitMutationEffects 阶段中,同步执行了useLayoutEffect 的上一次销毁函数,在 commitLayoutEffects 阶段中,同步执行了 useLayoutEffect 本次的执行函数,并注册上销毁函数。

结论

至此,我们粗略地查看了 commit 阶段的代码,分析了以下为什么 useEffect 是异步执行,而 useLayoutEffect 是同步执行,具体的代码我没有太过在文章中贴出来,因为这些都是可变的,真正的流程性的概览和 React 团队设计这一套机制的心智模型需要我们自己在不断调试代码和理解中慢慢去熟悉。

后续自己感兴趣的是 hooks 的实现,其中比较关键的 useReducer 会着重看一下源码,看看能不能写个简易版本的放到支付宝小程序中去实现一个 自定义的支付宝hooks 用于日常生产力开发。

到此这篇关于React中useEffect 与 useLayoutEffect的区别的文章就介绍到这了,更多相关React useEffect useLayoutEffect内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React事件绑定的方式详解

    React事件绑定的方式详解

    react事件绑定时。this并不会指向当前DOM元素。往往使用bind来改变this指向,今天通过本文给大家介绍React事件绑定的方式,感兴趣的朋友
    2021-07-07
  • React+Antd+Redux实现待办事件的方法

    React+Antd+Redux实现待办事件的方法

    这篇文章主要介绍了React+Antd+Redux实现待办事件的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • React获取url后面参数的值示例代码

    React获取url后面参数的值示例代码

    这篇文章主要介绍了React获取url后面参数的值示例代码,代码简单易懂,文末给大家补充介绍了react获取URL中参数方法,需要的朋友可以参考下
    2022-12-12
  • React替换传统拷贝方法的Immutable使用

    React替换传统拷贝方法的Immutable使用

    Immutable.js出自Facebook,是最流行的不可变数据结构的实现之一。它实现了完全的持久化数据结构,使用结构共享。所有的更新操作都会返回新的值,但是在内部结构是共享的,来减少内存占用
    2023-02-02
  • React 数据共享useContext的实现

    React 数据共享useContext的实现

    本文主要介绍了React 数据共享useContext的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • React 非父子组件传参的实例代码

    React 非父子组件传参的实例代码

    React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。本文给大家介绍React 非父子组件传参的相关知识,感兴趣的朋友跟随一起看看吧
    2021-04-04
  • react 项目 中使用 Dllplugin 打包优化技巧

    react 项目 中使用 Dllplugin 打包优化技巧

    在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 react,lodash,vue 我们希望能和自己的代码分离开,这篇文章主要介绍了react 项目 中 使用 Dllplugin 打包优化,需要的朋友可以参考下
    2023-01-01
  • React Native全面屏状态栏和底部导航栏适配教程详细讲解

    React Native全面屏状态栏和底部导航栏适配教程详细讲解

    最近在写 React Native 项目,调试应用时发现顶部状态栏和底部全面屏手势指示条区域不是透明的,看起来很难受。研究了一下这个问题,现在总结一下解决方案,这篇文章主要介绍了React Native全面屏状态栏和底部导航栏适配教程
    2023-01-01
  • react 下拉框内容回显的实现思路

    react 下拉框内容回显的实现思路

    这篇文章主要介绍了react 下拉框内容回显,实现思路是通过将下拉框选项的value和label一起存储到state中, 初始化表单数据时将faqType对应的label查找出来并设置到Form.Item中,最后修改useEffect,需要的朋友可以参考下
    2024-05-05
  • 使用react-color实现前端取色器的方法

    使用react-color实现前端取色器的方法

    本文通过代码给大家介绍了使用react-color实现前端取色器的方法,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-11-11

最新评论