React Native 组件性能优化

前言

影响性能最大的因素是界面的重绘和重排版,React 背后的 Virtual DOM 就是尽可能地减少重绘与重排版。
我们需要提高 React Virtual DOM 的效率,从 React 渲染过程看,如何防止不必要的渲染可能是最需要取解决的问题。针对这个问题,React 官方提供了一个便捷的方法来解决,那就是 PureRender。

纯函数

要理解 PureRender 中的 Pure,要从函数式编程的的基本概念 "纯函数"讲起。纯函数由三大原则构成。

  • 给定相同的输入,他总是返回相同的输出

对于一个方法,只要我们传入的值是固定,无论做多少次调用,结果都是一样。有些方法不依赖于你传入的参数,如 Math.random(),不传任何参数到方法中,该方法依然总是会输出不同的结果,这种方法也是纯函数。还有常用的 slice 和 splice 方法,slice在参数一致的时候结果都是一致的,splice方法的执行结果会改变原来数组,对于程序来说,splice 的隐藏行为是危险的,所以 splice 不是纯函数。

  • 过程没有副作用

在纯函数中我们不能改变外部状态。

  • 没有额外的状态依赖

方法内的状态都只在方法的生命周期内存活,意味着不能在方法内使用共享变量,这会给方法带来不可知因素。

纯函数也是函数式编程的基础,他完全独立于外部状态,这样就避免了因为共享外部状态而导致的 bug。
纯函数非常方便方法级别的测试以及重构,可以让程序具有良好的扩展性及适应性。
React 就是函数式编程,React 组件本身就是纯函数,可以表示为 UI = f(data),data 包括 props 和 state,改变 data,就能改变 UI,而不是像 JQuery 一样直接操纵 UI。
可以通过拆分组件为自组件,进而对组件做更小颗粒度的控制。这是函数式编程的魅力之一,保持纯净状态,让方法或组件更加专注 (focused),体积更小 (small),更独立(independent),更具有复用性 (reustability)。

PureRender

pureRender 指的就是组件满足纯函数条件,即组件的渲染是被相同的 props 和 state 渲染进而得到相同的结果。

1. PureRender 的本质

React 生命周期中有一个 shouldComponentUpdate 方法,如果返回 true,表示需要渲染,返回 false 表示不需要渲染。

2. 运用 PureRender

React 新版本提供一个 PureComponent,PureComponent 内部重新实现 shouldComponentUpdate 方法,让当前传入的 props 和 state 与之前的作浅比较,,浅比较对 object 只作了引用比较,并没有做值比较,如果返回 false,那么组件就不会执行 render 方法。

3. 优化 PureRender

  • 直接为props设置对象或数组

每次调用 React 组件其实都会重新创建组件,就算传入的数组或对象的值没有改变,它们引用的地址也会发生改变。

<TestComponent 
    style={{background: 'white'}}
/>

我们可以把传入的数组或对象保存成一份引用。

<TestComponent 
    style={styles.container}
/>
const styles = StyleSheet.create({
    container: {
        background: 'white'
    },
});
  • 设置 props 方法并通过事件绑定在元素上
onPress() {
}
<TestComponent 
    onPress={this.onPress.bind(this)}
/>

这样写,每一次渲染都会重新绑定 onPress方法, 不要让方法每一次都绑定,因此把绑定移动到构造器内。

constructor(props) {
    super(props);
    this.onPress = this.onPress.bind(this);
}

onPress() {
}

render() {
    <TestComponent 
        onPress={this.onPress}
    />
}

Immutable

在传递的数据的时候,可以直接使用 Immutable Data 来进一步提高组件的渲染性能。

1. Immutable Data

Immutable Data 就是一旦创建,就不能再更改的数据。对 Immutable 对象进行修改,添加或者删除操作,都会返回一个新的 Immutable 对象。Immutable 实现的原理是持久化的数据结构,也就是使用旧数据创建新数据,要保证就数据同时可用切不变。同时为了避免深拷贝把所有节点都复制一遍带来的性能损耗,Immtable 使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受他影响的父节点,其他节点进行共享。

  • Map:键值对集合,对应于Object。
  • List:有序可重复的列表,对应于Array。
  • ArraySet:无序切不可重复的列表。

2. Immutable 的优点

  • 降低了 "可变" 带来的复杂度。
  • 节省内存。Immutable 使用结构共享尽量复用内存。没有被引用的对象会被垃圾回收。
  • 撤销/重做,复制/粘帖,甚至时间旅行这些功能做起来都变得简单。因为每次数据都是不一样的,那么只要把这些数据都放到一个数组里存储起来,想回退到哪里,就拿出对应的数据。
  • 并发安全。Immutable 数据天生不可变,但 js 是单线程运行,现在并没有什么用。
  • 拥抱函数式编程。只要输入一致,那么输出必然一致,这样开发的组件更易于调试和组装。

3. 使用 Immutable 的缺点

容易与原生对象混淆。

4. Immutable.is

为了直接比较对象的值,Immutable提供了Immutable.is 来作"值比较"

const map1 = Immutable.Map({a: 1, b: 1})
const map2 = Immutable.Map({a: 1, b: 1})
map1 === map2; // => false
Immutable.is(map1, map2); // => true

Immutable.is比较的是两个对象的hashCode 或 valueOf (对于JS 对象),由于Immutable 内部使用 trie 数据结构来存储,只要两个对象 hashCode 相等,值就是一样的。

5. Immutable 和 PureRender

React 做性能优化最常用的就是 shouldComponentUpdate 方法,但他默认返回true,即始终会执行 render 方法,然后做 Virtual DOM比较,并得出是否需要做真实 DOM 更新,这里往往带来不必要的渲染,
当然,我们可以在 shouldComponentUpdate 中使用深拷贝和深比较来避免无必要的 render,但是深拷贝和深比较一般都是非常昂贵的选择。
Immutable.js 提供了简洁,高效的判断数据是否变化的方法,只需要 is 比较就能知道是否需要执行 render,而这个操作几乎零成本,所以可以极大提高性能。修改后的 shouldComponentUpdate 是这样的。

import React, {
    Component
} from 'react';
import Immutable from 'immutable';

class BaseComponent extends Component {

    shouldComponentUpdate(nextProps, nextState = {}) {
        return !Immutable.is(Immutable.fromJS(this.props), Immutable.fromJS(nextProps))
        || !Immutable.is(Immutable.fromJS(this.state), Immutable.fromJS(nextState));
    }
}

export default BaseComponent;

而在自定义组件你只需要继承BaseComponent,就可以拦截不必要的渲染。

class App extends BaseComponent {

}

6. key

写动态组件的时候,需要给动态子组件添加 key props,否则会报一个警告。Key 要保持惟一,稳定。
key 的作用是为了 Virtual DOM 在执行 diff 算法时更快地找到对应的节点,提高 diff 速度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 说在前面 关于 react 的总结过去半年就一直碎碎念着要搞起来,各(wo)种(tai)原(lan)因(le)。心...
    陈嘻嘻啊阅读 6,840评论 7 41
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,794评论 0 24
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 8,026评论 2 35
  • 1. 前言 在 React 中,一切皆是组件,因此理解组件的工作流与核心尤为重要。我们有多种创建组件的方式(不仅 ...
    cbw100阅读 3,623评论 0 11
  • 这几天应该是我过的最充实的几天,睁开眼睛就是签到,每天的最后一分钟都还在聊客户,辛苦我的手机了!加油加油!
    爱FAB小丹阅读 139评论 0 0