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

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

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

useEffect

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

基本用法:

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

不設(shè)置依賴

useEffect(()=>{
  //獲取最新的狀態(tài)值
})
  • 第一次渲染完成后,執(zhí)行callback,等價(jià)于 componentDidMount
  • 在組件每一次更新完畢后,也會(huì)執(zhí)行callback,等價(jià)于 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(()=>{  },[])

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

初次渲染,打印@1 @2 ,點(diǎn)擊按鈕之后,只打印出@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è)置多個(gè)依賴

useEffect(() => {
   }, [依賴項(xiàng)1,依賴項(xiàng)2,依賴項(xiàng)3]);
  • 第一次渲染完畢會(huì)執(zhí)行callback
  • 依賴的狀態(tài)值(或者多個(gè)依賴狀態(tài)中的一個(gè))發(fā)生變化,也會(huì)出發(fā)callback執(zhí)行
  • 但是依賴的狀態(tài)如果沒有變化,在組件更新的時(shí)候,callback是不會(huì)執(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>;
};

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

useEffect(()=>{ return ()=>{ 
//獲取的是上一次狀態(tài)的值
//返回的函數(shù),會(huì)在組件釋放的時(shí)候執(zhí)行
} } )
  • 初始渲染之后返回一個(gè)小函數(shù),放到鏈表當(dāng)中
  • 如果組件更新,會(huì)通過updateEffect會(huì)把上一次返回的函數(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)等操作語句中。
下面是錯(cuò)誤的寫法:

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>;
};

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

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

useEffect 中發(fā)送請(qǐng)求

首先模擬一個(gè)請(qǐng)求

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

錯(cuò)誤示例

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

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)建一個(gè)函數(shù)

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

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

總結(jié)

useLayoutEffect

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

基本用法:

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

useLayoutEffect 和useEffect區(qū)別

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

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

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

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

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

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

4、瀏覽器渲染和繪制真實(shí)DOM對(duì)象

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

const Demo = function Demo() {
    // console.log('RENDER');
    let [num, setNum] = useState(0);
    useLayoutEffect(() => {
        console.log('useLayoutEffect'); //第一個(gè)輸出
    }, [num]);
    useEffect(() => {
        console.log('useEffect'); //第二個(gè)輸出
    }, [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í)行時(shí)機(jī):瀏覽器渲染的關(guān)系

useEffect:

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

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

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

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

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

useLayoutEffect:

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

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

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

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

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

對(duì)瀏覽器渲染的影響

useEffect 的影響:

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

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

性能優(yōu)化:因?yàn)槭钱惒綀?zhí)行的,所以瀏覽器渲染不會(huì)被卡住,頁面的響應(yīng)速度和流暢性得到保證。

useLayoutEffect 的影響:

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

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

影響布局計(jì)算:適合用于獲取 DOM 元素的大小、位置等布局信息,因?yàn)樗陧撁驿秩局皥?zhí)行,你可以確保你拿到的是最新的、正確的布局信息。

使用場(chǎng)景

useEffect 的常見場(chǎng)景:

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

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

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

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

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

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

在組件中監(jiān)聽 resize 或 scroll 事件時(shí),useEffect 是一個(gè)常見的選擇。你可以在 useEffect 中添加事件監(jiān)聽器,并在組件卸載時(shí)清除這些監(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)
    };
    // 在組件掛載時(shí)添加事件監(jiān)聽器
    window.addEventListener('resize', handleResize);
    // 返回清理函數(shù),在組件卸載時(shí)移除事件監(jiān)聽器
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 空數(shù)組,表示只在組件掛載和卸載時(shí)執(zhí)行
  return (
    <div>
      <p>Window width: {windowWidth}px</p>
    </div>
  );
}
export default WindowResize;

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

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

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

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ù):在輸入值變化時(shí)清除上一個(gè) timeout
    return () => clearTimeout(timeoutId);
  }, [inputValue]); // 依賴于 inputValue,每次輸入值變化時(shí)都會(huì)觸發(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 的常見場(chǎng)景:

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

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

修復(fù)布局閃爍:如果你需要在頁面渲染之前進(jìn)行 DOM 操作,否則會(huì)導(dǎo)致閃爍或視覺不一致。

例如: 使用 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 });
    }
  }, []); // 只在掛載時(shí)執(zhí)行
  return (
    <div ref={divRef}>
      Width: {size.width}, Height: {size.height}
    </div>
  );
}

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

性能對(duì)比

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

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

相關(guān)文章

  • 淺談React中組件邏輯復(fù)用的那些事兒

    淺談React中組件邏輯復(fù)用的那些事兒

    這篇文章主要介紹了淺談React中組件邏輯復(fù)用的那些事兒,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • React使用Canvas繪制大數(shù)據(jù)表格的實(shí)例代碼

    React使用Canvas繪制大數(shù)據(jù)表格的實(shí)例代碼

    之前一直想用Canvas做表格渲染的,最近發(fā)現(xiàn)了一個(gè)很不錯(cuò)的Canvas繪圖框架Leafer,api很友好就試著寫了一下,文中有詳細(xì)的代碼示例供大家參考,感興趣的小伙伴可以自己動(dòng)手試試
    2023-09-09
  • 新建的React Native就遇到vscode報(bào)警解除方法

    新建的React Native就遇到vscode報(bào)警解除方法

    這篇文章主要為大家介紹了新建的React Native就遇到vscode報(bào)警解除方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 對(duì)react中間件的理解

    對(duì)react中間件的理解

    這篇文章主要介紹了對(duì)react中間件的理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 詳解React 和 Redux的關(guān)系

    詳解React 和 Redux的關(guān)系

    這篇文章主要為大家介紹了React 和 Redux的關(guān)系,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11
  • React-Router6版本的更新引起的路由用法變化

    React-Router6版本的更新引起的路由用法變化

    本文主要介紹了React-Router6版本的更新引起的路由用法變化,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • react?Scheduler?實(shí)現(xiàn)示例教程

    react?Scheduler?實(shí)現(xiàn)示例教程

    這篇文章主要為大家介紹了react?Scheduler?實(shí)現(xiàn)示例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Redux中間件的使用方法教程

    Redux中間件的使用方法教程

    中間件就是一個(gè)函數(shù),對(duì)store.dispatch方法進(jìn)行了改造,在發(fā)出 Action 和執(zhí)行 Reducer 這兩步之間,添加了其他功能,要理解中間件,關(guān)鍵點(diǎn)是要知道,這個(gè)中間件是連接哪些部分的軟件,它在中間做了什么事,提供了什么服務(wù)
    2023-01-01
  • reset.css瀏覽器默認(rèn)樣式表重置(user?agent?stylesheet)的示例代碼

    reset.css瀏覽器默認(rèn)樣式表重置(user?agent?stylesheet)的示例代碼

    這篇文章主要介紹了reset.css瀏覽器默認(rèn)樣式表重置(user?agent?stylesheet),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-12-12
  • 學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫

    學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫

    這篇文章主要為大家介紹了學(xué)習(xí)ahooks useRequest并實(shí)現(xiàn)手寫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03

最新評(píng)論