4.React 合成事件和原生事件

336 阅读2分钟

一.原生事件

原生事件就是js的原生事件,如通过document.addEventListener来设置的监听事件。

由于原生事件需要绑定在真实DOM上,所以一般是在componentDidMount阶段或者组件/元素的ref的函数执行阶段进行绑定操作,并且注意要在componentWillUnmount阶段进行解绑操作以避免内存泄漏。

二.事件代理

1.概述

如果我们为所有需要添加事件处理的DOM元素一一绑定上事件处理程序,那么不仅编写出的代码会很繁杂,整个页面的性能也会很低下。

一般来说,在通过选择器拿到元素集合后,我们会用for循环对集合进行遍历,然后为其添加事件处理方法

操作的DOM数量越多,对DOM的访问次数越多,浏览器的重绘次数就越来越多,页面的响应速度和运行性能也就越来越差。

2.原理

利用事件冒泡的机制来进行实现

按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则传播到父级去处理。

3.实现

利用事件冒泡的机制来进行实现

按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则传播到父级去处理。

<ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ol>

//比如我们想要实现的是点击li,弹出li的序列。如果要直接操作livar lists = document.querySelectorAll('li');
for(let i = 0; i < lists.length; i ++){
    lists[i].onclick =function () {
        alert(lists[i].innerText);
    }
}

//使用事件代理来实现

var lists = document.querySelector('ol');
lists.onclick = function (event) {
    alert(event.target.innerText);
}

通过选择器拿到li的容器也就是父节点ol,然后只绑定一次点击事件,并通过event对象提供的属性target指向容器的子节点,然后弹出该子节点的文本内容。

三.React 合成事件

React有自己的一套事件机制,它重新封装了绝大部分的原生事件。合成事件采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。

1.大致原理

React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行。

2.特点总结

React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
React 通过对象池的形式管理合成事件对象的创建和销毁,减少了垃圾的生成和新对象内存的分配,提高了性能

3.为什么react事件需要手动绑定this

合成事件触发之后会冒泡一路到document的节点,然后开始分发document节点收集到的事件,这个时候react从事件触发的组件实例开始, 遍历虚拟dom树,从树上取下我们绑定的事件,收集起来,然后执行

class Test extends React.Component {

fatherHandler = function father() {}
childHander = function child() {}

render() {
return (

); }

}
// 当事件触发以后react会把上面的事件处理函数放到一个数组里
// [father, child]

最后,react只要遍历执行这个数组,就能执行所有需要执行的事件处理函数。这里react对函数进行了临时保存,这个时候执行的话,this自然就丢失了。

如果react保存顺便保存一下实例,还是可以做到,不需要你绑定this的,但是这样对于react来说代价太大了。