HOW - React 如何在在浏览器绘制之前同步执行 - useLayoutEffect

目录

  • useEffect vs useLayoutEffect
    • useEffect
    • useLayoutEffect
    • 主要区别总结
    • 选择建议
    • 注意事项
  • useLayoutEffect 使用示例
    • 测量 DOM 元素的尺寸和位置
      • 示例:自适应弹出框定位
    • 同步更新样式以避免闪烁
      • 示例:根据内容动态调整容器高度
    • 图像或 Canvas 绘制前的准备工作
      • 示例:Canvas 绘制自定义图形

useEffect vs useLayoutEffect

useLayoutEffectuseEffect 都是 React Hooks 中用于处理副作用的钩子,但它们在执行时机和用途上有一些关键区别。

理解这些区别有助于在不同的场景下选择合适的钩子来优化组件的性能和用户体验。

useEffect

  • 执行时机

    • useEffect 的回调函数会在浏览器完成渲染之后异步执行。这意味着它不会阻塞浏览器的绘制过程。
  • 用途

    • 适用于大多数副作用操作,如数据获取、订阅、手动操作 DOM(例如添加事件监听器)、设置定时器等。
    • 因为它在渲染后异步执行,所以不会导致用户界面的阻塞,适合处理不影响当前渲染结果的副作用。
  • 示例

import React, { useEffect } from 'react';function Example() {useEffect(() => {// 这里的代码会在组件渲染到屏幕后异步执行console.log('useEffect 执行');return () => {// 清理函数console.log('useEffect 清理');};}, []); // 空依赖数组表示只在组件挂载和卸载时执行return <div>Hello World</div>;
}

useLayoutEffect

  • 执行时机

    • useLayoutEffect 的回调函数会在 DOM 更新完成后、浏览器进行绘制之前同步执行。这意味着它会阻塞浏览器的绘制过程,直到回调函数执行完毕。
  • 用途

    • 适用于需要在浏览器绘制之前同步执行的操作,如测量 DOM 元素的尺寸、位置,或者进行需要同步更新样式的操作。
    • 由于它会阻塞渲染,应谨慎使用,避免在高频率更新的场景下使用,以免影响性能。
  • 示例

import React, { useLayoutEffect, useRef, useState } from 'react';function LayoutEffectExample() {const divRef = useRef(null);const [width, setWidth] = useState(0);useLayoutEffect(() => {// 在浏览器绘制之前同步获取 DOM 元素的宽度if (divRef.current) {const { width } = divRef.current.getBoundingClientRect();setWidth(width);}}, []);return (<div><div ref={divRef} style={{ width: '50%', border: '1px solid black' }}>测量宽度</div><p>宽度: {width}px</p></div>);
}

主要区别总结

特性useEffectuseLayoutEffect
执行时机渲染后异步执行渲染后、绘制前同步执行
阻塞渲染不阻塞阻塞
适用场景大多数副作用操作,如数据获取、订阅等需要同步操作 DOM 或测量布局的场景
性能影响较低,适合频繁使用的副作用高,需谨慎使用以避免阻塞渲染

选择建议

  • 优先使用 useEffect:在大多数情况下,useEffect 足以满足需求,并且由于其异步执行的特性,不会影响用户界面的渲染性能。

  • 必要时使用 useLayoutEffect:只有在确实需要在浏览器绘制之前同步执行某些操作时,才使用 useLayoutEffect。例如,调整布局、测量元素尺寸等。

注意事项

  • 避免过度使用 useLayoutEffect:由于其会阻塞渲染,频繁或不当使用可能导致页面卡顿或响应缓慢。

  • 清理函数:与 useEffect 一样,useLayoutEffect 也支持返回一个清理函数,用于在组件卸载或依赖项变化时执行清理操作。

通过理解 useEffectuseLayoutEffect 的区别,可以更有效地管理组件的副作用,优化性能,并提升用户体验。

useLayoutEffect 使用示例

测量 DOM 元素的尺寸和位置

在某些情况下,你需要精确知道某个 DOM 元素的实际尺寸(宽度、高度)或在页面中的位置信息,以便根据这些信息进行后续的操作,而这些操作必须在浏览器绘制之前完成,否则可能会导致测量的不准确。

示例:自适应弹出框定位

当你有一个弹出框,需要根据触发它的元素的相对位置来动态定位时,就需要先测量触发元素的尺寸和位置,然后根据这些信息计算出弹出框的合适位置。

import React, { useRef, useState, useLayoutEffect } from 'react';const PopupExample = () => {const triggerRef = useRef(null);const popupRef = useRef(null);const [position, setPosition] = useState({ top: 0, left: 0 });useLayoutEffect(() => {if (triggerRef.current && popupRef.current) {const triggerRect = triggerRef.current.getBoundingClientRect();// 简单示例,将弹出框定位在触发元素的下方const newTop = triggerRect.bottom + window.scrollY;const newLeft = triggerRect.left + window.scrollX;setPosition({ top: newTop, left: newLeft });}}, []);return (<div><button ref={triggerRef}>点击显示弹出框</button><div ref={popupRef} style={{ position: 'absolute', top: position.top, left: position.left }}>这是一个弹出框</div></div>);
};export default PopupExample;

在上述代码中,useLayoutEffect 会在浏览器绘制之前同步执行,确保能够准确获取触发元素的尺寸和位置信息,从而正确地定位弹出框。

同步更新样式以避免闪烁

有时候,你需要根据某些计算结果立即更新元素的样式,而且希望这些更新在浏览器绘制之前完成,以避免出现样式闪烁的问题。

示例:根据内容动态调整容器高度

当容器内的内容动态变化时,你可能希望容器的高度能够立即适应内容的变化,而不让用户看到内容先溢出再调整高度的过程。

import React, { useRef, useState, useLayoutEffect } from 'react';const DynamicHeightContainer = () => {const containerRef = useRef(null);const [content, setContent] = useState('初始内容');const toggleContent = () => {setContent(prevContent => prevContent === '初始内容' ? '这是一段更长更长的内容,用于测试容器高度的动态调整。' : '初始内容');};useLayoutEffect(() => {if (containerRef.current) {// 这里可以根据内容做一些样式调整,例如设置高度// 示例中简单打印信息,实际应用中可根据需求修改样式console.log('根据内容调整容器样式');}}, [content]);return (<div><button onClick={toggleContent}>切换内容</button><div ref={containerRef} style={{ border: '1px solid black' }}>{content}</div></div>);
};export default DynamicHeightContainer;

在这个例子中,useLayoutEffect 确保在浏览器绘制之前根据内容的变化同步更新容器的样式,避免了内容溢出或高度闪烁的问题。

图像或 Canvas 绘制前的准备工作

在进行图像处理或使用 Canvas 进行绘图时,有时需要在绘制之前获取一些必要的信息或进行一些预处理操作,这些操作也需要在浏览器绘制之前完成。

示例:Canvas 绘制自定义图形

在使用 Canvas 绘制复杂图形时,可能需要先测量某些元素的位置或尺寸,然后根据这些信息进行绘制。

import React, { useRef, useLayoutEffect } from 'react';const CanvasDrawing = () => {const canvasRef = useRef(null);useLayoutEffect(() => {const canvas = canvasRef.current;const ctx = canvas.getContext('2d');// 假设这里需要根据某个 DOM 元素的位置来绘制图形// 先进行相关测量等准备工作(此处简化)// 开始绘制ctx.fillStyle = 'red';ctx.fillRect(10, 10, 50, 50);}, []);return <canvas ref={canvasRef} width="200" height="200"></canvas>;
};export default CanvasDrawing;

在上述代码中,useLayoutEffect 可以确保在进行 Canvas 绘制之前完成所有必要的准备工作,保证绘制的准确性和流畅性 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/72860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【无人机三维路径规划】基于CPO冠豪猪优化算法的无人机三维路径规划Maltab

代码获取基于CPO冠豪猪优化算法的无人机三维路径规划Maltab 基于CPO冠豪猪优化算法的无人机三维路径规划 一、CPO算法的基本原理与核心优势 冠豪猪优化算法&#xff08;Crested Porcupine Optimizer, CPO&#xff09;是一种新型元启发式算法&#xff0c;其灵感来源于冠豪猪的…

深度学习驱动的智能化革命:从技术突破到行业实践

第一章 深度学习的技术演进与核心架构 1.1 从浅层网络到深度学习的范式转变 深度学习的核心在于通过多层次非线性变换自动提取数据特征,其发展历程可划分为三个阶段:符号主义时代的规则驱动(1950s-1980s)、连接主义时代的浅层网络(1990s-2000s)以及深度学习时代的端到端…

简洁实用的3个免费wordpress主题

高端大气动态炫酷的免费企业官网wordpress主题 非常简洁的免费wordpress主题&#xff0c;安装简单、设置简单&#xff0c;几分钟就可以搭建好一个wordpress网站。 经典风格的免费wordpress主题 免费下载 https://www.fuyefa.com/wordpress

RabbitMQ 高级特性解析:RabbitMQ 消息可靠性保障 (上)

RabbitMQ 核心功能 RabbitMQ 高级特性解析&#xff1a;RabbitMQ 消息可靠性保障 &#xff08;上&#xff09;-CSDN博客 RabbitMQ 高级特性&#xff1a;从 TTL 到消息分发的全面解析 &#xff08;下&#xff09;-CSDN博客 前言 最近再看 RabbitMQ&#xff0c;看了看自己之前写…

用DeepSeek-R1-Distill-data-110k蒸馏中文数据集 微调Qwen2.5-7B-Instruct!

下载模型与数据 模型下载&#xff1a; huggingface&#xff1a; Qwen/Qwen2.5-7B-Instruct HF MirrorWe’re on a journey to advance and democratize artificial intelligence through open source and open science.https://hf-mirror.com/Qwen/Qwen2.5-7B-Instruct 魔搭&a…

在IDEA中进行git回滚操作:Reset current branch to here‌或Reset HEAD

问题描述 1&#xff09;在本地修改好的代码&#xff0c;commit到本地仓库&#xff0c;突然发觉有问题不想push推到远程仓库了&#xff0c;但它一直在push的列表中存在&#xff0c;那该怎么去掉push列表中的内容呢&#xff1f; 2&#xff09;合并别的分支到当前分支&#xff0…

六十天前端强化训练之第十一天之事件机制超详解析

欢迎来到编程星辰海的博客讲解 目录 一、事件模型演进史 1.1 原始事件模型&#xff08;DOM Level 0&#xff09; 1.2 DOM Level 2事件模型 1.3 DOM Level 3事件模型 二、事件流深度剖析 2.1 捕获与冒泡对比实验 2.2 事件终止方法对比 三、事件委托高级应用 3.1 动态元…

Qwen架构与Llama架构的核心区别

我们在讨论Deepseek不同版本之间的区别时了解到,DeepSeek-R1的蒸馏模型分为Qwen和Llama两个系列,包括Qwen系列的0.5B、1.5B、3B、7B、14B、32B、72B和Llama系列的8B、70B。Qwen系列以阿里通义千问(Qwen)为基础模型架构(具体是Qwen-2.5),Llama系列以Meta的Llama为基础模型…

匿名GitHub链接使用教程(Anonymous GitHub)2025

Anonymous GitHub 1. 引言2. 准备3. 进入Anonymous GitHub官网4. 用GitHub登录匿名GitHub并授权5. 进入个人中心&#xff0c;然后点击• Anonymize Repo实例化6. 输入你的GitHub链接7. 填写匿名链接的基础信息8. 提交9. 实例化对应匿名GitHub链接10. 进入个人中心管理项目11. 查…

工程化与框架系列(25)--低代码平台开发

低代码平台开发 &#x1f527; 引言 低代码开发平台是一种通过可视化配置和少量代码实现应用开发的技术方案。本文将深入探讨低代码平台的设计与实现&#xff0c;包括可视化编辑器、组件系统、数据流管理等关键主题&#xff0c;帮助开发者构建高效的低代码开发平台。 低代码…

Redis系列之慢查询分析与调优

Redis 慢查询分析与优化&#xff1a;提升性能的实战指南 Redis 作为一款高性能的内存数据库&#xff0c;因其快速的数据读写能力和灵活的数据结构&#xff0c;被广泛应用于缓存、消息队列、排行榜等多种业务场景。然而&#xff0c;随着业务规模的扩大和数据量的增加&#xff0…

Git系列之git tag和ReleaseMilestone

以下是关于 Git Tag、Release 和 Milestone 的深度融合内容&#xff0c;并补充了关于 Git Tag 的所有命令、详细解释和指令实例&#xff0c;条理清晰&#xff0c;结合实际使用场景和案例。 1. Git Tag 1.1 定义 • Tag 是 Git 中用于标记特定提交&#xff08;commit&#xf…

开源项目介绍:Native-LLM-for-Android

项目地址&#xff1a;Native-LLM-for-Android 创作活动时间&#xff1a;2025年 支持在 Android 设备上运行大型语言模型 &#xff08;LLM&#xff09; &#xff0c;具体支持的模型包括&#xff1a; DeepSeek-R1-Distill-Qwen: 1.5B Qwen2.5-Instruct: 0.5B, 1.5B Qwen2/2.5VL:…

深入理解 Java 虚拟机内存区域

Java 虚拟机&#xff08;JVM&#xff09;是 Java 程序运行的核心环境&#xff0c;它通过内存管理为程序提供高效的执行支持。JVM 在运行时将内存划分为多个区域&#xff0c;每个区域都有特定的作用和生命周期。本文将详细介绍 JVM 的运行时数据区域及其功能&#xff0c;并探讨与…

PDF转JPG(并去除多余的白边)

首先&#xff0c;手动下载一个软件&#xff08;poppler for Windows&#xff09;&#xff0c;下载地址&#xff1a;https://github.com/oschwartz10612/poppler-windows/releases/tag/v24.08.0-0 否则会出现以下错误&#xff1a; PDFInfoNotInstalledError: Unable to get pag…

深入剖析MyBatis缓存机制:原理、源码与实战指南

引言 MyBatis作为一款优秀的ORM框架,其缓存机制能显著提升数据库查询性能。但许多开发者仅停留在“知道有缓存”的层面,对其实现原理和细节知之甚少。本文将结合可运行的代码示例和源码分析,手把手带您彻底掌握MyBatis缓存机制。 一、MyBatis缓存分类 MyBatis提供两级缓存…

Vue 使用 vue-router 时,多级嵌套路由缓存问题处理

Vue 使用 vue-router 时&#xff0c;多级嵌套路由缓存问题处理 对于三级菜单&#xff08;或多级嵌套路由&#xff09;&#xff0c;vue 都是 通过 keep-alive 组件来实现路由组件的缓存。 有时候三级或者多级路由时&#xff0c;会出现失效情况。以下是三级菜单缓存的例子。 最…

QSplitter保存和读取

官方文档提供的方案 保存 connect(ui->splitter, &QSplitter::splitterMoved, [](){settings.setValue("splitterSizes", ui->splitter->saveState()); });读取 ui->splitter->restoreState(settings.value("splitterSizes").toByteA…

VanillaVueSvelteReactSolidAngularPreact前端框架/库的简要介绍及其优势

VanillaVueSvelteReactSolidAngularPreact前端框架/库的简要介绍及其优势。以下是这些前端框架/库的简要介绍及其优势&#xff1a; 1. Vanilla 定义&#xff1a;Vanilla 并不是一个框架&#xff0c;而是指 原生 JavaScript&#xff08;即不使用任何框架或库&#xff09;。优势…

Java多线程与高并发专题——关于CopyOnWrite 容器特点

引入 在 CopyOnWriteArrayList 出现之前&#xff0c;我们已经有了 ArrayList 和 LinkedList 作为 List 的数组和链表的实现&#xff0c;而且也有了线程安全的 Vector 和Collections.synchronizedList() 可以使用。 首先我们来看看Vector是如何实现线程安全的 &#xff0c;还是…