服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服务器之家 - 编程语言 - JavaScript - js教程 - 精准解析 useLayoutEffect 与 useEffect 的执行时机

精准解析 useLayoutEffect 与 useEffect 的执行时机

2024-01-18 16:01这波能反杀 js教程

当我们使用 useLayoutEffect 时他可能会覆盖你想要执行的渲染内容,也有可能会阻塞你的正常渲染过程,因此我们在使用它时,需要精确把控他的执行时机,防止出现你不想看到的结果

精准解析 useLayoutEffect 与 useEffect 的执行时机

我们前面花了大量篇幅,从基础、理论、实践、总结几个方面,全方位的为大家分析了 useEffect。除此之外,React 还提供了一个与 useEffect 几乎一样的 hook,它就是useLayoutEffect。

我们约定,useEffect 传入的第一个参数为 effect,useLayoutEffect 传入的第一个参数为 layoutEffect。

他们的语法为:

// 中括号表示参数可选
useEffect(effect[, deps])
useLayoutEffect(layoutEffect[, deps])

两个 hook 有高度相似的语义。

第一个参数 layoutEffect 为一个函数,定义为副作用执行逻辑,我们也可以在 layoutEffect 中定义返回函数。当依赖项发生了变化时,返回函数会使用依赖项旧值首先执行,然后再执行 layoutEffect。

useLayoutEffect(() => {

  // ...
  
  return () => {}
}, [state])

第二个参数为依赖项数组。React 内部会使用 Object.is 去比较依赖项是否发生了变化,我们通常会选择使用 state 或者 props 等响应性数据作为依赖项。依赖项也可以不传,此时 layoutEffect 在每次状态发生变化时都会执行。

useLayoutEffect 与 useEffect 唯一的区别在于 effect 与 layoutEffect 执行时机的不同。

我们借助一个例子来仔细分析他们的准确执行时机。

首先是 useEffect。

const [count, setCount] = useState(0)

useEffect(() => {
  document.title = `React ${count}`
})

effect 会在组件渲染完成之后执行。这里组件渲染完成的意思是当组件内容已经呈现在页面上之后,effect 再执行,具体的步骤如下图所示:

精准解析 useLayoutEffect 与 useEffect 的执行时机

在事件循环中, effect 是被定义为宏任务,在下一轮循环执行。

然后是 useLayoutEffect。

const [count, setCount] = useState(0)

useLayoutEffect(() => {
  document.title = `React ${count}`
})

layoutEffect 会在组件渲染之前执行。具体的步骤如下图。

精准解析 useLayoutEffect 与 useEffect 的执行时机

但是这里如果只是这样理解的话,估计很多人并不太清晰具体是怎么回事。因为这样的表达并没有说清楚具体的执行时刻。更准确的说法是在 commit 之后,组件内容绘制呈现到屏幕之前。

例如我们有这样一段代码。

// 此时已经对DOM发送改变的指令
div.style.color = 'red'

layoutEffect()

layoutEffect 紧随 DOM 修改指令发出之后执行,此时虽然 DOM 指令已经发出,但是在浏览器的机制中,内容绘制是一个异步的过程,这会儿绘制并没有执行。

因此在事件循环中,layoutEfect 被定义为类似于 Promise 的微任务,在 DOM 指令修改之后,内容绘制之前执行。

后续影响

大家可以猜想一下,如果我们在 layoutEffect 中直接去修改 state,会发生什么事情。

看看下面这个例子:

function Demo() {
  const [count, setCount] = useState(0)
  
  useLayoutEffect(() => {
    if (count == 0) {
      setCount(1)
    }
  }, [count])
  
    return (
    <div>
      <div>{count}</div>
      <button 
        onClick={() => setCount(0)}
      >
        reset 0
      </button>
    </div>
  )
}

我们在 state 中声明一个变量 count,初始值设置为 0,并定义 layoutEffect,其中的逻辑就是当 count == 0 时,将 count 设置为 1。

添加一个按钮,当按钮点击时,把 count 重新设置为 0。

大家思考一下,此时,页面上的显示结果,会在 0 和 1 之间来回切换吗?

答案是不会。

因为当我们执行 layoutEffect 时,UI 并没有进入事件循环的绘制流程,此时还处于 JS 逻辑的执行过程中,那么这个时候执行 setCount,整个逻辑会重新执行,对于浏览器而言,JS 针对同一个 UI 发出了两条不同的指令,在浏览器的渲染机制中,也会发生收集行为,将这两条指令进行合并,最后只执行一条。

// setCount(0)
div.innerHTML = 0

// setCount(1)
div.innerHTML = 1

如上例,当 setCount(0) 与 setCount(1)  执行完之后,实际上是发出了两条修改元素内容的指令给到浏览器。

当我们使用 useLayoutEffect 时他可能会覆盖你想要执行的渲染内容,也有可能会阻塞你的正常渲染过程,因此我们在使用它时,需要精确把控他的执行时机,防止出现你不想看到的结果。

但是很明显我们可以看到 layoutEffect 的执行时机比 effect 更早。因此我们也可以在 layoutEffect 中,执行一些轻量的,不直接影响 state 的逻辑。

原文地址:https://mp.weixin.qq.com/s/Bdwl0lDPVWYttTfEFkehsA

延伸 · 阅读

精彩推荐
  • js教程基于JS绘制2021的烟花效果 附源码下载

    基于JS绘制2021的烟花效果 附源码下载

    这篇文章主要介绍了基于JS绘制2021的烟花效果,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...

    3772022-02-17
  • js教程游戏开发中如何使用CocosCreator进行音效处理

    游戏开发中如何使用CocosCreator进行音效处理

    这篇文章主要介绍了游戏开发中如何使用CocosCreator进行音效处理,并对音效组件进行封装,方便以后使用,同学们看完之后,一定要亲手实验一下...

    weixin_397450136882022-03-01
  • js教程JS实现页面侧边栏效果探究

    JS实现页面侧边栏效果探究

    这篇文章主要介绍了JS实现页面侧边栏效果探究,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以...

    行舟客5402021-12-29
  • js教程CocosCreator学习之模块化脚本

    CocosCreator学习之模块化脚本

    这篇文章主要介绍了Cocos Creator 模块化脚本,想加深学习CocosCreator脚本的同学,一定要看一下...

    麦克煎蛋7732022-03-05
  • js教程JavaScript 生成唯一ID的几种方式

    JavaScript 生成唯一ID的几种方式

    这篇文章主要介绍了JavaScript 生成唯一ID的几种方式,帮助大家更好的理解和使用JavaScript,感兴趣的朋友可以了解下...

    specialCoder5602022-01-21
  • js教程微信小程序实现简单计算器

    微信小程序实现简单计算器

    这篇文章主要为大家详细介绍了微信小程序写简单计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    m0_459721569282022-02-28
  • js教程JS实现选项卡插件的两种写法(jQuery和class)

    JS实现选项卡插件的两种写法(jQuery和class)

    这篇文章主要为大家详细介绍了JS实现选项卡插件的两种写法:jQuery和class,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参...

    南柯Seven12322021-12-22
  • js教程JS实现鼠标移动拖尾

    JS实现鼠标移动拖尾

    这篇文章主要为大家详细介绍了JS实现鼠标移动拖尾效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    day010272021-12-21