19

我正在尝试使用 RN0.43 中的 FlatList 在 3 列中呈现约 250 个图像的列表,并且我在 FlatList 的 onLayout 函数中更改图像的宽度以适应屏幕的宽度。

初始性能还可以,但在向上/向下滚动后,有时需要一到两秒钟才能显示图像。

更糟糕的是,如果我更改为屏幕方向,屏幕更新需要 2~3 秒。

一些发现:

  1. 屏幕旋转后,调用 FlatList.onLayout 需要一两秒

  2. 在 FlatList.onLayout 和图像宽度更新之后,每个图像(大约列表的一半,~150 个图像;而只显示~15 个图像)被渲染 2~4 次,而 render() 只被调用一次。

问题:

  1. 如何修改代码以提高性能?
  2. 在多列列表的 getItemLayout() 中,偏移量是否应该类似于 (itemHeight + separatorHeight) * (index%numColumns)?

谢谢。

测试:GalaxySII (4.1.2) 和 Android SDK 模拟器 (7.1.1)

var data = [
    require('./res/img/Search.png'),
    require('./res/img/test - Copy.png'),
    // ~250 items
    ...];

class app extends React.Component {
    renderItem (info, width) {
        console.log('renderItem', info.index);
        if(width !== this.width) {
            this.imageStyle = {width: width-MarginHorizontal , height: width-MarginHorizontal, resizeMode: 'contain'};
        }
        return (
            <Image
                source = {info.item}
                key = {info.index}
                style={this.imageStyle}/>
            );
    }

    render() {
        console.log('Test.render');
        return (
            <View style={{
                flex: 1,
                flexDirection: 'row',
                justifyContent: 'flex-start',
                alignItems: 'center',
                backgroundColor: '#F5FCFF'
            }}>
                <GridList
                    numColumns={3}
                    columnWrapperStyle={{ alignItems: 'center', marginVertical: 5, justifyContent: 'space-around'}}
                    data={data}
                    renderItem={this.renderItem}
                />
            </View>
        );
    }
}

class GridList extends Component {
    onLayout(event) {
        console.log('GridList.onLayout()');
        let newColumnWidth = event.nativeEvent.layout.width/ this.numColumns;
        this.layout = Object.assign({},event.nativeEvent.layout);
        if( undefined === this.columnWidth  || Math.abs(newColumnWidth - this.columnWidth) > WidthTolerance ) {
            this.columnWidth = newColumnWidth;
            if(this.isComponentMounted) {
                this.setState({renderCount: this.state.renderCount+1});
            } else {
                this.state.renderCount +=1;
            }
        }
    }
    render() {
        console.log('GridList.render()');
        return (
            <FlatList
                {...(this.modifiedProps)}
                renderItem={(info) => { return this.props.renderItem(info, this.columnWidth); }}>
                {this.props.children}
            </FlatList>
        );
    }
}
4

5 回答 5

14

免责声明:我知道这个问题很老,但无论如何这是我的回答。

我的应用程序有一手包含 500 多个项目的列表。所以,我们到了应用程序在流行的不错的手机上崩溃的地步。然后我对 FlatLists 的性能进行了广泛的研究。

FlatList组件是作为旧的ScrollView. 问题是 ScrollViews 一次渲染所有列表,因此它们在视觉上表现得更好,但是在内存消耗方面存在权衡,这会导致应用程序崩溃。

所以平面列表是一个必要的邪恶。它们本质上只渲染可见的项目,这在内存消耗方面是一个巨大的收益,但是对于视觉性能来说是一个痛苦,特别是对于重/复杂的项目,这恰好是那些响应图像的情况。

如何解决?

您可以实施许多策略来缓解您的问题。

  • 使用缓存和性能图像,例如react-native-fast-image。您可以删除或缩写以释放Javascript线程的每个操作:执行此操作(每个图像new Image()都是loaded

  • 您的列表项组件是只读组件,应该是“哑”。根据需要实施一种shouldComponentUpdate() { return false }或更可靠的更新控制方法。这是巨大的性能提升。

  • 删除列表附近的任何地方的 console.logs。他们减慢了 Javascript 线程的速度真的很糟糕。

  • 为生产构建您的应用程序并对其进行测试。它几乎总是快两倍或三倍。由于调试,开发环境很慢。

  • 好好阅读这篇文章,了解更多策略。

结论

FlatList 一个缓慢的组件。这是一个已知、公开且有据可查的问题。尽你所能来改进它,让我们希望未来的版本可以解决这个问题。

于 2018-06-06T03:28:27.567 回答
0

您正在为列表的每一行单独重新创建大量样式对象。这会在 JS->Native 桥上产生大量流量。尝试使用样式表而不是内联传递样式。

于 2018-06-02T06:41:15.757 回答
0

我强烈建议大家阅读下面链接中的文章以优化您的平面列表。 https://reactnative.dev/docs/optimizing-flatlist-configuration

于 2020-02-28T12:52:20.243 回答
0

是的,我遇到了同样的问题-反应原生列表中有多个图像和视频所以我删除了 Flatlist 而不是这个我更喜欢使用 ListView 快速渲染并修复列表项的可触摸性问题,但不要忘记将 PureComponent 设置为列表项

于 2018-05-18T19:05:56.847 回答
-1

尝试使用keyExtractor为每个项目设置唯一键

例如:

render() {
  return (
    <List>
      <FlatList
        ...
        keyExtractor={item => item.email}
      />
    </List>
  );
}
于 2017-11-10T15:22:48.870 回答