Rumah > Artikel > hujung hadapan web > Mengapa bertindak balas menggunakan peristiwa sintetik?
React menggunakan peristiwa sintetik untuk tiga tujuan utama: 1. Untuk mencapai keserasian penyemak imbas dan mencapai platform silang yang lebih baik, peristiwa sintetik yang disediakan oleh React boleh digunakan untuk melancarkan perbezaan antara objek acara penyemak imbas yang berbeza dan menggabungkan yang berbeza Acara platform mensimulasikan peristiwa sintetik. 2. Elakkan pengumpulan sampah; objek acara React tidak akan dilepaskan, tetapi akan disimpan dalam tatasusunan Apabila acara dicetuskan, ia akan muncul keluar dari tatasusunan untuk mengelakkan penciptaan dan pemusnahan yang kerap (pengumpulan sampah). 3. Memudahkan pengurusan acara bersatu dan mekanisme transaksi.
Persekitaran pengendalian tutorial ini: sistem Windows 7, versi react18, komputer Dell G3.
1. Apakah peristiwa sintetik
Acara sintetik bertindak balas (SyntheticEvent) ialah objek acara yang menyerupai semua keupayaan acara DOM asli dalam React, iaitu pembalut silang penyemak imbas untuk acara penyemak imbas asli. Ia mentakrifkan peristiwa sintetik mengikut spesifikasi W3C, serasi dengan semua penyemak imbas, dan mempunyai antara muka yang sama seperti peristiwa asli penyemak imbas.
Dalam React, semua acara adalah sintetik dan bukan acara DOM asli, tetapi acara DOM boleh diperolehi melalui sifat e.nativeEvent. Contohnya:
const button = <button>react 按钮</button> const handleClick = (e) => console.log(e.nativeEvent); //原生事件对象
Apabila mempelajari ilmu baharu, anda mesti tahu mengapa teknologi ini muncul.
Jadi mengapa React menggunakan peristiwa sintetik? Ia mempunyai tiga tujuan utama:
Dayakan keserasian penyemak imbas dan mencapai platform merentas yang lebih baik
React menggunakan mekanisme proksi acara peringkat atas untuk memastikan pengambilan risiko Bubble ketekalan boleh dilaksanakan merentas pelayar. Peristiwa sintetik yang disediakan oleh React digunakan untuk melancarkan perbezaan antara objek acara penyemak imbas yang berbeza dan mensimulasikan peristiwa sintetik daripada acara platform yang berbeza.
Elakkan pengumpulan sampah
Objek acara mungkin kerap dicipta dan dikitar semula, jadi React memperkenalkan kumpulan acara untuk mendapatkan atau melepaskan objek acara dalam kumpulan acara. Iaitu, objek acara React tidak akan dikeluarkan, tetapi akan disimpan dalam tatasusunan Apabila acara dicetuskan, ia akan muncul keluar dari tatasusunan untuk mengelakkan penciptaan dan pemusnahan yang kerap (pengumpulan sampah).
Pengurusan acara bersatu yang mudah dan mekanisme transaksi
本文不介绍源码啦,对具体实现的源码有兴趣的朋友可以查阅:《React SyntheticEvent》 。 https://github.com/facebook/react/blob/75ab53b9e1de662121e68dabb010655943d28d11/packages/events/SyntheticEvent.js#L62
Semakan acara asli
Model acara JavaScript terbahagi terutamanya kepada tiga jenis: model acara asal (DOM0), model acara DOM2 dan model acara IE.
Model acara 1.DOM0
juga dipanggil model acara asal Dalam model ini, peristiwa tidak disebarkan, iaitu, tiada konsep aliran peristiwa . Fungsi pendengaran mengikat acara agak mudah, terdapat dua cara:
//HTML代码种直接绑定: <button></button> //通过JS代码指定属性值: var btn = document.getElementById('.test'); btn.onclick = fun; //移除监听函数: btn.onclick = null;
Kelebihan: Keserasian yang kukuh menyokong semua penyemak imbas
Kelemahan: Tiada pemisahan antara fungsi pendengaran dan paparan; peristiwa yang sama Hanya satu sahaja yang boleh diikat, dan acara yang didaftarkan kemudian daripada jenis yang sama akan menimpa acara yang didaftarkan sebelum ini.
Model acara 2.DOM2
Model standard yang ditubuhkan oleh W3C, yang disokong oleh penyemak imbas moden (pelayar selain IE6-8). Dalam model acara ini, terdapat tiga proses untuk acara:
Fasa menangkap acara (fasa menangkap). Peristiwa merambat dari dokumen sehingga ke elemen sasaran, dan menyemak sama ada nod yang berlalu terikat pada fungsi mendengar acara dan melaksanakannya jika ya.
Fasa pemprosesan acara (fasa sasaran). Apabila acara mencapai elemen sasaran, fungsi mendengar elemen sasaran akan dicetuskan.
Fasa menggelegak acara. Gelembung acara daripada elemen sasaran ke dokumen, dan menyemak sama ada nod yang berlalu terikat pada fungsi mendengar acara dan melaksanakannya jika ya.
//事件绑定监听函数的方式如下: addEventListener(eventType, handler, useCapture) //事件移除监听函数的方式如下: removeEventListener(eventType, handler, useCapture)
3. Model acara IE
Model acara IE mempunyai dua proses:
Fasa pemprosesan acara (fasa sasaran). Apabila acara mencapai elemen sasaran, fungsi mendengar elemen sasaran akan dicetuskan.
Fasa menggelegak acara. Gelembung acara daripada elemen sasaran ke dokumen, dan menyemak sama ada nod yang berlalu terikat pada fungsi mendengar acara dan melaksanakannya jika ya.
//事件绑定监听函数的方式如下: attachEvent(eventType, handler) //事件移除监听函数的方式如下: detachEvent(eventType, handler)
4. Aliran acara
Seperti yang ditunjukkan dalam rajah di atas, aliran peristiwa yang dipanggil merangkumi tiga peringkat : tangkapan acara, Peringkat sasaran dan acara menggelegak. Tangkapan acara adalah dari luar ke dalam, sepadan dengan tetingkap bertanda anak panah merah -> html... -> .
Tangkapan peristiwa
Apabila elemen mencetuskan peristiwa (seperti onclick), dokumen objek peringkat atas akan memancarkan aliran peristiwa dan sebagai nod bagi Pokok DOM bergerak ke arah Nod elemen sasaran mengalir sehingga ia mencapai elemen sasaran di mana peristiwa itu sebenarnya berlaku. Semasa proses ini, fungsi mendengar acara yang sepadan tidak akan dicetuskan.
Sasaran acara
Selepas mencapai elemen sasaran, laksanakan fungsi pemprosesan yang sepadan bagi acara elemen sasaran. Jika tiada fungsi mendengar terikat, ia tidak akan dilaksanakan.
Acara menggelegak
从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被触发一次。如果想阻止事件起泡,可以使用 e.stopPropagation()
或者 e.cancelBubble=true(IE)
来阻止事件的冒泡传播。
事件委托/事件代理
简单理解就是将一个响应事件委托到另一个元素。 当子节点被点击时,click 事件向上冒泡,父节点捕获到事件后,我们判断是否为所需的节点,然后进行处理。其优点在于减少内存消耗和动态绑定事件。
三、React合成事件原理
React合成事件的工作原理大致可以分为两个阶段:
事件绑定
事件触发
在React17之前,React是把事件委托在document上的,React17及以后版本不再把事件委托在document上,而是委托在挂载的容器上了,本文以16.x版本的React为例来探寻React的合成事件。当真实的dom触发事件时,此时构造React合成事件对象,按照冒泡或者捕获的路径去收集真正的事件处理函数,在此过程中会先处理原生事件,然后当冒泡到document对象后,再处理React事件。举个栗子:
import React from 'react'; import './App.less'; class Test extends React.Component { parentRef: React.RefObject<any>; childRef: React.RefObject<any>; constructor(props) { super(props); this.parentRef = React.createRef(); this.childRef = React.createRef(); } componentDidMount() { document.addEventListener( 'click', () => { console.log(`document原生事件捕获`); }, true, ); document.addEventListener('click', () => { console.log(`document原生事件冒泡`); }); this.parentRef.current.addEventListener( 'click', () => { console.log(`父元素原生事件捕获`); }, true, ); this.parentRef.current.addEventListener('click', () => { console.log(`父元素原生事件冒泡`); }); this.childRef.current.addEventListener( 'click', () => { console.log(`子元素原生事件捕获`); }, true, ); this.childRef.current.addEventListener('click', () => { console.log(`子元素原生事件冒泡`); }); } handleParentBubble = () => { console.log(`父元素React事件冒泡`); }; handleChildBubble = () => { console.log(`子元素React事件冒泡`); }; handleParentCapture = () => { console.log(`父元素React事件捕获`); }; handleChileCapture = () => { console.log(`子元素React事件捕获`); }; render() { return ( <p> </p> <p> 事件处理测试 </p> ); } } export default Test;</any></any>
上面案例打印的结果为:
注:React17中上述案例的执行会有所区别,会先执行所有捕获事件后,再执行所有冒泡事件。
1、事件绑定
通过上述案例,我们知道了React合成事件和原生事件执行的过程,两者其实是通过一个叫事件插件(EventPlugin)的模块产生关联的,每个插件只处理对应的合成事件,比如onClick事件对应SimpleEventPlugin插件,这样React在一开始会把这些插件加载进来,通过插件初始化一些全局对象,比如其中有一个对象是registrationNameDependencies,它定义了合成事件与原生事件的对应关系如下:
{ onClick: ['click'], onClickCapture: ['click'], onChange: ['blur', 'change', 'click', 'focus', 'input', 'keydown', 'keyup', 'selectionchange'], ... }
registrationNameModule对象指定了React事件到对应插件plugin的映射:
{ onClick: SimpleEventPlugin, onClickCapture: SimpleEventPlugin, onChange: ChangeEventPlugin, ... }
plugins对象就是上述插件的列表。在某个节点渲染过程中,合成事件比如onClick是作为它的prop的,如果判断该prop为事件类型,根据合成事件类型找到对应依赖的原生事件注册绑定到顶层document上,dispatchEvent为统一的事件处理函数。
2、事件触发
当任意事件触发都会执行dispatchEvent函数,比如上述事例中,当用户点击Child的p时会遍历这个元素的所有父元素,依次对每一级元素进行事件的收集处理,构造合成事件对象(SyntheticEvent–也就是通常我们说的React中自定义函数的默认参数event,原生的事件对象对应它的一个属性),然后由此形成了一条「链」,这条链会将合成事件依次存入eventQueue中,而后会遍历eventQueue模拟一遍捕获和冒泡阶段,然后通过runEventsInBatch方法依次触发调用每一项的监听事件,在此过程中会根据事件类型判断属于冒泡阶段还是捕获阶段触发,比如onClick是在冒泡阶段触发,onClickCapture是在捕获阶段触发,在事件处理完成后进行释放。SyntheticEvent对象属性如下:
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent // 原生事件对象 void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() void persist() DOMEventTarget target number timeStamp string type
dispatchEvent伪代码如下:
dispatchEvent = (event) => { const path = []; // 合成事件链 let current = event.target; // 触发事件源 while (current) { path.push(current); current = current.parentNode; // 逐级往上进行收集 } // 模拟捕获和冒泡阶段 // path = [target, p, body, html, ...] for (let i = path.length - 1; i >= 0; i--) { const targetHandler = path[i].onClickCapture; targetHandler && targetHandler(); } for (let i = 0; i <p><strong>3、更改事件委托(React v17.0)</strong></p><p>自React发布以来, 一直自动进行事件委托。当 document 上触发 DOM 事件时,React 会找出调用的组件,然后 React 事件会在组件中向上 “冒泡”。但实际上,原生事件已经冒泡出了 document 级别,React 在其中安装了事件处理器。</p><p>但是,这就是逐步升级的困难所在。</p><p>在 React 17 中,React 将不再向 document 附加事件处理器。而会将事件处理器附加到渲染 React 树的根 DOM 容器中:</p><pre class="brush:php;toolbar:false">const rootNode = document.getElementById('root'); ReactDOM.render(<app></app>, rootNode);
在 React 16 或更早版本中,React 会对大多数事件执行 document.addEventListener()。React 17 将会在底层调用 rootNode.addEventListener()
四、注意
由于事件对象可能会频繁创建和回收在React16.x中,合成事件SyntheticEvent采用了事件池,合成事件会被放进事件池中统一管理,这样能够减少内存开销。React通过合成事件,模拟捕获和冒泡阶段,从而达到不同浏览器兼容的目的。另外,React不建议将原生事件和合成事件一起使用,这样很容易造成使用混乱。
Disebabkan perubahan dalam delegasi acara dalam versi 17, sarang pokok React lama dan baharu kini boleh dilakukan dengan lebih selamat. Ambil perhatian bahawa untuk ini berfungsi, kedua-dua versi mestilah 17 atau lebih tinggi, itulah sebabnya menaik taraf kepada React 17 dan ke atas amat disyorkan.
[Cadangan berkaitan: Tutorial video Redis]
Atas ialah kandungan terperinci Mengapa bertindak balas menggunakan peristiwa sintetik?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!