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.pswp.cn/bicheng/72860.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72860.shtml
英文地址,請注明出處:http://en.pswp.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;還是…