我们忘记了JavaScript中的委托。 事件委派在反应

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



到底是什么意思? 为什么和为什么?


首先,让我们简要讨论一下:


  1. 什么是事件;
  2. 分布如何发生;
  3. 使用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> 

在这种情况下,将有以下顺序:


  1. div元素
  2. 身体元素
  3. html元素
  4. 单据

现在,所有现代浏览器都支持冲浪,尽管实现方式不同。

对于事件拦截,情况恰恰相反:


  1. 单据
  2. html元素
  3. 身体元素
  4. div元素

人们认为事件可以在到达目标元素之前进行处理(根据Netscape的决定,后来选择了所有现代浏览器)。

因此,我们具有用于分发DOM事件的以下结构:


  1. 单据
  2. html元素
  3. 主体元素//拦截阶段结束
  4. div元素//目标阶段
  5. 身体元素//上升阶段开始
  6. html元素
  7. 单据

此方案分为三个阶段:拦截阶段(事件可以在到达元素之前被拦截),目标阶段(目标元素进行处理以及上升阶段)以响应事件执行任何最终动作。


因此,我们转向事件处理


让我们来看一个JavaScript中事件处理的典型示例。


 const btn = document.getElementById('myDiv') btn.addEventListener("click", handler) // some code btn.removeEventListener("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    <li id="id1">go somewhere</li> <li id="id2">do something</li> <li id="some-next-id">next</li> </ul> var list = document.getElementById('main-id') EventUtil.addHandler(list, "click", function(event) { event = EventUtil.getEvent(event) var target = EventUtil.getTarget(event) switch(target.id) { case "id1": //  -    id1 break case "id2": //  -    id1 break case "some-next-id": //  -    break } }) 

结果,我们在内存中只有一个处理程序,并且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书。


非常感谢您的关注。 如果您有什么要分享的东西或发现某种缺陷,可能是一个错误或只是一个问题,请在评论中写下。 我将很高兴收到任何反馈!

Source: https://habr.com/ru/post/zh-CN467361/


All Articles