react 性能优化之函数绑定

react中创建组件的时候, 如果有用到回调方法传递到下级组件, 而在方法中需要使用this参数时候, 有两种方法可以使用, 一种是使用bind方法绑定this, 还一种是使用箭头函数绑定this. 一般推荐都是使用bind方法, 因为箭头函数在每次执行render方法时都会重新生成一次, 造成传入的props改变, 重新触发render.

1
2
3
4
5
1.
this.childChange = this.childChange.bind(this);
<Child onChange={this.childChange}/>
2.
<Child onChange={(event)=>{this.childChange(event)}}/>

在第 2 种写法中, 当Child组件是PureComponent时, 也会在每次父组件执行render之后执行render, 因为props.onChange属性改变了.

那么, 一般在需要传入回调函数时候, 都使用bind之后的函数就可以了, 但是在某些特殊情况下, 必须要使用箭头函数时, 如何优化呢.

1
2
3
4
5
6
7
8
9
10
11
childChange(event, index){
...;
}
<NgFor for={arraysItem} of="item" render={
function (_props) {
return (<Child onChange={(event)=>{this.childChange(event, _props.$$index)} />)
}}>
</NgFor>
{arrays.map((value, index) => {
return (<Child onChange={(event)=>{this.childChange(event, index)} />)
})}

当使用闭包传入值的时候, 即使执行bind后的方法, 也需要在里面使用箭头函数调用, 传入index值. 那么需要用到bind函数的args来优化.
自定义一个bindOnce方法, 传入组件和要绑定的方法以及其他参数, 对方法进行bind和缓存.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Immutable from 'immutable';

var __bindOnce = function (component, func, ...args) {
if (!component.__bindFuncs) {
component.__bindFuncs = Immutable.Map();
}
let __bindFuncs = component.__bindFuncs;
let argsMap = Immutable.fromJS(args);
if (__bindFuncs.has(func)) {
if (__bindFuncs.get(func).has(argsMap)) {
return __bindFuncs.get(func).get(argsMap);
} else {
let bindFunc = func.bind(component, ...args);
component.__bindFuncs = __bindFuncs.update(func, (v) => v.set(argsMap, bindFunc));
return bindFunc;
}
} else {
let bindFunc = func.bind(component, ...args);
component.__bindFuncs = __bindFuncs.set(func, Immutable.Map().set(argsMap, bindFunc));
return bindFunc;
}
};

export default bindOnce;

这里使用了immutableMap来作为缓存字典, 因为Map结构可以使用Objectfunction作为Key. 使用方法:

1
2
3
4
5
6
7
8
9
10
11
childChange(index, event){
...;
}
<NgFor for={arraysItem} of="item" render={
function (_props) {
return (<Child onChange={bindOnce(this, this.childChange, _props.$$index)} />)
}}>
</NgFor>
{arrays.map((value, index) => {
return (<Child onChange={bindOnce(this, this.childChange, index)} />)
})}

可以见到, 唯一需要改变的就是, 在childChange中, event参数的位置在最后, 这是因为onChange总会传入event参数, 而先bind的参数会出现在参数列表前面. 当使用了bindOnce之后, Child便不会因为propsonChange变化而重新render了.

作者

Mosby

发布于

2017-09-25

许可协议

评论