浅谈React事件系统主要分为两篇:
浅谈React事件系统-事件绑定
浅谈React事件系统-源码解读
场景
一位同事在群里抛出一个问题, 假设一个列表有800个节点, 给每个节点都绑定一个 onClick事件, 如果下面这样子写页面就会有非常卡
1 | <ul> |
假如当这个list的长度为800个节点的时候, 每次渲染都会进行都会由于生成一个新的匿名函数 (event) => {onClick(event, data) , props.onClick的已用发生改变, 导致li这个组件会重新执行render, 如果是 800个节点, 自然就卡了.
要解决不卡顿, 就要保证每次传入的onClick引用不发生变化
先声明事件处理函数
不推荐在大量节点的场景使用下面的绑定事件方式
1 | onClick={this.handleClick.bind(this)} |
这种方式, 因为bind本身就是产生一个新的函数. 这两种的onClick引用会发生变化, 在使用PureComponent的时候, 容易触发重复的渲染
推荐在类里面先声明好事件处理函数
1 | class App extends PureComponent { |
解决参数传递
- 直接给组件传入已经定义好的方法, 我们先在组件里面定义个onClick方法, 当然这样子也会有问题, 假如我要传入相关的数据咋办, 比如我要传一个desc的字符串到 onClick方法里面, 怎么实现, 这里我们可以通过data-xxx的属性来实现, 在onClick中只需要 event.tcurrentTarget.dataset.desc就可以获取到 desc字符串了.
1 | class App extends PureComponent { |
- 把list 转换为 listMap的结构, 每次只需要传入索引值, 在onClick方法里面从 listMap 中取值
1 | class App extends PureComponent { |
下文引言
在完成重构Tree组件的过程中, 为了追求最好的性能, 我也对代码进行如上的改造, 这一改就出问题了
每个人都有这样子一种经历吧, 因为某些意外的原因看到一些特殊的现象, 就以为自己发现新大陆了.
在改造代码过程中, 发现每次onClick事件中event对象中的currentTarget都是指向 document, 由于自己接触过一点react的事件模型, React的所有的事件都是绑定到 document上面, 并不是绑定到元素上面的. 所以看整个dom树只有document是绑定了事件的. 然后再由React事件系统去分发事件, 这样子一联想, 可不得了, 我就以为自己发现React事件模型的bug了, 作为这么成熟的前端框架, 居然currentTarget这个属性都有问题了,这明显是不合理的….. 我把我的发现分享到群里, 结果脸好疼…..
痛定思过, 重新写demo来排查问题, 结果是自己的代码逻辑错误, 同时也研究了一下React Event源码