React中useEffect 与 useLayoutEffect的区别

目录
  • 前置知识
  • useEffect
    • commitBeforeMutationEffects
    • commitMutationEffects
    • commitLayoutEffects
    • 后续阶段
  • 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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React useEffect的理解与使用

    React16.8新增的useEffec这个hook函数就是处理副作用的. 所谓的"副作用",举个通俗一点的例子,假如感冒了本来吃点药就没事了,但是吃了药发现身体过敏了,而这个"过敏"就是副作用. 放到React中,本来只是想渲染DOM展示到页面上,但除了DOM之外还有数据,而这些数据必须从外部的数据源中获取,这个"获取外部数据源"的过程就是副作用. useEffect怎么用可以参考官网给出的例子,这里主要针对使用useEffect过程中遇到的问

  • 浅谈react useEffect闭包的坑

    问题代码 看一段因为useEffect导致的闭包问题代码 const btn = useRef(); const [v, setV] = useState(''); useEffect(() => { let clickHandle = () => { console.log('v:', v); } btn.current.addEventListener('click', clickHandle) return () => { btn.removeEventListener('clic

  • React中useEffect 与 useLayoutEffect的区别

    目录 前置知识 useEffect commitBeforeMutationEffects commitMutationEffects commitLayoutEffects 后续阶段 useLayoutEffect 结论 前置知识 我们可以将 React 的工作流程划分为几大块: render 阶段:主要生成 Fiber节点 并构建出完整的 Fiber树 commit 阶段:在上一个render 阶段中会在 rootFiber 上生成一条副作用链表,应用的DOM操作就会在本阶段执行 commi

  • 关于 React 中 useEffect 使用问题浅谈

    目录 前言 优化前 优化后 总结 前言 最近看了一下 ant-design 中的 tree 组件源码时发现 useEffect 中根据 props 来计算当前函数组件的 state 的,感到好奇,因为这样会导致应用重新绘制一次,这样才复杂场景下会对应用有一定的性能影响.为了验证自己猜想是否正确做了一下实践.这里的 React 是官方 16.12.0的源码. 优化前 import * as React from './react-source/packages/react' import * as

  • 解析React中useMemo与useCallback的区别

    useMemo 把“创建”函数和依赖项数组作为参数传⼊入useMemo,它仅会在某个依赖项改变时才重新计算memoized 值.这种优化有助于避免在每次渲染时都进⾏行行⾼高开销的计算. importReact, { useState, useMemo } from"react"; export default functionUseMemoPage(props) { const [count, setCount] =useState(0); constexpensive=useMemo

  • React中useEffect与生命周期钩子函数的对应关系说明

    目录 React useEffect与生命周期钩子函数的对应关系 使用格式一:不带参数的情况 使用格式二:带第二个参数,参数为空数组 使用格式三:带第二个参数,并且指定了依赖项 使用格式四:依赖项为空,没有具体的副作用函数.但是有副作用函数的清理函数. React函数式组件用useEffect模拟三个生命周期钩子函数 React useEffect与生命周期钩子函数的对应关系 在React的函数组件中,useEffect的作用其实也对标了类组件中的生命周期,它的四种使用格式也与生命周期的四种钩子

  • React深入分析useEffect源码

    目录 热身准备 初始化 mount 更新 update updateEffect 执行副作用 总结 热身准备 这里不再讲useLayoutEffect,它和useEffect的代码是一样的,区别主要是: 执行时机不同: useEffect是异步, useLayoutEffect是同步,会阻塞渲染: 初始化 mount mountEffect 在所有hook初始化时都会通过下面这行代码实现hook结构的初始化和存储,这里不再讲mountWorkInProgressHook方法 var hook =

  • React中useLayoutEffect钩子使用场景详解

    目录 简介 useEffect钩子的概述 钩子流程 useLayoutEffect钩子的概述 钩子流程 什么时候使用useLayoutEffect钩子? 总结 简介 不久前,React对其功能组件进行了一次重大更新(在2019年3月的16.8版本中),终于为这些组件提供了一种变得有状态的方法. 钩子的加入不仅意味着功能组件将能够提供自己的状态,而且还能通过引入useEffect钩子来管理自己的生命周期事件. 此外,这次更新还引入了一个全新的useLayoutEffect钩子,根据React文档,

  • 说说react中引入css的方式有哪些并区别在哪

    目录 前言 方式 在组件内 组件中引入css文件 组件中引入 .module.css 文件 CSS in JS 区别 前言 组件式开发选择合适的css解决方案尤为重要 通常会遵循以下规则: 可以编写局部css,不会随意污染其他组件内的原生: 可以编写动态的css,可以获取当前组件的一些状态,根据状态的变化生成不同的css样式: 支持所有的css特性:伪类.动画.媒体查询等: 编写起来简洁方便.最好符合一贯的css风格特点 在这一方面,vue使用css起来更为简洁: 通过 style 标签编写样式

  • react中使用useEffect及踩坑记录

    目录 使用useEffect及踩坑记录 useEffect 介绍 useEffect常见跳坑 hooks中useEffect()使用总结 常见使用 useEffect() 的第二个参数说明 useEffect() 第一个函数参数的返回值 useEffect() 的注意点 使用useEffect及踩坑记录 useEffect 介绍 useEffect时reactHook中最重要,最常用的hook之一. useEffect相当于react中的什么生命周期呢? 这个问题在react官网中有过介绍,在使

  • react中的watch监视属性-useEffect介绍

    目录 react的watch监视属性-useEffect useEffect使用指南 最基本的使用 响应更新 如何处理Loading和Error 处理表单 自定义hooks 使用useReducer整合逻辑 取消数据请求 react的watch监视属性-useEffect 在vue中可以使用watch属性,去监视一个值,当这个值进行变化的时候就去执行一些操作.在react是没有这个属性的,但是它也一样可以达到相同的效果,那么接下来看看它是怎么实现的呢? 在react中实现监听效果有一个比较简单的

  • React中hook函数与useState及useEffect的使用

    目录 1. 简介 2. useState使用 3. useEffect使用 useEffect发起网络请求 1. 简介 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数组件,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染.React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数

随机推荐