关键时刻,第一时间送达!
React中的事件处理函数绑定看似容易,但还是需要一些技巧。如果你了解Perl和Python的历史,就应该知道TMTOWTDI和TOOWTDI这两个词的意思:Perl的理念是TMTOWTDI,即There's More Than One Way To Do It(完成一项任务有很多种方法),而Python的理念是TOOWTDI,即There's Only One Way To Do It(完成一项任务只有一种方法)。在历史上,Perl曾以这种多样性闻名,但实践证明,过多的实现方法只会给代码造成混乱,从软件工程的角度来看,它远远不如Python奉行的单一方法理念。很不幸,JavaScript就是个TMTOWTDI语言,所以混乱也是在所难免了。
这篇文章将探讨在React中绑定事件处理函数的常见方法,以及每种方法的优缺点。本文最重要的目的就是帮你找到那个“唯一的方法”。
▌在render()函数中动态绑定
第一种常用的方法就是在 .render() 函数中直接调用 .bind(this) 实现绑定。例如:
我们知道React使用Virtual DOM,每当绘制发生时,React会比较更新后的Virtual DOM和上一次的Virtual DOM,然后在画面上仅更新DOM树中改变了的部分。
在这个例子中,当 render() 被调用时,this.handleClick.bind(this) 也会被调用。注意:该调用会生成一个全新的绑定好的事件处理函数,这个函数与第一次 render() 被调用时的事件处理函数是不同的!
动态绑定情况下的Virtual DOM,蓝色的元素将被重绘
如上图所示,第一次 render() 被调用时,this.handleClick.bind(this) 返回 funcA,因此React认为 onChange 是 funcA。
之后当 render() 再次被调用时,this.handleClick.bind(this) 返回 funcB(注意它每次被调用都会返回一个新的函数)。于是,React认为 onChange 从 funcA 变成了 funcB,意味着button也会被重绘。
有人说就一个按钮其实无所谓了,但如果是100个按钮呢?
在上例中,任何 label 的改变都将导致所有的按钮被重绘(理想状况是只有label变更了的那个按钮才重绘,其他按钮不会),因为这个循环会为每个按钮生成新的 onChange 处理函数。
▌在constructor()中绑定
传统的做法是在构造函数中进行绑定。非常朴实的做法:
该方法要比第一种方法好得多。调用 render() 不会为 onClick 生成新的事件处理函数,因此只要按钮不发生变化, 就不会被重绘。
在构造函数中进行绑定情况下的Virtual DOM,蓝色的元素将被重绘
▌利用箭头函数绑定
通过ES7的类属性功能(现在可以通过Babel实现:https://babeljs.io/docs/plugins/transform-class-properties/),可以在定义方法的时候进行绑定:
上述代码中,handleClick 被赋值为绑定后的函数,这种写法相当于:
因此,在组件初始化结束之后,this.handleClick 的值就不会再变化。因此,这样做能保证 不会被重绘。个人认为这是实现绑定的最好的办法。代码写起来很简单、易懂,而且能达到我们的目的。
▌利用箭头函数为多个元素实现动态绑定
利用同样的技巧,可以用箭头函数给多个输入绑定同一个处理函数:
乍一看貌似没问题,而且实现极其简单明了。但仔细想想,就会发现这种方法与第一种方法有同样的问题:每次 render() 被调用时,两个 都会被重绘。
不过个人认为这种方法其实问题不大,毕竟实现简单,而且我不愿意为每个输入框都写一个 handleXXXChange 函数。而且最重要的是,这种“多用处理函数”几乎不太可能出现在一个列表中。也就是说,只会有固定数量的元素被重绘(而不像列表那样会产生任意多个),一般不会造成性能问题。
毕竟,这种写法带来的便利性要远远大于性能损失,所以我还是推荐这种做法的。
▌利用autobind自动绑定
还有一种方法是利用 lodash-decorators 或 react-autobind 等包,在定义类时自动绑定所有方法。我个人不太喜欢这种自动化处理,而是倾向于把绑定限制在必需的最小范围,但自动绑定的确能在保证代码可读性的同时节省很多工作量。代码大概类似于:
领取专属 10元无门槛券
私享最新 技术干货