使用immutable和react-immutable-render-mixin优化React Native视图渲染

原创文章转载请注明出处

想知道React Native是什么?先移步官网。另外,本文部分内容参考了搞定immutable.js

熟悉React.js的都应该知道,React.js是一个UI = f(states)的框架,为了解决更新的问题,React.js使用了virtual dom,virtual dom通过diff修改dom,来实现高效的dom更新。听起来很完美吧,但是有一个问题。当state更新时,如果数据没变,你也会去做virtual dom的diff,这就产生了浪费,可以参考flummox这篇文章

React Native的视图刷新机制和React一脉相承,官方文档中抛出了PureRenderMixin,因为PureRenderMixin只是简单的浅比较,不适用于多层比较,所以对于一些特殊情况解决不了问题,而react-immutable-render-mixin则可以直接比较immutable对象的hashcode,既可以进行深层次比较,又大大的提高了比较速度。另外,关于immutable.js的使用方法,这里不赘述,可以自行查阅官方文档

直接上代码了。

package.json引入两个包

"immutable": "^3.8.1",
"react-immutable-render-mixin": "^0.9.7",

创建3个组件,ViewOne、ViewTwo、ViewThree。

/**
 * ViewOne
 * @flow
 */

import React, {Component} from 'react';
import {Text, View} from 'react-native';
import { shallowEqualImmutable } from 'react-immutable-render-mixin';

import * as StyleSheet from 'MallStyleSheet';

export default class ViewOne extends Component {

    // 构造
    constructor(props) {
        super(props);
    }

    shouldComponentUpdate(nextProps, nextState) {
        return !shallowEqualImmutable(this.props, nextProps)
            || !shallowEqualImmutable(this.state, nextState);
    }

    render() {
        console.log('render in ViewOne');
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    {this.props.content}
                </Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
        justifyContent: 'center',
        alignItems: 'center'
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10
    }
});
/**
 * ViewTwo
 * @flow
 */

import React, {Component} from 'react';
import {Text, View} from 'react-native';
import { shallowEqualImmutable } from 'react-immutable-render-mixin';

import * as StyleSheet from 'MallStyleSheet';

export default class ViewTwo extends Component {

    // 构造
    constructor(props) {
        super(props);
    }

    shouldComponentUpdate(nextProps, nextState) {
        return !shallowEqualImmutable(this.props, nextProps)
            || !shallowEqualImmutable(this.state, nextState);
    }

    render() {
        console.log('render in ViewTwo');
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    {this.props.content.name}
                </Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
        justifyContent: 'center',
        alignItems: 'center'
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10
    }
});
/**
 * ViewThree
 * @flow
 */

import React, {Component} from 'react';
import {Text, View} from 'react-native';
import { shallowEqualImmutable } from 'react-immutable-render-mixin';

import * as StyleSheet from 'MallStyleSheet';

export default class ViewThree extends Component {

    // 构造
    constructor(props) {
        super(props);
    }

    shouldComponentUpdate(nextProps, nextState) {
        return !shallowEqualImmutable(this.props, nextProps)
            || !shallowEqualImmutable(this.state, nextState);
    }

    render() {
        console.log('render in ViewThree');
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>
                    {this.props.content.get('name')}
                </Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
        justifyContent: 'center',
        alignItems: 'center'
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10
    }
});

在render()函数打印了log,组件只是渲染了文本。
把组件插入到主界面。

/**
 * NextScreen
 * @flow
 */

import React, {Component} from 'react';
import {View} from 'react-native';
import Button from 'react-native-button';
import {Map} from 'immutable';

import * as StyleSheet from 'MallStyleSheet';
import {BlueNavBar} from 'MallNavBar';
import ViewOne from './ViewOne';
import ViewTwo from './ViewTwo';
import ViewThree from './ViewThree';

export default class NextScreen extends Component {

    // 构造
    constructor(props) {
        super(props);
        this.state = {
            buttonTitle: '修改',
            viewOne: 'ViewOne',
            viewTwo: {name: 'ViewTwo'},
            viewThree: Map({name: 'ViewThree'})
        };
        this._changState = this._changState.bind(this);
        this._resetState = this._resetState.bind(this);
    }

    _changState() {
        // 由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 `hashCode` 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。
        this.setState({
            buttonTitle: '再次修改',
            viewOne: 'ViewOne changed!',
            viewTwo: {name: 'ViewTwo changed'},
            viewThree: this.state.viewThree.set('name', 'ViewThree changed')
        });
    }

    _resetState() {
        this.setState({
            viewOne: 'ViewOne',
            viewTwo: {name: 'ViewTwo'},
            viewThree: this.state.viewThree.set('name', 'ViewThree')
        });
    }

    render() {
        return (
            <View style={styles.container}>
                <BlueNavBar title={this.props.title}/>
                <View style={styles.content}>
                    <Button containerStyle={styles.touch}
                            style={styles.textEnter}
                            onPress={() => {this._changState();}}>
                            {this.state.buttonTitle}
                    </Button>
                    <Button containerStyle={styles.touch}
                            style={styles.textEnter}
                            onPress={() => {this._resetState();}}>
                            恢复
                    </Button>
                    <ViewOne content={this.state.viewOne}/>
                    <ViewTwo content={this.state.viewTwo}/>
                    <ViewThree content={this.state.viewThree}/>
                </View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#F5FCFF'
    },
    content: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    touch: {
        width: 90,
        height:32,
        marginTop: 10,
        alignItems: 'center',
        justifyContent: 'center',
        borderRadius:3,
        backgroundColor:'purple'
    },
    textEnter: {
        fontSize: 16,
        color: 'white'
    }
});

运行结果
初次渲染的时候,三个子组件都渲染了。

Paste_Image.png

点击修改按钮,因为三个子组件的props都变了,一样重新做了渲染。
Paste_Image.png

点击再次修改按钮,通过比较只有组件2的props发生了变化,故而只渲染了ViewTwo,就算点击n次再次修改,也只会重新渲染ViewTwo。
Paste_Image.png

从代码中我们可以看到,ViewOne的props是字符串,因此浅比较可以对比出再次修改以后内容没有发生改变。ViewTwo的props是对象,浅比较只会比较到content的内容指向的对象,因为对象发生了改变,所以ViewTwo会重新渲染,尽管新对象的key和value值都没有变化。ViewThree使用了immutable对象作为props,基于hashcode的对比可以从深层次比较两个对象内容是否一致,从而快速准确的判断是否需要重新渲染。

我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。

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

推荐阅读更多精彩内容