美文网首页
React Native 如何优化导航类型组件的切换卡顿问题

React Native 如何优化导航类型组件的切换卡顿问题

作者: 莫帆海氵 | 来源:发表于2020-12-20 15:35 被阅读0次

问题描述

刚开始做 RN 项目,在实现的商品列表有导航切换的布局时切换非常卡顿,总是要等待几秒钟才能渲染完效果。
即使在每个商品 item 上增加 shouldComponentUpdate 严格控制,切换也很卡顿。

截图


rn_tab_demo.png

实现代码如下

// 根据数据渲染所有导航对应的商品列表,
// 通过透明度来实现商品列表的隐藏显示,
// 辅以定位和层级来实现当前的列表是可用
<View key="goods-list-wrap">
  {tab_data.map((item, idx) => {
    let selected = idx === current_nav_index
    return (
      <View
        key={`goods-list-${idx}`}
        style={cn(styles, { scene: true, show: selected })}
      >
        <FlatList
          style={{ flex: 1 }}
          contentContainerStyle={[styleA, styleB]}
          data={list}
          renderItem={this.renderGoodsItem}
          numColumns={2}
          keyExtractor={(it) => it.goods_id}
        />
      </View>
    )
  })}
</View>

// style 的部分代码
scene: {
  flex: 1,
  position: 'absolute',
  top: 0,
  right: 0,
  bottom: 0,
  left: 0,
  opacity: 0,
  overflow: 'hidden',
},
show: {
  opacity: 1,
  zIndex: 1,
},

商品 item 组件实现的 shouldComponentUpdate

shouldComponentUpdate(nextProps) {
  if (nextProps.item === this.props.item) {
    return false
  }
  return true
}

尝试优化手段

基于以上的实现思路,做了各种优化尝试,都没能很好的解决切换卡顿的问题。

实现方式:

  • 商品 item 的 shouldComponentUpdate 控制更严格
  • 精简更多的 dom 层级
  • 包括把 FlatList 独立组件增加 shouldComponentUpdate 控制
  • 严格控制父级组件的状态更新,确保在导航切换没有因父级状态变化而加深渲染耗时

解决问题

尝试多种方法后,实在没有思路,想到已经实现的类似组件有哪些,经过一番查找,
在 RN 社区中发现 react-native-tab-navigator 这个组件,研究后发现一些差异,在本地尝试后发现效果有比较大的提升

// 摘抄部分 react-native-tab-navigator 代码
<View
  {...props}
  pointerEvents={selected ? 'auto' : 'none'}
  removeClippedSubviews={!selected}
  style={[
    styles.sceneContainer,
    selected ? null : styles.hiddenSceneContainer,
    props.style,
  ]}>
  <StaticContainer shouldUpdate={selected}>
    {this.props.children}
  </StaticContainer>
</View>

// StaticContainer 中的方法
shouldComponentUpdate(nextProps) {
  return !!nextProps.shouldUpdate;
}

主要差异有两点

  1. 使用 removeClippedSubviews,在非当前选中列表中都启用这个属性
  2. 它的所有内容通过统一组件实现 shouldComponentUpdate,只有选中列表上启用更新

结论

实现方式还是通过减少渲染内容来解决切换卡顿的问题,确保每次更新只渲染需要的部分。
removeClippedSubviews + shouldComponentUpdate 确保更新的只有当前选中列表。

removeClippedSubviews

这是一个特殊的性能相关的属性,由 RCTView 导出。在制作滑动控件时,如果控件有很多不在屏幕内的子视图,会非常有用。
要让此属性生效,首先要求视图有很多超出范围的子视图,并且子视图和容器视图(或它的某个祖先视图)都应该有样式 overflow: hidden。

附上实现的 SceneContainer 源码

import React from 'react'
import { View, StyleSheet, ViewPropTypes } from 'react-native'
import PropTypes from 'prop-types'

function SceneContainer({ selected, children, ...props }) {
  return (
    <View
      {...props}
      pointerEvents={selected ? 'auto' : 'none'}
      removeClippedSubviews={!selected}
      style={[
        styles.sceneContainer,
        selected ? null : styles.hiddenSceneContainer,
        props.style,
      ]}
    >
      <StaticContainer shouldUpdate={selected}>
        {children}
      </StaticContainer>
    </View>
  )
}

SceneContainer.propTypes = {
  ...ViewPropTypes,
  selected: PropTypes.bool,
}
SceneContainer.defaultProps = {
  selected: false,
}

class StaticContainer extends React.Component {
  shouldComponentUpdate(nextProps) {
    return !!nextProps.shouldUpdate
  }

  render() {
    let { children } = this.props
    return children ? React.Children.only(children) : null
  }
}

StaticContainer.propTypes = {
  shouldUpdate: PropTypes.bool,
}

StaticContainer.defaultProps = {
  shouldUpdate: false,
}

let styles = StyleSheet.create({
  sceneContainer: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  hiddenSceneContainer: {
    overflow: 'hidden',
    opacity: 0,
  },
})

export default SceneContainer

相关文章

网友评论

      本文标题:React Native 如何优化导航类型组件的切换卡顿问题

      本文链接:https://www.haomeiwen.com/subject/gwthnktx.html