react-native組件避免重復(fù)渲染

react-native若有性能問題蒂誉,很可能是由于組件的重復(fù)渲染扰路,研究下相關(guān)知識點醉锅。

export default class Home1 extends Component {
    render() {
        console.log('渲染根');
        return (
            <View style={{backgroundColor: 'white', marginTop: 100}}>
                <ComponentA/>
                <ComponentB/>
            </View>
        );
    }
}
class ComponentA extends Component {
    render() {
        console.log('渲染A');
        return (
            <Text>組件A</Text>
        )
    }
}
class ComponentB extends Component {
    render() {
        console.log('渲染B');
        return (
            <View>
                <Text>組件B</Text>
                <ComponentC/>
            </View>
        )
    }
}
class ComponentC extends Component {
    render() {
        console.log('渲染C');
        return (
            <View>
                <Text>組件C</Text>
                <ComponentD/>
            </View>
        )
    }
}
class ComponentD extends Component {
    render() {
        console.log('渲染D');
        return (
            <View>
                <Text>組件D</Text>
            </View>
        )
    }
}

組件關(guān)系圖如下:


測試現(xiàn)象:
若A被渲染肌括,則C轧抗、D也會跟著被渲染匿刮,其他不變狼牺。
若根被渲染峰档,所有組件都重新渲染禁添。
若B被渲染撮胧,其他組件不變。
結(jié)論:某一組件被render老翘,只會導(dǎo)致本身和所有的子組件被重新render芹啥,其他的沒有影響锻离。

問題:例如當A被渲染時,C叁征、D的數(shù)據(jù)并未改變纳账,甚至C、D根本就是無狀態(tài)無屬性組件捺疼,但它們也被重新渲染了疏虫,浪費性能。
組件是否重新渲染的決定因素是組件的生命周期方法shouldComponentUpdate的返回值啤呼,而Component組件該方法的默認實現(xiàn)返回值總是true卧秘,所以會被重新渲染」倏郏可以重寫該方法讓其根據(jù)業(yè)務(wù)需求判斷是否返回true來決定是否刷新組件翅敌。但是每個組件都重寫該方法很麻煩。
PureComponent組件惕蹄,繼承自Component蚯涮,已經(jīng)實現(xiàn)了shouldComponentUpdate,大概實現(xiàn)如下

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

shallowEqual實現(xiàn)在"node_modules/fbjs/lib/shallowEqual"處卖陵,高效判斷兩個對象是否相等遭顶,從而決定是否渲染頁面。
將上面所有組件的父類改為PureComponent泪蔫,再次測試棒旗,發(fā)現(xiàn)當A被渲染時,C撩荣、D也不會被渲染了铣揉,性能肯定變好。

此法雖好餐曹,但也有不少副作用逛拱,比如將B組件的實現(xiàn)改為如下

class ComponentB extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            num: 4,
            arr: [1,2,3]
        };
    }
    componentDidMount() {
        setInterval(() => {
            this.state.arr.push(this.state.num);
            this.setState({
                num: this.state.num + 1,
                arr: this.state.arr
            })
        }, 2000)
    }
    render() {
        console.log('渲染B');
        return (
            <View>
                <ComponentC arr={this.state.arr}/>
                <Text>{`組件B: ${this.state.num}`}</Text>
            </View>
        )
    }
}

開個定時器,不斷改變arr的值台猴,組件C的屬性發(fā)生了變化橘券,但是由于C組件的shouldComponentUpdate判斷方法shallowEqual只能對對象做淺比較,也就是判斷對象的地址是否一致卿吐,這里數(shù)組本身地址并未發(fā)生變化,僅僅是內(nèi)容有所變化锋华,該方法鑒別不出來嗡官,返回的是false,頁面不會重新被渲染毯焕,有大問題衍腥。

這里的解決方案有好些:
a磺樱、在C組件中重寫shouldComponentUpdate,判斷arr內(nèi)容是否變化婆咸,決定重新渲染竹捉。
b、B組件給C組件傳遞屬性前尚骄,將arr對象進行深拷貝(有性能問題)块差,重新生成對象
c、利用不可變對象倔丈,我這里用的是輕量級的seamless-immutable

不可變對象有諸多優(yōu)點就不說了憨闰,總之很好很強大,性能提升利器需五。使用之后B組件代碼如下

class ComponentB extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            num: 0,
            arr: Immutable([1,2,3])
        };
    }
    componentDidMount() {
        setInterval(() => {
            let arr = Immutable.asMutable(this.state.arr);
            arr.push(5);
            let newArr = Immutable(arr);
            this.setState({
                num: this.state.num + 1,
                arr: newArr
            })
        }, 2000)
    }
    render() {
        console.log('渲染B');
        return (
            <View>
                <ComponentC arr={this.state.arr}/>
                <Text>{`組件B: ${this.state.num}`}</Text>
            </View>
        )
    }
}

使用就三步:
1鹉动、導(dǎo)入頭文件,import Immutable from 'seamless-immutable'
2、數(shù)組或?qū)ο蟪跏蓟瘯r使用如Immutable([1,2,3])的方式
3宏邮、改變數(shù)組或?qū)ο髸r使用專門的api泽示,比如Immutable.asMutable、Immutable.flatMap

有些組件擁有繼承關(guān)系蜜氨,但是頂層父類又是繼承的Component械筛,這時可以采用pure-render-decorator,給該組件添加一個裝飾器擴展方法shouldComponentUpdate记劝,這個效果跟PureComponent基本一致变姨。

import pureRenderDecorator from 'pure-render-decorator';
@pureRenderDecorator
class ComponentA extends PureComponent {

還有個小問題,上面B組件傳遞到C組件的屬性arr厌丑,在C組件中并沒有在render方法中被使用定欧,理論上該組件是不需要不斷渲染的,但是因為shouldComponentUpdate方法返回true所以會反復(fù)渲染怒竿。當然既然B組件傳遞了屬性arr給C砍鸠,那么實際開發(fā)中arr肯定是必不可少的。我要說的是耕驰,如果在開發(fā)中C組件拿到arr不是為了渲染更新頁面的目的爷辱,而是其他的比如統(tǒng)計之類的跟頁面渲染無關(guān)的事,那么朦肘,還是需要自己重寫shouldComponentUpdate饭弓,避免不必要的渲染發(fā)生。

接下來簡單說下seamless-immutable中數(shù)組的實現(xiàn)方式:
Immutable([1,2,3])媒抠,會調(diào)用到下面這些方法

function makeImmutableArray(array) {    // 方法A
    for (var index in nonMutatingArrayMethods) {
        if (nonMutatingArrayMethods.hasOwnProperty(index)) {
            var methodName = nonMutatingArrayMethods[index];
            makeMethodReturnImmutable(array, methodName);       // 方法B
        }
    }
    // 給數(shù)組添加上flatMap弟断、asObject等一系列自定義方法
    if (!globalConfig.use_static) {
        addPropertyTo(array, "flatMap", flatMap);
        addPropertyTo(array, "asObject", asObject);
        addPropertyTo(array, "asMutable", asMutableArray);
        addPropertyTo(array, "set", arraySet);
        addPropertyTo(array, "setIn", arraySetIn);
        addPropertyTo(array, "update", update);
        addPropertyTo(array, "updateIn", updateIn);
        addPropertyTo(array, "getIn", getIn);
    }
    // 讓數(shù)組中的每一項都變?yōu)椴豢勺儗ο?    for (var i = 0, length = array.length; i < length; i++) {
        array[i] = Immutable(array[i]);
    }
    return makeImmutable(array, mutatingArrayMethods);  // 方法C
}

function makeMethodReturnImmutable(obj, methodName) {    // 方法B實現(xiàn)
    var currentMethod = obj[methodName];
    Object.defineProperty(obj, methodName, {
        enumerable: false,
        configurable: false,
        writable: false,
        value: Immutable(currentMethod.apply(obj, arguments))
    })
}

function makeImmutable(obj, bannedMethods) {    // 方法C實現(xiàn)
    for (var index in bannedMethods) {
        if (bannedMethods.hasOwnProperty(index)) {
            banProperty(obj, bannedMethods[index]);
        }
    }
    Object.freeze(obj);
    return obj;
}

B方法:
參數(shù)obj就是數(shù)組對象,methodName為"map", "filter", "slice", "concat", "reduce", "reduceRight等趴生。Object.defineProperty為數(shù)組重定義屬性和方法阀趴,writable為false變?yōu)椴豢蓪懟韬玻摲椒ǖ哪康木褪亲寯?shù)組的這些方法失去作用,外界調(diào)用不可變數(shù)組的map刘急、concat等方法后不再有效棚菊。

C方法:bannedMethods為"push", "pop", "sort", "splice", "shift", "unshift", "reverse"等,banProperty方法的實現(xiàn)也是用Object.defineProperty實現(xiàn)叔汁,作用是外界調(diào)用不可變數(shù)組的push统求、pop等方法時直接報錯。最后Object.freeze(obj)凍結(jié)整個數(shù)組對象攻柠,讓其本身無法被修改球订,變?yōu)椴豢勺儗ο蟆?/p>

A方法:包含B、C瑰钮,并且給數(shù)組添加上自定義的很多方法如遍歷flatMap冒滩、轉(zhuǎn)換為普通數(shù)組asMutable等。array[i] = Immutable(array[i])使數(shù)組內(nèi)部的每一個成員都變?yōu)椴豢勺儗ο罄饲矗谶@里开睡,若數(shù)組內(nèi)部元素又是個數(shù)組,則會遞歸到該方法再次執(zhí)行苟耻,直到數(shù)組內(nèi)部所有對象都變?yōu)榱瞬豢勺儗ο笃悖緮?shù)據(jù)類型不可變對象就是本身。

seamless-immutable使用Object.defineProperty擴展了JavaScript 的Array和Object對象來實現(xiàn)凶杖,只支持 Array和Object兩種數(shù)據(jù)類型胁艰,API 基于與 Array 和 Object ,因此許多不用改變自己的使用習慣智蝠,對代碼的入侵非常小腾么。同時,它的代碼庫也非常小杈湾,壓縮后下載只有 2K解虱。相比于笨重的教科書級別的Immutable無疑更適用于react-native。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末漆撞,一起剝皮案震驚了整個濱河市殴泰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浮驳,老刑警劉巖悍汛,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異至会,居然都是意外死亡员凝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門奋献,熙熙樓的掌柜王于貴愁眉苦臉地迎上來健霹,“玉大人,你說我怎么就攤上這事瓶蚂√锹瘢” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵窃这,是天一觀的道長瞳别。 經(jīng)常有香客問我,道長杭攻,這世上最難降的妖魔是什么祟敛? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮兆解,結(jié)果婚禮上馆铁,老公的妹妹穿的比我還像新娘。我一直安慰自己锅睛,他們只是感情好埠巨,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著现拒,像睡著了一般辣垒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上印蔬,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天勋桶,我揣著相機與錄音,去河邊找鬼侥猬。 笑死例驹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的陵究。 我是一名探鬼主播眠饮,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铜邮!你這毒婦竟也來了仪召?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤松蒜,失蹤者是張志新(化名)和其女友劉穎扔茅,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秸苗,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡召娜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惊楼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玖瘸。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秸讹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雅倒,到底是詐尸還是另有隱情璃诀,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布蔑匣,位于F島的核電站劣欢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏裁良。R本人自食惡果不足惜凿将,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望价脾。 院中可真熱鬧牧抵,春花似錦、人聲如沸彼棍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽座硕。三九已至弛作,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間华匾,已是汗流浹背映琳。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜘拉,地道東北人萨西。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像旭旭,于是被迫代替她去往敵國和親谎脯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • React對重排和重繪的提高雅虎性能優(yōu)化比較重要的點持寄,老司機自行忽略源梭。如下圖,HTML被瀏覽器解析為DOM樹稍味,CS...
    HT_Jonson閱讀 1,658評論 0 49
  • react 基本概念解析 react 的組件聲明周期 react 高階組件废麻,context, redux 等高級...
    南航閱讀 1,068評論 0 1
  • 3. JSX JSX是對JavaScript語言的一個擴展語法, 用于生產(chǎn)React“元素”模庐,建議在描述UI的時候...
    pixels閱讀 2,823評論 0 24
  • 原教程內(nèi)容詳見精益 React 學習指南烛愧,這只是我在學習過程中的一些閱讀筆記,個人覺得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,833評論 1 18
  • 昨天明明還艷陽高照 今夜卻突然滂沱大雨 生活不就是這樣嗎 平平坦坦的卻突然要絆你一腳 讓你體會一下生活的跌宕起伏 ...
    長生月閱讀 350評論 0 1