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

React 合成事件的實(shí)現(xiàn)

 更新時(shí)間:2026年02月28日 08:21:30   作者:二二四一  
本文主要介紹了React 合成事件的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

?? 什么是合成事件?

合成事件(SyntheticEvent) 是React模擬原生DOM事件所有能力的一個(gè)事件對(duì)象,即瀏覽器原生事件的跨瀏覽器包裝器。它根據(jù)W3C規(guī)范定義,兼容所有瀏覽器,擁有與瀏覽器原生事件相同的接口。

// React中的事件使用
function Button() {
  const handleClick = (e) => {
    console.log(e) // 這是合成事件對(duì)象,不是原生事件
    console.log(e.nativeEvent) // 通過nativeEvent獲取原生事件
  }
  
  return <button onClick={handleClick}>點(diǎn)擊我</button>
}

??為什么需要合成事件?

React設(shè)計(jì)合成事件主要有三個(gè)目的:

  1. 跨瀏覽器兼容:抹平不同瀏覽器事件對(duì)象的差異,提供一致的API
  2. 性能優(yōu)化:通過事件委托機(jī)制,減少內(nèi)存消耗
  3. 統(tǒng)一管理:方便事件的事務(wù)機(jī)制和優(yōu)先級(jí)調(diào)度

研究表明,在大型列表中,事件委托可以減少90%以上的事件綁定,顯著提升性能。

??? 合成事件的核心原理

1?? 事件委托

React并不是將事件綁定到具體的DOM元素上,而是在頂層統(tǒng)一監(jiān)聽。

版本差異

  • React 16及之前:事件綁定在document
  • React 17+:事件綁定在root容器上(id="root"的DOM元素)
// React 17+ 的事件綁定位置
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
// 所有事件都委托在root元素上

為什么改到root? 這有利于多個(gè)React版本共存,避免微前端等場(chǎng)景的沖突。

2?? 事件注冊(cè)流程

React事件系統(tǒng)的核心架構(gòu)分為三個(gè)層次:

// 簡化版的事件注冊(cè)機(jī)制
// 1. 事件注冊(cè):registerEvents
// 2. 事件監(jiān)聽:listenToAllSupportedEvents
// 3. 事件合成:SyntheticBaseEvent
// 4. 事件派發(fā):dispatchEvent

事件注冊(cè)源碼簡化版

// 注冊(cè)不同類型的事件
registerSimpleEvents();   // 注冊(cè)click、keyup等基礎(chǔ)事件
registerEvents$2();       // 注冊(cè)onMouseEnter等單階段事件
registerEvents$1();       // 注冊(cè)onChange相關(guān)事件
registerEvents$3();       // 注冊(cè)onSelect相關(guān)事件
registerEvents();         // 注冊(cè)onBeforeInput等事件

3?? 事件存儲(chǔ)與分發(fā)

React內(nèi)部維護(hù)了一個(gè)事件插件系統(tǒng),采用模塊化設(shè)計(jì),每個(gè)插件負(fù)責(zé)特定類型的事件處理。

// 簡化版的事件分發(fā)邏輯
function dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
  // 找到觸發(fā)事件的DOM元素對(duì)應(yīng)的fiber節(jié)點(diǎn)
  const target = nativeEvent.target
  const targetInst = getClosestInstanceFromNode(target)
  
  // 創(chuàng)建合成事件
  const events = extractEvents(
    domEventName,
    targetInst,
    nativeEvent,
    target
  )
  
  // 按階段分發(fā)事件
  events.forEach(event => {
    runEventsInBatch(event)
  })
}

?? 合成事件 vs 原生事件

核心區(qū)別對(duì)比表

對(duì)比維度原生事件React合成事件
事件名稱純小寫(onclick, onblur)小駝峰(onClick, onBlur)
處理函數(shù)字符串函數(shù)
阻止默認(rèn)行為返回false必須顯式調(diào)用preventDefault()
綁定方式addEventListenerJSX屬性
內(nèi)存消耗每個(gè)元素獨(dú)立綁定事件委托,統(tǒng)一管理
執(zhí)行順序直接在目標(biāo)元素觸發(fā)冒泡到頂層后統(tǒng)一處理

執(zhí)行順序演示

class EventOrderDemo extends React.Component {
  componentDidMount() {
    // 原生事件監(jiān)聽
    this.refs.button.addEventListener('click', () => {
      console.log('1. 原生事件:子元素')
    })
    
    document.addEventListener('click', () => {
      console.log('4. 原生事件:document')
    })
  }
  
  handleParentClick = () => {
    console.log('3. React事件:父元素')
  }
  
  handleChildClick = () => {
    console.log('2. React事件:子元素')
  }
  
  render() {
    return (
      <div onClick={this.handleParentClick} ref="parent">
        <button onClick={this.handleChildClick} ref="button">
          點(diǎn)擊我
        </button>
      </div>
    )
  }
}

// 輸出順序:
// 1. 原生事件:子元素
// 2. React事件:子元素
// 3. React事件:父元素
// 4. 原生事件:document

關(guān)鍵結(jié)論:原生事件先執(zhí)行,然后執(zhí)行React事件,最后執(zhí)行document上的原生事件。

??‍♂? 事件池機(jī)制(??????)

React 16及之前的事件池

在React 16及更早版本中,React使用事件池來管理合成事件對(duì)象。

// React 16 示例
function handleClick(e) {
  console.log(e.target) // 正常輸出
  
  setTimeout(() => {
    console.log(e.target) // ? null!事件對(duì)象已被回收
  }, 100)
}

// 解決方案:使用e.persist()
function handleClickCorrect(e) {
  e.persist() // 從事件池中移除,保留屬性
  
  setTimeout(() => {
    console.log(e.target) // ? 正常輸出
  }, 100)
}

事件池的工作原理

  • 事件對(duì)象會(huì)被重用,避免頻繁創(chuàng)建銷毀
  • 事件處理函數(shù)執(zhí)行完后,所有屬性會(huì)被置為null
  • 默認(rèn)池大小為10個(gè)對(duì)象

React 17+ 的變更

重要:React 17 開始,Web端不再使用事件池!

// React 17+,不需要e.persist()
function handleClick(e) {
  setTimeout(() => {
    console.log(e.target) // ? 正常輸出,事件池已移除
  }, 100)
}

官方解釋:現(xiàn)代瀏覽器性能已經(jīng)足夠好,事件池優(yōu)化帶來的收益不及復(fù)雜性成本。

?? 合成事件對(duì)象屬性

合成事件對(duì)象提供了豐富的屬性和方法:

function EventPropertiesDemo() {
  const handleEvent = (e) => {
    // 基礎(chǔ)屬性
    console.log(e.type)           // 事件類型:click
    console.log(e.target)         // 觸發(fā)事件的DOM元素
    console.log(e.currentTarget)  // 當(dāng)前處理事件的DOM元素
    console.log(e.nativeEvent)    // 原生事件對(duì)象
    
    // 事件方法
    e.preventDefault()   // 阻止默認(rèn)行為
    e.stopPropagation()  // 阻止冒泡
    
    // 狀態(tài)查詢
    console.log(e.isDefaultPrevented())  // 是否已阻止默認(rèn)行為
    console.log(e.isPropagationStopped()) // 是否已阻止冒泡
    
    // 其他屬性
    console.log(e.bubbles)     // 是否可冒泡
    console.log(e.cancelable)  // 是否可取消
    console.log(e.timeStamp)   // 事件觸發(fā)時(shí)間戳
  }
  
  return <button onClick={handleEvent}>測(cè)試事件</button>
}

? 性能優(yōu)化最佳實(shí)踐

1?? 使用事件委托

// ? 不推薦:為每個(gè)列表項(xiàng)綁定事件
function BadList({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id} onClick={() => handleItem(item)}>
          {item.name}
        </li>
      ))}
    </ul>
  )
}

// ? 推薦:使用事件委托
function GoodList({ items }) {
  const handleListClick = (e) => {
    const target = e.target
    if (target.tagName === 'LI') {
      const id = target.dataset.id
      console.log('點(diǎn)擊了項(xiàng)目:', id)
    }
  }
  
  return (
    <ul onClick={handleListClick}>
      {items.map(item => (
        <li key={item.id} data-id={item.id}>
          {item.name}
        </li>
      ))}
    </ul>
  )
}

2?? 避免混用原生事件和合成事件

// ? 危險(xiǎn):混用可能導(dǎo)致事件不執(zhí)行
function BadMixing() {
  useEffect(() => {
    document.addEventListener('click', (e) => {
      e.stopPropagation() // 阻止了冒泡,React事件可能收不到
    })
  }, [])
  
  return <button onClick={() => console.log('不會(huì)執(zhí)行')}>點(diǎn)擊</button>
}

// ? 建議:統(tǒng)一使用React事件
function GoodPractice() {
  return <button onClick={() => console.log('正常執(zhí)行')}>點(diǎn)擊</button>
}

3?? 合理使用preventDefault和stopPropagation

function FormDemo() {
  const handleSubmit = (e) => {
    // ? 阻止表單提交的默認(rèn)行為
    e.preventDefault()
    
    // 處理表單邏輯
    submitForm()
  }
  
  const handleButtonClick = (e) => {
    // 只在必要時(shí)阻止冒泡
    if (shouldStopPropagation) {
      e.stopPropagation()
    }
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <button onClick={handleButtonClick}>提交</button>
    </form>
  )
}

?? 難點(diǎn)解析

Q1:React合成事件和原生事件的區(qū)別?

滿分回答思路

  1. 定義區(qū)別:合成事件是React的跨瀏覽器包裝器,原生事件是瀏覽器原生實(shí)現(xiàn)
  2. 命名方式:合成事件小駝峰(onClick),原生事件全小寫(onclick)
  3. 處理函數(shù):合成事件傳函數(shù),原生事件傳字符串
  4. 阻止默認(rèn):合成事件必須用preventDefault(),原生可return false
  5. 綁定機(jī)制:合成事件用事件委托統(tǒng)一管理,原生事件直接綁定
  6. 內(nèi)存優(yōu)化:合成事件減少內(nèi)存消耗,原生事件綁定越多內(nèi)存消耗越大

Q2:合成事件的執(zhí)行順序是怎樣的?

觸發(fā)事件 → 原生事件(目標(biāo)元素)→ React事件(冒泡階段)→ document事件

關(guān)鍵點(diǎn):原生事件先執(zhí)行,如果原生事件阻止冒泡,React事件可能不會(huì)執(zhí)行(阻止合成事件不會(huì)影響原生事件)。

Q3:React 17對(duì)事件系統(tǒng)做了哪些改進(jìn)?

  1. 事件綁定位置:從document改為root容器
  2. 移除事件池:不再需要e.persist()
  3. onScroll冒泡:不再冒泡,匹配瀏覽器行為
  4. 優(yōu)化微前端:多個(gè)React版本可共存

Q4:如何在React事件中獲取異步訪問事件對(duì)象?

// React 16及以前:需要用e.persist()
function handleAsync(e) {
  e.persist()
  setTimeout(() => {
    console.log(e.target)
  }, 100)
}

// React 17+:直接使用即可
function handleAsync(e) {
  setTimeout(() => {
    console.log(e.target) // 沒問題
  }, 100)
}

?? 總結(jié):合成事件的核心價(jià)值

維度價(jià)值體現(xiàn)
兼容性抹平瀏覽器差異,提供一致API
性能事件委托減少90%+事件綁定
內(nèi)存事件池機(jī)制(16及以前)減少GC壓力
可維護(hù)性統(tǒng)一管理,自動(dòng)清理,避免內(nèi)存泄漏
開發(fā)體驗(yàn)聲明式API,符合W3C規(guī)范,上手簡單

一句話總結(jié):

React合成事件是一套基于事件委托、跨瀏覽器兼容、性能優(yōu)化的事件系統(tǒng),它通過頂層監(jiān)聽和統(tǒng)一分發(fā),為開發(fā)者提供了穩(wěn)定高效的事件處理機(jī)制。

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

相關(guān)文章

  • 前端面試題必會(huì)之前端react面試題

    前端面試題必會(huì)之前端react面試題

    在前端面試過程中經(jīng)常會(huì)問到一些面試題,今天小編抽空給大家講解前端面試題之必會(huì)react面試題,需要的朋友可以參考下
    2023-03-03
  • React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂播放時(shí)間滾動(dòng))

    React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂播放時(shí)間滾動(dòng))

    這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)歌詞滾動(dòng)效果(跟隨音樂播放使勁按滾動(dòng)),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2024-02-02
  • 解決react-connect中使用forwardRef遇到的問題

    解決react-connect中使用forwardRef遇到的問題

    這篇文章主要介紹了解決react-connect中使用forwardRef遇到的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 使用React hook實(shí)現(xiàn)remember me功能

    使用React hook實(shí)現(xiàn)remember me功能

    相信大家在使用 React 寫頁面的時(shí)候都遇到過完成 Remember me 的需求吧!本文就將這個(gè)需求封裝在一個(gè) React hook 中以供后續(xù)的使用,覺得有用的同學(xué)可以收藏起來以備不時(shí)之需,感興趣的小伙伴跟著小編一起來看看吧
    2024-04-04
  • React?模式之純組件使用示例詳解

    React?模式之純組件使用示例詳解

    這篇文章主要為大家介紹了React?模式之純組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • React中useState的理解和使用案例

    React中useState的理解和使用案例

    Hook是React16.8的新增特性,它可以讓你在不編寫class的情況下使用state以及其他的React特性,本文中講解的useState就是React中的其中一個(gè)Hook,這篇文章主要給大家介紹了關(guān)于React中useState理解和使用的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • 全棧輕量級(jí)搭配之Remix Prisma Sqlite使用分析

    全棧輕量級(jí)搭配之Remix Prisma Sqlite使用分析

    這篇文章主要為大家介紹了全棧輕量級(jí)搭配之Remix Prisma Sqlite使用示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • react native 仿微信聊天室實(shí)例代碼

    react native 仿微信聊天室實(shí)例代碼

    這篇文章主要介紹了react native 仿微信聊天室實(shí)例代碼,代碼簡單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • React 對(duì)接流式接口的具體使用

    React 對(duì)接流式接口的具體使用

    React應(yīng)用中對(duì)接流式接口通常涉及到處理實(shí)時(shí)數(shù)據(jù)傳輸,本文就來介紹一下React 對(duì)接流式接口的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-04-04
  • React循環(huán)遍歷渲染數(shù)組和對(duì)象元素方式

    React循環(huán)遍歷渲染數(shù)組和對(duì)象元素方式

    這篇文章主要介紹了React循環(huán)遍歷渲染數(shù)組和對(duì)象元素方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評(píng)論

米易县| 永福县| 资溪县| 隆德县| 金乡县| 永靖县| 偏关县| 密云县| 额尔古纳市| 青河县| 安宁市| 辽中县| 鄯善县| 灵璧县| 蓬安县| 陈巴尔虎旗| 武川县| 景谷| 岱山县| 崇州市| 同德县| 海城市| 灌云县| 邯郸市| 吴桥县| 赤水市| 酒泉市| 辰溪县| 山阴县| 和田县| 金华市| 嘉义市| 无为县| 卫辉市| 白朗县| 永济市| 余江县| 化德县| 迁西县| 虹口区| 定结县|