react-native tips

react-native tips

目前flutter当红, react-native显得有些迟暮.
在学flutter过程中, 发现很多思想跟rn都是相通的, 比如:

  • widget虚拟dom.
  • flutter context/elementref.
  • RenderObjectnative UI.
  • key控制更新或重建的作用. GlobalKey 可以跨组件传值, 类似于rn的key + ref.
  • 相通的布局样式.
  • redux.

毕竟断断续续做了几年RN, 还是记录下一些不曾总结过的tips, 免得学着新的忘了旧的.

tips

  • 1 组件继承于PureComponent或自己重写shouldUpdate方法; 组件props或state, 能用常量时用常量. 比如:

    // 全局定义
    const BlankObject = Object.freeze({});
    const BlankArray = Object.freeze([]);
    
    // 传给子一级的函数, 需要bind this, 提前bind好
    // 在contructor里bind
    constructor(props) {
        super(props);
        this.onXXXComplete = this.onXXXComplete.bind(this);
    }
    // 或, 直接像下面这样定义
    onXXXComplete = () => {
        // ...
    }
    
  • 2 组件内, 需要显示的value, 可以先放到this下, 而非state中, 每次值变化时, 判断是否需要更新. 当然, 也可以在shouldUpdate生命周期函数里做剔除. 例如:

class ComponentA extends PureComponent {

    $valueObj = {};  // {key1, key2, key3, ...}
    $valueToShowObj = {};  // {key1, key2, key3, ...}
    
    set valueObj({key1, key2, key3, ...}) {
        const {key1: oldKey1, key2: oldKey2, key3: oldKey3, ...} = this.$valueObj;
        let needUpdate = false;
        if (key1 !== oldKey1) {
            this.$valueToShowObj[key1] = key1;
            needUpdate = true;
        }
        
        if (key2 !== oldKey2) {
            this.$valueToShowObj[key2] = key2;
            needUpdate = true;
        }
        
        if (key3 !== oldKey3) {
            this.$valueToShowObj[key3] = key3;
            needUpdate = true;
        }
        
        ...
        
        if (needUpdate) {
            this.setState(this.$valueToShowObj);
        }
    }
}
  • 3 列表, 或同一级同类型兄弟组件比较多时, 如果有动态更新需求, 比如增/删/交换位置, 使用key值, 来提高diff效率, 降低不必要的销毁重建.
  • 4 如果有跨层的UI组件操作, 对应组件重建代价又比较高, 或牵连范围比较广, 就要考虑用绝对布局了, 目的就是把视图结构flatten了, diff特性才能有效的发挥. 比如, 使用FlatList做多列展示, 有增加/删除操作, 因为FlatList是按行排布, 每一行内是分列的, 操作时就有了item的跨行迁移, 基本上全局都会丢弃重建. 如果item带图片, 页面表现就十分感人了.
  • 5 UI封装动静分离, 粒度足够小. 静态组件尽量用函数而非class, 比较轻量. 另外, 父组件的子组件比较多又比较重, 父组件又要操作某个子组件时, 用拿到子的ref去操作, 或者也可以走消息通知.
  • 6 如果组件有特别频繁的位置/外形变化, 比如关联scollView的滚动, 使用AnimatesetNativeProps, 不要使用state, 不然性能基本都耗在diff上了. 方案如下:
/* animate */
height = new Animated.Value(h);


// 修改height
this.height.setValue(newH);

/* setNativeProps */
// 修改width
this.$refView.setNativeProps({ style: { width: newW } });
  • 7 如果UI组件在屏幕之外, 在数据变化时最好屏蔽掉这部分UI的刷新. 对于不需要立即显示, 而且还比较重的组件, 可以懒加载. 比如新闻类的多列表页面, 先加载当前要显示的, 其他的用空view占位, 即将进入屏幕时替换为真正的视图. 节省cpu时间, 内存占用和用户流量. 有必要时做好最大并存数限制, 避免内存累计占用过大. 实现这个效果, 一个很简单的方案就是, 封装一个容器view:
class UpdateContainer extends PureComponent {
    shouldComponentUpdate(nextProps, nextState) {
        return nextProps.shouldUpdate || (nextProps.shouldMount === false && this.props.shouldMount === true);
    }

    render() {
        const { shouldMount, style, children } = this.props;
        if (children === null || children === false) {
            return null;
        }
        if (shouldMount) {
            React.Child.only(children);
        }
        // 占位空view
        return ;
    }
}
  • 9 redux, connect到UI组件时, 如果有比较重的数据运算, 可以加缓存, 避免其他节点更新引起的重复运算, 类似的框架有reselect. 思想比较简单, 也可以自己实现.
  • 10 redux store尽量不要存储页面状态数据, 或者不需要跨组件保持一致性的数据. 如果有些页面状态需要共享, 可以用消息, 也可以用context; 还可以自定义一个状态管理类, 维护状态和相关组件的引用的列表, 当状态变化时遍历列表调用setState 或 forceUpdate.
  • 11 图片使用跟视图匹配的size. 静态图问UI要. 网络图, 很多图片云服务都支持设置裁剪/填充参数, 可以按需配置. 另外, RN内部是支持用assertId读取相册图片的, 所以可以用Photos.framework去读相册, 用的时候urlph://前缀, 如ph://23123.
  • 12 视图层级不要太深.
  • 13 scrollView的滚动和内部view的拖动类手势冲突的时候, 可以在手势识别时scrollEnabled = false, 结束时scrollEnabled = true, 不过是用setNativeProps来设置, 而非setState. 如果是原生自定义的子视图,也可以添加原生gesture来进行手势管理。

总结

目前就想起这么多, 很多注意的点都内化了, 遇到时知道怎么做更好, 但乍一想记不起来. 一直在学新的技术, 没有停下来总结, 这点很不好, 慢慢改正.
总之, 就是需要在减少重复运算, 重复刷新上多作留意. diff虽然较为高效, 但dom树的比较还是很耗性能的, 能简单比较就排除的刷新, 就不要留给diff去判断.
最后, 虽然使用的是RN, 但要认清视图依然是原生的, 不要被js局限住, 有些需求是可以直接通过在原生拿到视图来达成的.

你可能感兴趣的:(react-native tips)