最近小伙伴问了我关于react中class中事件为什么要进行绑定,感觉他对这块的理解比较模糊,特此总结一下。
为什么要进行bind
首先,我们来看一个例子
let test = {
a: 1,
b: function() {
console.log(this.a)
}
}
我们执行如下操作:
test.b() // 1
const {b} = test;
b() // undefined
首先我们使用test来调用其中的方法b,此时根据谁调用this指向谁的原则,this.a输出结果就是test中的a,即为1 当我们使用es6的解构,从test中将b解构出来,即将test.b赋值给一个变量b,当我们执行b()时,依据谁调用指向谁的原则,b指向了window,此时window下无a,因此输出undefined。
如果我们设置严格模式:
let test = {
a: 1,
b: function() {
"use strict"
console.log(this.a)
}
}
const {b} = test;
b()
此时运行b()时,b中的this指向undefined。 那么我们来看看react中:
render() {
return (
<div>
<a onClick={this.clickEvent}>test</a>
</div>
)
}
如代码所示,我们给a标签绑定了一个onClick事件。我们大家都知道,react是从虚拟DOM映射成为真实DOM,onClick也不是真实DOM中的click事件,只是在从虚拟DOM生成真实DOM的过程中,将这个this.clickEvent赋值到真实DOM上的click事件。因此,render中的onClick相当于一个中间量的存在,因为中间量的存在,当真实DOM调用this.clickEvent时,此时的this自然无法指向到我们定义的组件实例,导致this指向丢失,因此必须进行bind。
bind的方法有哪些
class Test {
consoleName () {
this.con('Hello Test');
}
con(name) {
console.log(name);
}
}
const test = new Test();
test.consoleName(); // 'Hello Test'
const { consoleName } = test;
consoleName(); // Cannot read property 'con' of undefined
如上代码所示,我们从test中解构出方法并赋值给consoleName,此时执行方法的时候,this指向丢失,导致consoleName中无法找到con。此时this是什么呢?ES6的class内部默认严格模式,严格模式下,如果 this 没有被执行环境定义(即没有作为对象的属性或者方法调用),那它将保持为进入执行环境时的值,即undefined。那怎么才能不报错呢?
方法1 constructor bind
class Test {
constructor() {
this.consoleName = this.consoleName.bind(this);
this.con = this.con.bind(this);
}
consoleName () {
this.con('Hello Test');
}
con(name) {
console.log(name);
}
}
const test = new Test();
test.consoleName(); // 'Hello Test'
const { consoleName } = test;
consoleName(); // 'Hello Test'
方法2 箭头函数
class Test {
consoleName = () => {
this.con('Hello Test');
}
con = (name) => {
console.log(name);
}
}
const test = new Test();
test.consoleName(); // 'Hello Test'
const { consoleName } = test;
consoleName(); // 'Hello Test'
方法3 render中bind
在render中直接bind。
因此总结下来如下:
render () {
return (
<a onClick={this.clickEvent}>方法1 constructor绑定</a>
<a onClick={() => this.clickEvent()}>方法2 箭头函数绑定</a>
<a onClick={this.clickEvent.bind(this)}>方法3 直接绑定</a>
)
}
性能对比
首先,官方推荐第一种方法绑定this。那么接下来我们分析一下:
第一种方法,只在构造函数里面渲染一次,即实例化时执行一遍。
第二种方法,每一次render的时候,都会生成一个新的箭头函数。
第三种方法,每一次render的时候都会执行一次函数。
我们先说下PureComponent,PureComponent是Component的优化,可以少写 shouldComponentUpdate 函数,从而达到组件的 props 和state 都没发生改变,render就不渲染的效果。假如说你使用了第二种和第三种方法,那么在判断是否需要render的时候,PureComponent的shallowCompare会认为你的onClick与之前的不相等,自然就需要重新render。这一定程度上带来了不少的性能损耗。并且,箭头函数的方式,我们可以在chrome dev tools堆栈中看到一堆的anonymous,带来调试上的困难。
因此方法1性能最好,也是官方推荐。方法2和方法3会带来重新渲染的问题。