大家好 本文是关于JavaScript中的事件委托及其在react.js中的实现。

到底是什么意思? 为什么和为什么?
首先,让我们简要讨论一下:
- 什么是事件;
- 分布如何发生;
- 使用JavaScript中的示例处理DOM Level 2;
最后:为什么不要忘记React中的委托。
大事记
JavaScript和HTML通过事件相互交互。 每个事件都用于告诉JavaScript文档或浏览器窗口中发生了某些事情。 为了捕获这些事件,我们需要侦听器(侦听器),例如在事件事件中触发的处理程序。
事件传播
订购 解决问题:如何了解事件属于页面的哪一部分? 实现了两种方法:在Internet Explorer中,“事件冒泡”;在Netscape Communicator中,“事件挂钩”。
事件冒泡
在这种情况下,事件在文档树中最深的节点处触发,然后在整个层次结构中上升到窗口本身。
<!DOCTYPE html> <html> <head> <title>Some title</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html>
在这种情况下,将有以下顺序:
- div元素
- 身体元素
- html元素
- 单据
- 窗
现在,所有现代浏览器都支持冲浪,尽管实现方式不同。
对于事件拦截,情况恰恰相反:
- 窗
- 单据
- html元素
- 身体元素
- div元素
人们认为事件可以在到达目标元素之前进行处理(根据Netscape的决定,后来选择了所有现代浏览器)。
因此,我们具有用于分发DOM事件的以下结构:
- 窗
- 单据
- html元素
- 主体元素//拦截阶段结束
- div元素//目标阶段
- 身体元素//上升阶段开始
- html元素
- 单据
- 窗
此方案分为三个阶段:拦截阶段(事件可以在到达元素之前被拦截),目标阶段(目标元素进行处理以及上升阶段)以响应事件执行任何最终动作。
因此,我们转向事件处理
让我们来看一个JavaScript中事件处理的典型示例。
const btn = document.getElementById('myDiv') btn.addEventListener("click", handler)
一切都将一事无成,但在这里我们回想起了我们心爱的IE,它使用attachEvent订阅了事件,并删除了detachEvent。 您可以多次订阅该活动。 并且不要忘记通过签署匿名功能,我们没有机会退订。
但是我们不是g *外部编码员。 让我们按照经典去做:
var EventUtil = { addHandler: function (elem, type, handler) { if (elem.addEventListener) { elem.addEventListener(type, handler, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, handler) } else { elem["on" = type] = hendler } }, removeHandler: function (elem, type, handler) { if (elem.removeEventListener) { elem.removeEventListener(type, handler, false) } else if (elem.detachEvent) { elem.detachEvent("on" + type, handler) } else { elem["on" = type] = null } } }
很好,但是事件对象呢? 毕竟,在IE中没有.target .srcElement,preventDefault吗? 没有returnValue = false。 但是没有什么可以添加两种方法的:
var EventUtil = { addHandler: function (elem, type, handler) { if (elem.addEventListener) { elem.addEventListener(type, handler, false) } else if (elem.attachEvent) { elem.attachEvent("on" + type, handler) } else { elem["on" = type] = hendler } }, getEvent: function (event) { return event ? event : window.event }, getTarget: function (event) { return event.target || event.srcElement }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault() } else { event.returnValue = false } }, removeHandler: function (elem, type, handler) { if (elem.removeEventListener) { elem.removeEventListener(type, handler, false) } else if (elem.detachEvent) { elem.detachEvent("on" + type, handler) } else { elem["on" = type] = null } }, stopPropagation: function (event) { if (event.stopPropagation) { event.stopPropagation() } else { event.cancelBubble = true } } }
等等 等 这些都是舞蹈。
好吧,我们做得很好,我们解决了所有问题,一切正常。 没错,代码出来很麻烦。 现在想象我们需要许多元素的订阅。 哇,这将需要很多代码。 一个例子:
<ul> <li id="id1">go somewhere</li> <li id="id2">do something</li> <li id="some-next-id">next</li> </ul> var item1 = document.getElementById('id1') var item2 = document.getElementById('id2') var itemNext = document.getElementById('some-next-id') EventUtil.addHandler(item1, "click", someHandle) EventUtil.addHandler(item2, "click", someHandle2) EventUtil.addHandler(itemNext, "click", someHandle3)
因此,对于每个元素,我们都不要忘记删除,使用target等对象
在这里,事件授权可以为我们提供帮助。
我们需要做的就是将一个处理程序连接到DOM树的最高点:
<ul id="main-id">
结果,我们在内存中只有一个处理程序,并且id属性可用于所需的操作。 较少的内存消耗可提高整体页面性能。 注册事件处理程序需要更少的时间和更少的DOM调用。 唯一的例外可能是鼠标悬停和鼠标移开,它们的所有操作都比较复杂。
那反应呢
关于跨浏览器兼容性的所有工作已经由来自Facebook的人完成。 我们所有的事件处理程序都接收SyntheticEvent的实例。 它负责我们重用池中的事件,并在调用处理程序后删除所有属性。
好啊
但是,额外的处理程序就是额外的处理程序。 我见过几次面,我悔改了,我写了这样的代码:
class Example extends React.Component { handleClick () { console.log('click') } render () { return ( <div> {new Array(20).fill().map((_, index) => <div key={index} // elem.id id={index} // elem.id onClick={() => console.log('click')} /> )} </div> ) } }
该示例显示了当某张工作表的元素数为n,因此处理程序的注册数为n时的情况。
让我们运行,转到页面上,检查当前有多少个处理程序正在运行。 为此,我找到了一个不错的脚本:
Array.from(document.querySelectorAll('*')) .reduce(function(pre, dom){ var clks = getEventListeners(dom).click; pre += clks ? clks.length || 0 : 0; return pre }, 0)
在chrome dev-tool中工作。
现在,我们将所有这些委托给父div并加油,我们刚刚优化了应用程序n = array.length次。 下面的示例代码:
class Example extends React.Component { constructor () { super() this.state = { useElem: 0 } } handleClick (elem) { var id = elem.target.id this.setState({ useElem: id }) } render () { return ( <div onClick={this.handleClick}> {new Array(20).fill().map((_, index) => <div key={index} // elem.id id={index} // elem.id useElem={index === this.state.useElem} /> )} </div> ) } }
委托是处理大量订阅的好工具,在动态渲染和频繁重绘的情况下,它是不可替代的。 可怜用户的资源,它们不是无限的。
本文基于Nicholas Zakas为专业的Web开发人员编写的JavaScript书。
非常感谢您的关注。 如果您有什么要分享的东西或发现某种缺陷,可能是一个错误或只是一个问题,请在评论中写下。 我将很高兴收到任何反馈!