最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React合成事件原理及實(shí)現(xiàn)(React18和React16)

 更新時(shí)間:2025年02月27日 09:37:08   作者:不叫貓先生  
本文主要介紹了React合成事件原理及實(shí)現(xiàn),包含React18和React16兩種版本,具有一定的參考價(jià)值,感興趣的可以了解一下

React中的合成事件絕對(duì)不是給當(dāng)前元素基于addEventListener單獨(dú)做的事件綁定,而是基于事件委托處理的!

  • 在React17及以后版本,都是委托給#root這個(gè)容器「捕獲和冒泡都做了委托」;

  • 在React17以前的版本,都是為委托給document容器的「且只做了冒泡階段的委托」;

  • 對(duì)于沒有實(shí)現(xiàn)事件傳播機(jī)制的事件,才是單獨(dú)做的事件綁定「例如:onMouseEnter/onMouseLeave…」

    在組件渲染的時(shí)候,如果發(fā)現(xiàn)JSX元素屬性中有 onXxx/onXxxCapture 這樣的屬性,不會(huì)給當(dāng)前元素直接做事件綁定,只是把綁定的方法賦值給元素的相關(guān)屬性!
    例如:(onClick不是dom事件綁定,onclick才是事件綁定)

 outer.onClick=() => {console.log('outer 冒泡「合成」');}  //這不是DOM事件綁定「這樣的才是 outer.onclick」
 outer.onClickCapture=() => {console.log('outer 捕獲「合成」');}
 inner.onClick=() => {console.log('inner 冒泡「合成」');}
 inner.onClickCapture=() => {console.log('inner 捕獲「合成」');}

然后對(duì)#root這個(gè)容器做了事件綁定「捕獲和冒泡都做了」

組件中所渲染的內(nèi)容,最后都會(huì)插入到#root容器中,這樣點(diǎn)擊頁(yè)面中任何一個(gè)元素,最后都會(huì)把#root的點(diǎn)擊行為觸發(fā)。而在給#root綁定的方法中,把之前給元素設(shè)置的onXxx/onXxxCapture屬性,在相應(yīng)的階段執(zhí)行!!

React 18版本

React 合成事件事件傳播

 render() {
        return <div className="outer"
            onClick={() => {
                console.log('outer 冒泡「合成」');
            }}
            onClickCapture={() => {
                console.log('outer 捕獲「合成」');
            }}>

            <div className="inner"
                onClick={(ev) => {
                    // ev:合成事件對(duì)象
                    console.log('inner 冒泡「合成」', ev, ev.type);
                }}
                onClickCapture={() => {
                    console.log('inner 捕獲「合成」');
                }} 
            ></div>
        </div>;
    }

點(diǎn)擊inner元素之后

在這里插入圖片描述

在componentDidMount周期中,給元素綁定事件,可以看到此時(shí)已經(jīng)“亂序”

 componentDidMount() {
        document.addEventListener('click', () => {
            console.log('document 捕獲');
        }, true);
        document.addEventListener('click', () => {
            console.log('document 冒泡');
        }, false);

        document.body.addEventListener('click', () => {
            console.log('body 捕獲');
        }, true);
        document.body.addEventListener('click', () => {
            console.log('body 冒泡');
        }, false);   
        let root = document.querySelector('#root');
        root.addEventListener('click', () => {
            console.log('root 捕獲');
        }, true);
        root.addEventListener('click', () => {
            console.log('root 冒泡');
        }, false); 
    }

在這里插入圖片描述

接著我們對(duì)inner、outer元素做事件綁定

  componentDidMount() {
        document.addEventListener('click', () => {
            console.log('document 捕獲');
        }, true);
        document.addEventListener('click', () => {
            console.log('document 冒泡');
        }, false);

        document.body.addEventListener('click', () => {
            console.log('body 捕獲');
        }, true);
        document.body.addEventListener('click', () => {
            console.log('body 冒泡');
        }, false);   

        let root = document.querySelector('#root');
        root.addEventListener('click', () => {
            console.log('root 捕獲');
        }, true);
        root.addEventListener('click', () => {
            console.log('root 冒泡');
        }, false);

        let outer = document.querySelector('.outer');
        outer.addEventListener('click', () => {
            console.log('outer 捕獲「原生」');
        }, true);
        outer.addEventListener('click', () => {
            console.log('outer 冒泡「原生」');
        }, false);

        let inner = document.querySelector('.inner');
        inner.addEventListener('click', () => {
            console.log('inner 捕獲「原生」');
        }, true);
        inner.addEventListener('click', (ev) => {
            // ev:原生事件對(duì)象
            // ev.stopPropagation();
            console.log('inner 冒泡「原生」');
        }, false);
    }

在這里插入圖片描述

React 合成事件原理

<script>
        const root = document.querySelector('#root'),
            outer = document.querySelector('#outer'),
            inner = document.querySelector('#inner');
        // 經(jīng)過(guò)視圖渲染解析,outer/inner上都有onXxx/onXxxCapture這樣的屬性
        /* <div className="outer"
            onClick={() => { console.log('outer 冒泡「合成」'); }}
            onClickCapture={() => { console.log('outer 捕獲「合成」'); }}>
            <div className="inner"
                onClick={() => { console.log('inner 冒泡「合成」'); }}
                onClickCapture={() => { console.log('inner 捕獲「合成」'); }}
            ></div>
        </div>; */
        outer.onClick = () => { console.log('outer 冒泡「合成」'); }
        outer.onClickCapture = () => { console.log('outer 捕獲「合成」'); }
        inner.onClick = () => { console.log('inner 冒泡「合成」'); }
        inner.onClickCapture = () => { console.log('inner 捕獲「合成」'); }

        // 給#root做事件綁定
        root.addEventListener('click', (ev) => {
            let path = ev.path; // path:[事件源->....->window] 所有祖先元素
            [...path].reverse().forEach(ele => {
                let handle = ele.onClickCapture;
                if (handle) handle();
            });
        }, true);
        root.addEventListener('click', (ev) => {
            let path = ev.path;
            path.forEach(ele => {
                let handle = ele.onClick;
                if (handle) handle();
            });
        }, false);
    </script>

1、在視圖渲染時(shí),遇到合成事件,并沒有給元素做事件綁定,而是給元素設(shè)置相對(duì)應(yīng)的屬性,即合成屬性「 onXxx/onXxxCapture 

 <div className="outer"
            onClick={() => { console.log('outer 冒泡「合成」'); }}
            onClickCapture={() => { console.log('outer 捕獲「合成」'); }}>
            <div className="inner"
                onClick={() => { console.log('inner 冒泡「合成」'); }}
                onClickCapture={() => { console.log('inner 捕獲「合成」'); }}
            ></div>
        </div>; 

2、給#root做事件綁定,包括冒泡、捕獲,#root 上綁定的方法執(zhí)行,把所有規(guī)劃的路徑中,有合成事件屬性都執(zhí)行。
其中ev是原生事件對(duì)象

// 給#root做事件綁定
//捕獲階段 方法A
        root.addEventListener('click', (ev) => {
            let path = ev.composedPath(); // path:[事件源->....->window] 所有祖先元素
            [...path].reverse().forEach(ele => {
                let handle = ele.onClickCapture;
                if (handle) handle();
            });
        }, true);
//冒泡階段 方法B
        root.addEventListener('click', (ev) => {
            let path = ev.composedPath();
            path.forEach(ele => {
                let handle = ele.onClick;
                if (handle) handle();
            });
        }, false);

方法A,打印出path,事件源——>window

在這里插入圖片描述

方法B,打印出path,事件源——>window

在這里插入圖片描述

其中handle方法,具體的處理邏輯

const handleEv = function(ev){
    //ev 原生事件對(duì)象
     ......
    //返回  合成事件對(duì)象
}
//調(diào)用的時(shí)候
handleEv(ev)

其中在執(zhí)行綁定的合成事件handle()時(shí)

  • 如果不經(jīng)過(guò)處理,方法中的thisundefined
  • 如果綁定的方法是箭頭函數(shù),則找上級(jí)上下文中的this
  • 在執(zhí)行這些方法之前,會(huì)把原生對(duì)象ev做特殊處理,返回合成事件,傳遞給函數(shù)

在這里插入圖片描述

點(diǎn)擊inner元素的時(shí)候,按照原生的事件傳播機(jī)制

捕獲階段:

window捕獲、
document捕獲、
html捕獲
body捕獲
root捕獲 ->執(zhí)行:方法A
- window.onClickCapture (無(wú))
- document.onClickCapture(無(wú))
- html.onClickCapture(無(wú))
- body.onClickCapture(無(wú))
- root.onClickCapture(無(wú))
- outer.onClickCapture => outer 捕獲「合成」
- inner.onClickCapture => inner 捕獲「合成」
outer 捕獲
innner 捕獲

冒泡階段:inner冒泡
outer冒泡
root冒泡->執(zhí)行:方法B
- inner.onClick = > inner 冒泡「合成」
- outer.onClick = > inner 冒泡「合成」
- body.onClick
- html.onClick
- document.onClick
- window.onClick

可以用圖來(lái)表示:

在這里插入圖片描述

React 合成事件中阻止事件傳播的影響

<div className="inner"
                onClick={(ev) => {
                    // ev:合成事件對(duì)象
                    console.log('inner 冒泡「合成」', ev, ev.type);
                    // ev.stopPropagation(); //合成事件對(duì)象中的“阻止事件傳播”:阻止原生的事件傳播 & 阻止合成事件中的事件傳播
                    // ev.nativeEvent.stopPropagation(); //原生事件對(duì)象中的“阻止事件傳播”:只能阻止原生事件的傳播
                    // ev.nativeEvent.stopImmediatePropagation(); //原生事件對(duì)象的阻止事件傳播,只不過(guò)可以阻止#root上其它綁定的方法執(zhí)行

                    /* setTimeout(() => {
                        console.log(ev, ev.type); //React18中并沒有事件對(duì)象池機(jī)制,所以也不存在:創(chuàng)建的事件對(duì)象信息清空問(wèn)題!!
                    }, 500); */ 
                }}
                onClickCapture={() => {
                    console.log('inner 捕獲「合成」');
                }}

合成事件stopPropagation

首先 ev.stopPropagation(),合成事件對(duì)象中的“阻止事件傳播”:阻止原生的事件傳播& 阻止合成事件中的事件傳播。

在這里插入圖片描述

合成事件nativeEvent.stopPropagation

ev.nativeEvent.stopPropagation:原生事件對(duì)象中的“阻止事件傳播”:只能阻止原生事件的傳播。

在這里插入圖片描述

合成事件nativeEvent.stopImmediatePropagation

ev.nativeEvent.stopImmediatePropagation :原生事件對(duì)象的阻止事件傳播,只不過(guò)可以阻止#root上其他綁定的方法執(zhí)行,此時(shí)root冒泡就沒了

在這里插入圖片描述

原生stopPropagation

inner.addEventListener('click', (ev) => {
            // ev:原生事件對(duì)象
            ev.stopPropagation();
            console.log('inner 冒泡「原生」');
        }, false);

在這里插入圖片描述

React 16版本

合成事件原理

16版本中,合成事件的處理機(jī)制不再把事件委托給root元素,而是委托給document元素;并且只做了冒泡階段的委托;在委托的方法中,把onXxx/onXxxCapture合成事件屬性執(zhí)行。

在這里插入圖片描述

點(diǎn)擊inner元素之后,打印結(jié)果

在這里插入圖片描述

1、對(duì)視圖進(jìn)行解析
給元素添加合成事件元素,并不是直接做事件綁定

outer.onClick = ()=>{...}
outer.onClickCapture = ()=>{...}
inner.onClick = ()=>{...}
innner.onClickCapture = ()=>{...}

2、對(duì)document的冒泡階段做了事件委托

document.addEventListener("click",(ev)=>{
    //ev:原生事件對(duì)象
    let path = ev.composedPath();//傳播路徑:[事件源-> window];
    let syntheticEv=處理事件對(duì)象(ev)
    //把捕獲階段的合成事件執(zhí)行
    [...path].reverse().forEach(ele=>{
           let handle = ele.onClickCapture;
           if(handle) handle(syntheticEv)
    })
    //把冒泡階段的合成事件執(zhí)行
    path.forEach(ele=>{
           let handle = ele.onClick;
           if(handle) handle(syntheticEv)
      }) 
})

在這里插入圖片描述

合成事件中阻止事件傳播的影響

<div className="inner"
                onClick={(ev) => {
                    // ev:合成事件對(duì)象
                    console.log('inner 冒泡「合成」', ev, ev.type);
                    // ev.stopPropagation(); //合成事件對(duì)象中的“阻止事件傳播”:阻止原生的事件傳播 & 阻止合成事件中的事件傳播
                    // ev.nativeEvent.stopPropagation(); //原生事件對(duì)象中的“阻止事件傳播”:只能阻止原生事件的傳播
                    // ev.nativeEvent.stopImmediatePropagation(); //原生事件對(duì)象的阻止事件傳播,只不過(guò)可以阻止#root上其它綁定的方法執(zhí)行

                    /* setTimeout(() => {
                        console.log(ev, ev.type); //React18中并沒有事件對(duì)象池機(jī)制,所以也不存在:創(chuàng)建的事件對(duì)象信息清空問(wèn)題??!
                    }, 500); */ 
                }}
                onClickCapture={() => {
                    console.log('inner 捕獲「合成」');
                }}
 </div>               

合成事件stopPropagation()

在這里插入圖片描述

合成事件nativeEvent.stopPropagation()

在這里插入圖片描述

合成事件nativeEvent.stopImmediatePropagation()

在這里插入圖片描述

原生事件stopPropagation

inner.addEventListener('click', (ev) => {
            // ev:原生事件對(duì)象
            ev.stopPropagation();
            console.log('inner 冒泡「原生」');
        }, false);

在這里插入圖片描述

事件對(duì)象池

React 16中,關(guān)于合成事件對(duì)象的處理,React內(nèi)部是基于“事件對(duì)象池”,做了一個(gè)緩存機(jī)制。

React 17及以后,是去掉了這套事件對(duì)象池和緩存機(jī)制

  • 當(dāng)每一次事件觸發(fā)的時(shí)候,如果傳播到了委托的元素上「document/#root」,在委托的方法中,首先會(huì)對(duì)內(nèi)置事件對(duì)象做統(tǒng)一處理,生成合成事件對(duì)象

React 16 版本中:

為了防止每一次都是重新創(chuàng)建出新的合成事件對(duì)象,它設(shè)置了一個(gè)事件對(duì)象池「緩存池」

  • 本次事件觸發(fā),獲取到事件操作的相關(guān)信息后,從 事件對(duì)象池 中獲取存儲(chǔ)的合成事件對(duì)象,把信息賦值給相關(guān)的成員
  • 等待本次操作結(jié)束,把合成事件對(duì)象中的成員信息都清空掉,再放入到「事件對(duì)象池」中

在這里插入圖片描述

<div className="inner"
                onClick={(ev) => {
                    // ev:合成事件對(duì)象
                    console.log('inner 冒泡「合成」', ev, ev.type);

                    setTimeout(() => {
                        console.log(ev, ev.type); //React18中并沒有事件對(duì)象池機(jī)制,所以也不存在:創(chuàng)建的事件對(duì)象信息清空問(wèn)題?。?
                    }, 500); 
                }}
                onClickCapture={() => {
                    console.log('inner 捕獲「合成」', ev, ev.type);
                }}
    ></div>

500ms之后,合成事件對(duì)象還在,但是里面的成員信息都被清空了

在這里插入圖片描述

在React 18 中,并沒有事件對(duì)象池機(jī)制,所以不存在創(chuàng)建的事件對(duì)象信息清空的問(wèn)題。但是React 合成事件中 ev.persist() 可以把合成事件對(duì)象中的信息保留下來(lái)。

到此這篇關(guān)于React合成事件原理及實(shí)現(xiàn)(React18和React16)的文章就介紹到這了,更多相關(guān)React合成事件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React Native功能豐富的Toast通知庫(kù)

    React Native功能豐富的Toast通知庫(kù)

    這篇文章主要為大家介紹了React Native功能豐富的Toast通知庫(kù)使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React使用高階組件與Hooks實(shí)現(xiàn)權(quán)限攔截教程詳細(xì)分析

    React使用高階組件與Hooks實(shí)現(xiàn)權(quán)限攔截教程詳細(xì)分析

    高階組件就是接受一個(gè)組件作為參數(shù)并返回一個(gè)新組件(功能增強(qiáng)的組件)的函數(shù)。這里需要注意高階組件是一個(gè)函數(shù),并不是組件,這一點(diǎn)一定要注意,本文給大家分享React高階組件使用小結(jié),一起看看吧
    2023-01-01
  • react?express實(shí)現(xiàn)webssh?demo解析

    react?express實(shí)現(xiàn)webssh?demo解析

    這篇文章主要為大家介紹了詳解react?express實(shí)現(xiàn)webssh?demo解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 使用react context 實(shí)現(xiàn)vue插槽slot功能

    使用react context 實(shí)現(xiàn)vue插槽slot功能

    這篇文章主要介紹了使用react context 實(shí)現(xiàn)vue插槽slot功能,文中給大家介紹了vue的slot的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2019-07-07
  • React useState 使用從基礎(chǔ)到高級(jí)應(yīng)用示例小結(jié)

    React useState 使用從基礎(chǔ)到高級(jí)應(yīng)用示例小結(jié)

    React 的 useState 是一個(gè)內(nèi)置的 Hook,它允許我們?cè)诤瘮?shù)組件中添加和管理狀態(tài),本文給大家介紹React useState 使用從基礎(chǔ)到高級(jí)應(yīng)用示例小結(jié),感興趣的朋友一起看看吧
    2025-09-09
  • Redis數(shù)據(jù)結(jié)構(gòu)面試高頻問(wèn)題解析

    Redis數(shù)據(jù)結(jié)構(gòu)面試高頻問(wèn)題解析

    這篇文章主要為大家介紹了Redis數(shù)據(jù)結(jié)構(gòu)高頻面試問(wèn)題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • react實(shí)現(xiàn)動(dòng)態(tài)表單

    react實(shí)現(xiàn)動(dòng)態(tài)表單

    這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)動(dòng)態(tài)表單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • React 腳手架使用指南(最新推薦)

    React 腳手架使用指南(最新推薦)

    本文介紹了React腳手架(CreateReactApp)的使用指南,包括創(chuàng)建項(xiàng)目、項(xiàng)目結(jié)構(gòu)、常用命令、最佳實(shí)踐、常見問(wèn)題解決以及腳手架的優(yōu)勢(shì),結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12
  • react配置webpack-bundle-analyzer項(xiàng)目?jī)?yōu)化踩坑記錄

    react配置webpack-bundle-analyzer項(xiàng)目?jī)?yōu)化踩坑記錄

    這篇文章主要介紹了react配置webpack-bundle-analyzer項(xiàng)目?jī)?yōu)化踩坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解

    Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解

    今天是使用?Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能,前端我們使用?Reactjs?+?Axios?來(lái)搭建前端上傳文件應(yīng)用,后端我們使用?Node.js?+?Express?+?Multer?+?Mongodb?來(lái)搭建后端上傳文件處理應(yīng)用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-06-06

最新評(píng)論

甘谷县| 赣州市| 开阳县| 陆河县| 江门市| 富民县| 云南省| 芒康县| 阳江市| 茶陵县| 宝丰县| 二手房| 铅山县| 温泉县| 遂昌县| 武冈市| 麻栗坡县| 西安市| 闵行区| 乡城县| 乌什县| 达拉特旗| 中牟县| 县级市| 金门县| 确山县| 延津县| 玉田县| 安福县| 海盐县| 儋州市| 沧源| 陈巴尔虎旗| 万宁市| 交口县| 田东县| 桦甸市| 潜江市| 博罗县| 西畴县| 广南县|