欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React useEffect、useLayoutEffect底層機制及區(qū)別介紹

 更新時間:2025年04月10日 11:47:42   作者:不叫貓先生  
useEffect 是 React 中的一個 Hook,允許你在函數(shù)組件中執(zhí)行副作用操作,本文給大家介紹React useEffect、useLayoutEffect底層機制及區(qū)別介紹,感興趣的朋友一起看看吧

useEffect

useEffect 是 React 中的一個 Hook,允許你在函數(shù)組件中執(zhí)行副作用操作。副作用(Side Effects)是指組件中不直接涉及渲染過程的行為,例如數(shù)據(jù)獲取、事件監(jiān)聽、訂閱、設(shè)置定時器、手動修改 DOM 等。

基本用法:

useEffect(() => {
  // 執(zhí)行副作用操作
  // 可以是數(shù)據(jù)獲取、訂閱等操作
  return () => {
    // 可選的清理操作,清理副作用
  };
}, [dependencies]);

不設(shè)置依賴

useEffect(()=>{
  //獲取最新的狀態(tài)值
})
  • 第一次渲染完成后,執(zhí)行callback,等價于 componentDidMount
  • 在組件每一次更新完畢后,也會執(zhí)行callback,等價于 componentDidUpdate

下面的寫法可以獲取到最新的狀態(tài)值

const Demo = function Demo() {
    let [num, setNum] = useState(0),
        [x, setX] = useState(100);
    useEffect(() => {
        // 獲取最新的狀態(tài)值
        console.log('@1', num);
    });
    const handle = () => {
        setNum(num + 1);
    };
    return <div className="demo">
        <span className="num">{num}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

設(shè)置空數(shù)組,無依賴

useEffect(()=>{  },[])

只有第一次渲染完畢后,才會執(zhí)行callback,每一次視圖更新完畢后,callback不再執(zhí)行,「類似于componentDidMount

初次渲染,打印@1 @2 ,點擊按鈕之后,只打印出@1

const Demo = function Demo() {
    let [num, setNum] = useState(0),
        [x, setX] = useState(100);
    useEffect(() => {
        // 獲取最新的狀態(tài)值
        console.log('@1', num);
    });
    useEffect(() => {
        console.log('@2', num);
    }, []);
    const handle = () => {
        setNum(num + 1);
    };
    return <div className="demo">
        <span className="num">{num}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

設(shè)置多個依賴

useEffect(() => {
   }, [依賴項1,依賴項2,依賴項3]);
  • 第一次渲染完畢會執(zhí)行callback
  • 依賴的狀態(tài)值(或者多個依賴狀態(tài)中的一個)發(fā)生變化,也會出發(fā)callback執(zhí)行
  • 但是依賴的狀態(tài)如果沒有變化,在組件更新的時候,callback是不會執(zhí)行
const Demo = function Demo() {
    let [num, setNum] = useState(0),
        [x, setX] = useState(100);
    useEffect(() => {
        console.log('@3', num);
    }, [num]);
    const handle = () => {
        setNum(num + 1);
    };
    return <div className="demo">
        <span className="num">{num}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

返回值是一個函數(shù)

useEffect(()=>{ return ()=>{ 
//獲取的是上一次狀態(tài)的值
//返回的函數(shù),會在組件釋放的時候執(zhí)行
} } )
  • 初始渲染之后返回一個小函數(shù),放到鏈表當中
  • 如果組件更新,會通過updateEffect會把上一次返回的函數(shù)執(zhí)行「可以“理解為”上一次渲染的組件釋放了」
const Demo = function Demo() {
    let [num, setNum] = useState(0),
        [x, setX] = useState(100);
    useEffect(() => {
        return () => {
            // 獲取的是上一次的狀態(tài)值
            console.log('@4', num);
        };
    });
    const handle = () => {
        setNum(num + 1);
    };
    return <div className="demo">
        <span className="num">{num}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

總結(jié)

useEffect的使用環(huán)境

useEffect必須是在函數(shù)的最外層上下文中調(diào)用,不能把其嵌入到條件判斷、循環(huán)等操作語句中。
下面是錯誤的寫法:

const Demo = function Demo() {
    let [num, setNum] = useState(0);
   if (num > 5) {
        useEffect(() => {
            console.log('OK');
        });
    }
    const handle = () => {
        setNum(num + 1);
    };
    return <div className="demo">
        <span className="num">{num}</span>
        <Button type="primary"
            size="small"
            onClick={handle}>
            新增
        </Button>
    </div>;
};

正確的應該是這樣,把邏輯寫在useEffect內(nèi)部:

  useEffect(() => {
        if (num > 5) {
            console.log('OK');
        }
    }, [num]);

useEffect 中發(fā)送請求

首先模擬一個請求

// 模擬從服務器異步獲取數(shù)據(jù)
const queryData = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve([10, 20, 30]);
        }, 1000);
    });
};

錯誤示例

這樣寫會直接進行報錯。useEffect如果設(shè)置返回值,則返回值必須是一個函數(shù)「代表組件銷毀時觸發(fā)」;下面案例中,callback經(jīng)過async的修飾,返回的是一個promise實例,不符合要求,所以報錯!

useEffect(async ()=>{
   let data = await queryData();
   console.log(”成功“,data)
},[])

用.then獲取數(shù)據(jù)

直接調(diào)用queryData,通過.then獲取數(shù)據(jù)

useEffect(async ()=>{
   queryData().then(data=>{
   console.log(”成功“,data)
      })
},[])

在useEffect創(chuàng)建一個函數(shù)

useEffect返回值里創(chuàng)建一個函數(shù)并調(diào)用

useEffect( ()=>{
    const next = async()=>{
     let data = await queryData();
     console.log(”成功“,data)
    };
    next();
},[])

總結(jié)

useLayoutEffect

useLayoutEffect useEffect 具有相似的 API 和用法,但它們的執(zhí)行時機不同。useLayoutEffect 是 同步執(zhí)行 的,它會在瀏覽器 繪制(paint)之前 執(zhí)行副作用操作。

基本用法:

useLayoutEffect(() => {
  // 執(zhí)行副作用操作,特別是需要與 DOM 布局相關(guān)的操作
  return () => {
    // 可選的清理操作
  };
}, [dependencies]);

useLayoutEffect 和useEffect區(qū)別

useLayoutEffect 會阻塞瀏覽器渲染真實DOM,優(yōu)先執(zhí)行Effect鏈表中的callback;

useEffect不會阻塞瀏覽器渲染真實DOM,在渲染真實DOM的同時,去執(zhí)行Effect鏈表中的callback;

  • useLayoutEffect設(shè)置的callback要優(yōu)先于useEffect去執(zhí)行
  • 在兩者設(shè)置的callback中,依然可以獲取DOM元素「因為這是的DOM對象已經(jīng)創(chuàng)建了,區(qū)別只是瀏覽器是否渲染」
  • 如果在callback函數(shù)中又修改了狀態(tài)值「視圖又要更新」
    • useEffect:瀏覽器肯定是把第一次的真實DOM已經(jīng)繪制,再去渲染第二次的真實
    • DOMuseLayoutEffect:瀏覽器是把兩次真實DOM的渲染,合并在一起渲染

視圖更新的步驟
1、基于babel-preset-react-app把JSX便衣乘createElement`格式

2、把createElement執(zhí)行,創(chuàng)建virtualDOM

3、基于root.render方法把virtual變?yōu)檎鎸岲OM對象「DOM- DIFF」
useLayoutEffect 阻塞第4步操作,先去執(zhí)行Effect鏈表中的方法「同步操作」
useEffect第4步操作和Effect鏈表中的方法執(zhí)行,是同時進行的「異步操作」

4、瀏覽器渲染和繪制真實DOM對象

下面先打印出useLayoutEffect,再打印出useEffect

const Demo = function Demo() {
    // console.log('RENDER');
    let [num, setNum] = useState(0);
    useLayoutEffect(() => {
        console.log('useLayoutEffect'); //第一個輸出
    }, [num]);
    useEffect(() => {
        console.log('useEffect'); //第二個輸出
    }, [num]);
    return <div className="demo"
        style={{
            backgroundColor: num === 0 ? 'red' : 'green'
        }}>
        <span className="num">{num}</span>
        <Button type="primary" size="small"
            onClick={() => {
                setNum(0);
            }}>
            新增
        </Button>
    </div>;
};

執(zhí)行時機:瀏覽器渲染的關(guān)系

useEffect:

useEffect 是 異步 執(zhí)行的,它是在 React 更新完 DOM 后(即瀏覽器繪制之后)執(zhí)行的。瀏覽器渲染通常分為幾個階段:

瀏覽器渲染:更新 DOM、進行布局計算、繪制頁面等。

React 執(zhí)行副作用(useEffect):在頁面渲染完成后,再去執(zhí)行副作用。

這種順序意味著 useEffect 中的副作用操作不會阻塞瀏覽器渲染。換句話說,React 在觸發(fā) useEffect 后,會立即開始瀏覽器的繪制過程,所以不會影響頁面的視覺展示。

舉個例子,如果你使用 useEffect 來發(fā)起 API 請求,React 會等到瀏覽器完成渲染后,再去發(fā)起請求,不會影響渲染速度。

useLayoutEffect:

useLayoutEffect 與 useEffect 的最大區(qū)別是它會 同步執(zhí)行,并且會在 DOM 更新后但在瀏覽器渲染(繪制)之前執(zhí)行。執(zhí)行順序如下:

React 更新虛擬 DOM 和 DOM:這一步會根據(jù)組件的變化更新頁面結(jié)構(gòu)。

useLayoutEffect 執(zhí)行:同步執(zhí)行副作用,這時 DOM 已經(jīng)更新,但瀏覽器還沒進行繪制。

瀏覽器繪制:完成頁面渲染。

這意味著 useLayoutEffect 會 阻塞 渲染,直到它執(zhí)行完成后,瀏覽器才會進行頁面渲染。因此,如果 useLayoutEffect 中執(zhí)行的操作非常耗時,可能會導致頁面渲染延遲,影響用戶體驗。

對瀏覽器渲染的影響

useEffect 的影響:

異步執(zhí)行:不會阻塞頁面渲染,可以在渲染完成后執(zhí)行副作用操作。

不會影響頁面視覺:由于 useEffect 在瀏覽器渲染完成后才執(zhí)行,它不會導致頁面布局變化,也不會造成視覺閃爍。

性能優(yōu)化:因為是異步執(zhí)行的,所以瀏覽器渲染不會被卡住,頁面的響應速度和流暢性得到保證。

useLayoutEffect 的影響:

同步執(zhí)行:會在 DOM 更新后但在頁面渲染之前立即執(zhí)行副作用,阻塞瀏覽器的繪制過程。

可能影響性能:由于同步執(zhí)行,瀏覽器渲染必須等待 useLayoutEffect 完成,如果副作用中有復雜的操作,可能會導致頁面加載時間延遲或出現(xiàn)白屏現(xiàn)象。

影響布局計算:適合用于獲取 DOM 元素的大小、位置等布局信息,因為它在頁面渲染之前執(zhí)行,你可以確保你拿到的是最新的、正確的布局信息。

使用場景

useEffect 的常見場景:

數(shù)據(jù)獲取:例如從 API 獲取數(shù)據(jù),或者發(fā)起網(wǎng)絡請求。

事件監(jiān)聽和取消訂閱:如為組件添加事件監(jiān)聽器(例如 resize 或 scroll),并在組件卸載時移除它們。

定時器/計時器:設(shè)置定時任務(如 setInterval 或 setTimeout),并在組件卸載時清理。

更新狀態(tài):例如當某個副作用觸發(fā)時更新組件狀態(tài),通常與 DOM 操作無關(guān)。

例如: 通過 useEffect 實現(xiàn)獲取數(shù)據(jù)并更新狀態(tài):

useEffect(() => {
  fetchData().then(data => {
    setData(data);
  });
}, []); // 依賴空數(shù)組,表示只在組件掛載時執(zhí)行

在組件中監(jiān)聽 resize 或 scroll 事件時,useEffect 是一個常見的選擇。你可以在 useEffect 中添加事件監(jiān)聽器,并在組件卸載時清除這些監(jiān)聽器。

import React, { useState, useEffect } from 'react';
function WindowResize() {
  const [windowWidth, setWindowWidth] = useState(window.innerWidth);
  useEffect(() => {
    // 定義事件處理函數(shù)
    const handleResize = () => {
      setWindowWidth(window.innerWidth); // 更新寬度狀態(tài)
    };
    // 在組件掛載時添加事件監(jiān)聽器
    window.addEventListener('resize', handleResize);
    // 返回清理函數(shù),在組件卸載時移除事件監(jiān)聽器
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 空數(shù)組,表示只在組件掛載和卸載時執(zhí)行
  return (
    <div>
      <p>Window width: {windowWidth}px</p>
    </div>
  );
}
export default WindowResize;

使用定時器來執(zhí)行某些定期操作,如每隔一定時間更新狀態(tài)。

import React, { useState, useEffect } from 'react';
function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    // 創(chuàng)建定時器,每秒增加一次秒數(shù)
    const intervalId = setInterval(() => {
      setSeconds((prevSeconds) => prevSeconds + 1);
    }, 1000);
    // 清理函數(shù),組件卸載時清除定時器
    return () => clearInterval(intervalId);
  }, []); // 空數(shù)組,表示只在組件掛載時設(shè)置定時器,卸載時清理
  return (
    <div>
      <p>Seconds: {seconds}</p>
    </div>
  );
}
export default Timer;

副作用可能會觸發(fā)狀態(tài)更新,特別是在某些條件發(fā)生變化時,比如從 API 獲取數(shù)據(jù)或處理輸入事件等。useEffect 在 inputValue 改變時觸發(fā),設(shè)置一個 500 毫秒的延遲,用 setTimeout 更新 delayedValue。每次 inputValue 更新時,都會清理上一個定時器,避免舊的定時器執(zhí)行。這樣,delayedValue 會延遲顯示輸入框的值,實現(xiàn)了一個防抖的效果。

import React, { useState, useEffect } from 'react';
function InputWithDelay() {
  const [inputValue, setInputValue] = useState('');
  const [delayedValue, setDelayedValue] = useState('');
  useEffect(() => {
    // 設(shè)置延遲更新的效果
    const timeoutId = setTimeout(() => {
      setDelayedValue(inputValue);
    }, 500); // 輸入后 500ms 更新 delayedValue
    // 清理函數(shù):在輸入值變化時清除上一個 timeout
    return () => clearTimeout(timeoutId);
  }, [inputValue]); // 依賴于 inputValue,每次輸入值變化時都會觸發(fā)
  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="Type something..."
      />
      <p>Delayed value: {delayedValue}</p>
    </div>
  );
}
export default InputWithDelay;

useLayoutEffect 的常見場景:

DOM 操作:需要在頁面渲染之前操作 DOM(比如滾動條位置、修改樣式、元素大小調(diào)整等)。

獲取布局信息:例如測量 DOM 元素的寬度、高度或位置,因為這些信息可能在瀏覽器繪制過程中發(fā)生變化,所以你必須在渲染之前獲取。

修復布局閃爍:如果你需要在頁面渲染之前進行 DOM 操作,否則會導致閃爍或視覺不一致。

例如: 使用 useLayoutEffect 獲取 DOM 元素尺寸:

import React, { useState, useLayoutEffect, useRef } from 'react';
function Component() {
  const [size, setSize] = useState({ width: 0, height: 0 });
  const divRef = useRef(null);
  useLayoutEffect(() => {
    const div = divRef.current;
    if (div) {
      const { width, height } = div.getBoundingClientRect();
      setSize({ width, height });
    }
  }, []); // 只在掛載時執(zhí)行
  return (
    <div ref={divRef}>
      Width: {size.width}, Height: {size.height}
    </div>
  );
}

使用 useLayoutEffect 來確保在瀏覽器繪制頁面之前,能獲取到最新的 DOM 元素的尺寸。

性能對比

  • useEffect 的性能優(yōu)勢:由于是異步執(zhí)行,它不會阻塞瀏覽器的渲染過程。即使副作用中有較重的操作(如網(wǎng)絡請求、設(shè)置定時器等),它們也會在瀏覽器渲染完成后執(zhí)行,不會影響頁面渲染速度。
  • useLayoutEffect 的性能成本:由于它是同步執(zhí)行,并且會阻塞瀏覽器繪制,可能會導致頁面渲染的延遲,特別是在副作用操作比較復雜時(比如大量的 DOM 計算)。如果在 useLayoutEffect 中執(zhí)行了復雜的邏輯,它可能會影響頁面的響應速度,給用戶帶來不流暢的體驗。

到此這篇關(guān)于React useEffect、useLayoutEffect底層機制的文章就介紹到這了,更多相關(guān)React useEffect、useLayoutEffect內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解在React里使用

    詳解在React里使用"Vuex"

    本篇文章主要介紹了詳解在React里使用"Vuex",小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • React Form組件的實現(xiàn)封裝雜談

    React Form組件的實現(xiàn)封裝雜談

    這篇文章主要介紹了React Form組件的實現(xiàn)封裝雜談,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • React根據(jù)當前頁面路由進行自動高亮示例代碼

    React根據(jù)當前頁面路由進行自動高亮示例代碼

    要根據(jù)當前頁面路由自動高亮頂部菜單項,可以使用 React Router 的 useLocation 鉤子來獲取當前路徑,并根據(jù)路徑動態(tài)設(shè)置菜單項的高亮效果,本文給大家介紹了一個完整的示例,展示如何根據(jù)當前頁面路由自動高亮頂部菜單項,需要的朋友可以參考下
    2024-07-07
  • React項目使用ES6解決方案及JSX使用示例詳解

    React項目使用ES6解決方案及JSX使用示例詳解

    這篇文章主要為大家介紹了React項目使用ES6解決方案及JSX使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • React組件的創(chuàng)建與state同步異步詳解

    React組件的創(chuàng)建與state同步異步詳解

    這篇文章主要介紹了react組件實例屬性state,有狀態(tài)state的組件稱作復雜組件,沒有狀態(tài)的組件稱為簡單組件,狀態(tài)里存儲數(shù)據(jù),數(shù)據(jù)的改變驅(qū)動頁面的展示,本文結(jié)合實例代碼給大家詳細講解,需要的朋友可以參考下
    2023-03-03
  • React Native按鈕Touchable系列組件使用教程示例

    React Native按鈕Touchable系列組件使用教程示例

    這篇文章主要為大家介紹了React Native按鈕Touchable系列組件使用教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • React?Hook實現(xiàn)對話框組件

    React?Hook實現(xiàn)對話框組件

    這篇文章主要為大家詳細介紹了React?Hook實現(xiàn)對話框組件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 在React項目中使用TypeScript詳情

    在React項目中使用TypeScript詳情

    這篇文章主要介紹了在React項目中使用TypeScript詳情,文章通過圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • React Native 集成jpush-react-native的示例代碼

    React Native 集成jpush-react-native的示例代碼

    這篇文章主要介紹了React Native 集成jpush-react-native的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • React高階組件使用教程詳解

    React高階組件使用教程詳解

    高階組件就是接受一個組件作為參數(shù)并返回一個新組件(功能增強的組件)的函數(shù)。這里需要注意高階組件是一個函數(shù),并不是組件,這一點一定要注意,本文給大家分享React 高階組件HOC使用小結(jié),一起看看吧
    2022-12-12

最新評論