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

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

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

?? 什么是合成事件?

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

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

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

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

  1. 跨瀏覽器兼容:抹平不同瀏覽器事件對象的差異,提供一致的API
  2. 性能優(yōu)化:通過事件委托機制,減少內(nèi)存消耗
  3. 統(tǒng)一管理:方便事件的事務(wù)機制和優(yōu)先級調(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? 這有利于多個React版本共存,避免微前端等場景的沖突。

2?? 事件注冊流程

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

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

事件注冊源碼簡化版

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

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

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

// 簡化版的事件分發(fā)邏輯
function dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
  // 找到觸發(fā)事件的DOM元素對應(yīng)的fiber節(jié)點
  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ū)別對比表

對比維度原生事件React合成事件
事件名稱純小寫(onclick, onblur)小駝峰(onClick, onBlur)
處理函數(shù)字符串函數(shù)
阻止默認行為返回false必須顯式調(diào)用preventDefault()
綁定方式addEventListenerJSX屬性
內(nèi)存消耗每個元素獨立綁定事件委托,統(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">
          點擊我
        </button>
      </div>
    )
  }
}

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

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

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

React 16及之前的事件池

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

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

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

事件池的工作原理

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

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ù)雜性成本。

?? 合成事件對象屬性

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

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)    // 原生事件對象
    
    // 事件方法
    e.preventDefault()   // 阻止默認行為
    e.stopPropagation()  // 阻止冒泡
    
    // 狀態(tài)查詢
    console.log(e.isDefaultPrevented())  // 是否已阻止默認行為
    console.log(e.isPropagationStopped()) // 是否已阻止冒泡
    
    // 其他屬性
    console.log(e.bubbles)     // 是否可冒泡
    console.log(e.cancelable)  // 是否可取消
    console.log(e.timeStamp)   // 事件觸發(fā)時間戳
  }
  
  return <button onClick={handleEvent}>測試事件</button>
}

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

1?? 使用事件委托

// ? 不推薦:為每個列表項綁定事件
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('點擊了項目:', id)
    }
  }
  
  return (
    <ul onClick={handleListClick}>
      {items.map(item => (
        <li key={item.id} data-id={item.id}>
          {item.name}
        </li>
      ))}
    </ul>
  )
}

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

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

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

3?? 合理使用preventDefault和stopPropagation

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

?? 難點解析

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

滿分回答思路

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

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

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

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

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

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

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

// 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é):合成事件的核心價值

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

一句話總結(jié):

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

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

相關(guān)文章

  • 前端面試題必會之前端react面試題

    前端面試題必會之前端react面試題

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

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

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

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

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

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

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

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

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

    React中useState的理解和使用案例

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

    全棧輕量級搭配之Remix Prisma Sqlite使用分析

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

    react native 仿微信聊天室實例代碼

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

    React 對接流式接口的具體使用

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

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

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

最新評論

寿宁县| 贵南县| 五峰| 兴文县| 明星| 微博| 昌吉市| 永济市| 石柱| 保亭| 台江县| 尖扎县| 洞口县| 政和县| 沂源县| 昌宁县| 娄底市| 平泉县| 渭南市| 永胜县| 休宁县| 墨玉县| 临清市| 山西省| 福贡县| 鱼台县| 柳河县| 通山县| 星子县| 保康县| 延川县| 牙克石市| 蓬安县| 来宾市| 丰原市| 茌平县| 乐都县| 凤台县| 淮阳县| 天水市| 巨鹿县|