最近公司发现用 ReactNative 的 ListView 中的 Cell 并不像原生 TableView 中那样重用,一旦有多个Cell 被加载,app 的内存就唰唰得往上飙。
所以需要寻找解决方法,经过一番 Google 发现外国不少人也在表示这个问题
最后发现一个解决方法,其中的想法比较特别,所以我们来研究一下
《Recycling Rows for High Performance React Native List Views》。
他的主要思想是:
-
react-native js,端创建足够的滑动行;
const ROWS_FOR_RECYCLING = 20;
const bodyComponents = [];
for (let i = 0; i < ROWS_FOR_RECYCLING; i++) {
bodyComponents.push(
<ReboundRenderer key={'r_' + i} boundTo={this.state.binding[i]} render={this.props.renderRow} />
);
}
然后在 Render 的时候把bodyComponents放 <b>原生组件RNTableViewChildren</b> 里面
render() {
console.log("重画了!!!!!");
return (
<View style={{flex: 1}}>
<RNTableViewChildren
style={{flex: 1}}
onChange={this.onBind.bind(this)}
rowHeight={this.props.rowHeight}
numRows={this.props.numRows}>
{bodyComponents}
</RNTableViewChildren>
</View>
);
}
之后在<b>原生组件RNTableViewChildren</b>的 insertReactSubview 中可以捕捉到嵌套的 View
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
// will not insert because we don't need to draw them
// [super insertSubview:subview atIndex:atIndex]; //不需要做任何处理
[_unusedCells addObject:subview];
}
这样就能把 javaScript 中创建的 View 加入到
-
TableViewChildren.js里维护一个binging数组,关联视图和对应数据的行号;
把binding数组放到 State 中,修改 State 的 Binding 刷新视图
const binding = [];
for (let i = 0; i < ROWS_FOR_RECYCLING; i++) {
binding.push(i);
}
this.state = { binding: binding // childIndex -> rowID};
-
原生发送onChange消息告诉TableViewChildren视图对应数据的行号有更改;
原生代码中发送onChange 消息
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = @"CustomCell";
TableViewCell *cell = (TableViewCell *)[theTableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.cellView = [self getUnusedCell];
NSLog(@"Allocated childIndex %d for row %d", (int)cell.cellView.tag, (int)indexPath.row);
} else {
NSLog(@"Recycled childIndex %d for row %d", (int)cell.cellView.tag, (int)indexPath.row);
}
NSDictionary *event = @{
@"target": cell.cellView.reactTag,
@"childIndex": @(cell.cellView.tag),
@"rowID": @(indexPath.row),
@"sectionID": @(indexPath.section),
};
[_eventDispatcher sendInputEventWithName:@"onChange" body:event];
return cell;
}
javaScript端接收到 Onchage 消息,就修改 State 触发刷新
onBind(event) {
const {target, childIndex, rowID, sectionID} = event.nativeEvent;
bodyComponents[childIndex] = (
<ReboundRenderer key={'r_' + childIndex} boundTo={rowID} render={this.props.renderRow} /> );
this.state.binding[childIndex] = rowID;
this.setState({
binding: this.state.binding
});
}
-
ReboundRenderer.js通过判断行号更改刷新视图。
ReboundRenderer通过 React 的刷新机制,重写shouldComponentUpdate,来判断是否需要刷新
从而只刷新需要更新的 View
shouldComponentUpdate: function(nextProps): boolean {
return nextProps.boundTo !== this.props.boundTo;
},
整个项目的想法很精妙,利用原生的重用来解决 reactNative 没解决的性能问题,其中reactNative只需要画出 Cell 的 View 即可,其他复杂的要求都被原生代替,原生只作为一个触发器,触发一些特定的刷新视图事件。但由于其基本视图使用的是原生的 TableView,很难和 reactNative 的组件结合,譬如下拉刷新,所以实用性比较低。但这个项目给了我们一个解决一些奇特要求方法,使reactNative很好的和原生结合。个人觉得很值得学习。