文章来自我我的的 Github
)
在平时的开发里面,总会碰到handle
绑定的问题。若是你和我同样懒或者思考过,你会以为这个过程实在是太烦了吧。这里记录一下个人思路和历程。git
这里以一个按钮的点击事件来作示例。github
class App extends React.Components { state = { count: 0 } clickHandler () { const count = this.state.count + 1 this.setState({ count }) } render() { return ( <button> Click me to show something in dev tools </button> ) } }
这个例子的目的是点击按钮触发clickHandler
来让计数器加1
。咱们能够用两种不一样的方式来触发这个handle
,由于咱们使用了this.setState
,因此咱们都必需要给函数绑定this
。亦或是使用箭头函数
处理这个地方。typescript
jsx
里面bind(this)
<button onClick={this.clickHandler.bind(this)}> Click me to show something in dev tools </button>
嗯 这个的确能够。可是写起来很是的长看起来也挺丑的。有个问题是每次重渲染
的时候都会从新bind
一次函数,对于比较大的列表来讲这个地方很是不可取。babel
把clickHandler
改为以下的范式。ide
clickHandler = () => { const count = this.state.count + 1 this.setState({ count }) } // render ... <button onClick={this.clickHandler)}> Click me to show something in dev tools </button>
诶这样看起来会好不少诶。可是若是你有强迫症你会发现一件事情。若是咱们加上生命周期函数和一些其余的handler ... 好比这样。函数
componentDidMount () {} componentWillMount () {} componentWillUpdate () {} clickHandler = () => { const count = this.state.count + 1 this.setState({ count }) } antoherHandle = () => {}
你会发现这里生命周期函数和handler
的写法不同。可是你的确可让它们变得同样,好比把生命周期函数改为箭头函数。但是这看起来不会以为很怪异吗。毕竟你一直以来都不是这么作的。性能
除此以外箭头函数没法被继承,这意味着若是你的子组件须要继承函数,这将会致使没法作到。更加须要注意的东西是没法继承带来的性能问题。这会致使每次建立组件都会建立新的方法致使额外的开销(由于箭头函数的实现实际上是直接在constructor
函数里丢方法),若是是经过继承,那么它们这些方法老是来自同一个prototype
,js编译器是会作优化的。测试
详细文章能够看这一篇Arrow Functions in Class Properties Might Not Be As Great As We Think。fetch
bind(this)
经过构造器来写绑定函数其实看起来是不错的优化
constructor (props) { super(props) this.clickHandler = this.clickHandler.bind(this) this.antoherHandle = this.antoherHandle.bind(this) }
既解决了性能(内存)的问题。还能作不少有意思的事情好比说,利用现有的方法添加更有语义化的方法。
constructor (props) { super(props) this.clickHandler = this.clickHandler.bind(this) this.antoherHandle = this.antoherHandle.bind(this) this.clickWithOne = this.clickHandler.bind(this, 1) }
这样就能产生每次都会传参数1
的新事件。看起来的确是还不错。可是仍然有问题。当你的方法线性的增长的时候,若是有三个四个五个六个的时候,你可能须要一个一个的绑定。添加它们到构造函数里面,更糟糕的多是经过复制粘贴之前写的方法,你会绑定错误的函数。就像这样。
constructor (props) { super(props) this.clickHandler = this.clickHandler.bind(this) this.antoherHandle = this.antoherHandle.bind(this) this.clickWithOne = this.antoherHandle.bind(this, 1) }
你必须在运行的时候才知道你的clickWithOne
绑定的实际上是antoherHandle
。若是你没测试过,那么极可能就会出现一些你难以理解的问题或者bug。
若是你动脑想一想会发现能够写一个autobind
的方法来自绑定函数呀。可是你很懒没有去写,你经过github
搜索到了一个叫作React-autobind的库。看起来好像还不错。
constructor(props) { super(props); autoBind(this); }
甚至能够不绑定某些方法。
constructor(props) { super(props); autoBind(this, { wontBind: ['leaveAlone1', 'leaveAlone2'] }); }
或者指定只绑定某些方法。
constructor(props) { super(props); autoBind(this, { bindOnly: ['myMethod1', 'myMethod2'] }); }
看起来彷佛是妙极了。可是你会发现这个写法其实仍是很繁琐啊。要写一坨东西。。打开源码看一眼你会发现有一个默认的wonbind
列表。
let wontBind = [ 'constructor', 'render', 'componentWillMount', 'componentDidMount', 'componentWillReceiveProps', 'shouldComponentUpdate', 'componentWillUpdate', 'componentDidUpdate', 'componentWillUnmount' ];
表示不须要自动绑定的函数的名字。可是这个列表很是的糟糕,由于随着React版本的提高,某些钩子和方法都会被废弃,随着时间可能还会增长增多的方法。
这个库也好久没更新了。差评仍是放弃吧。。。
若是你了解过ES7
的decorator
。你会发现上面的写法彻底可使用decorator
的形式表示,而且这个库也支持在typescript
上面使用。而且结构会很是的清晰。因而你找到了autobind-decorator这个库。它能帮助到咱们,给咱们想要的东西,文档一开始就告诉咱们。
// Before: <button onClick={ this.handleClick.bind(this) }></button> // After: <button onClick={ this.handleClick }></button>
用以前...用以后的样子,很好就是咱们要的。 这个库有个缺点就是必须的IE11+以上的版本才支持,可是这其实也还好。
另外就是你的开启decorator
的支持在babel
的配置里面。
{ "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ] }
咱们来看看推荐的用法。
import {boundMethod} from 'autobind-decorator' class Component { constructor(value) { this.value = value } @boundMethod method() { return this.value } } let component = new Component(42) let method = component.method // .bind(component) isn't needed! method() // returns 42
给方法绑定this,而不是整个类,这么作是更加合理的。由于不是每一个方法都须要用到this
的。如Dan
所说。
It is unnecessary to do that to every function. This is just as bad as autobinding (on a class). You only need to bind functions that you pass around. e.g.onClick={this.doSomething}
. Orfetch.then(this.handleDone)
-- Dan Abramov
既能够在函数上,也能够在类上使用的@autobind。
import autobind from 'autobind-decorator' class Component { constructor(value) { this.value = value } @autobind method() { return this.value } } let component = new Component(42) let method = component.method // .bind(component) isn't needed! method() // returns 42 // Also usable on the class to bind all methods // Please see performance if you decide to autobind your class @autobind class Component { }
只能做用于类的@boundClass,咱们不免也会有全都须要绑定到this
的状况这时候咱们直接boundClass
会更加的简洁。
import {boundClass} from 'autobind-decorator' @boundClass class Component { constructor(value) { this.value = value } method() { return this.value } } let component = new Component(42) let method = component.method // .bind(component) isn't needed! method() // returns 42
缺点也是有的,并不能像constructor
那样本身随随便便的定不一样的方法名经过原有的方法,必须的写出一个新的,可是这是小问题,无伤大雅。而且descorator
并无成为标准,可是其实也差很少了,并不担忧。
这里的全部的解决思路都各有千秋吧。怎么取舍仍是看本身,这里就不一一列出来各自的对比了 ,于我我的而言会偏好Autobind-decorator
,认为这是全部解决方案里面最好的一个了,可是要引入一个额外的依赖仍是有点麻烦。