成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

記錄一次利用Timeline Performance工具進(jìn)行 React性能優(yōu)化的真實(shí)案例

jsyzchen / 3499人閱讀

摘要:出現(xiàn)紅幀表示頁(yè)面已經(jīng)超負(fù)荷,會(huì)出現(xiàn)卡頓,響應(yīng)緩慢等現(xiàn)象。因此當(dāng)滑動(dòng)周日歷時(shí)已經(jīng)不會(huì)有紅幀發(fā)生了。我的目的是每一次遞歸會(huì)調(diào)用一次與但是這樣寫(xiě)只會(huì)在遞歸結(jié)束時(shí)調(diào)用一次因此修改如下這樣優(yōu)化之后,發(fā)現(xiàn)內(nèi)存占用下降一些,但是紅幀仍然存在。

性能優(yōu)化可以說(shuō)是衡量一個(gè)前端程序員react使用水平的重要標(biāo)準(zhǔn)。

在學(xué)習(xí)react之初的時(shí)候,由于對(duì)react不夠了解,寫(xiě)的項(xiàng)目雖然功能都實(shí)現(xiàn)了,但是性能優(yōu)化方面的考慮卻做得很少,因此回過(guò)頭來(lái)發(fā)現(xiàn)自己以前寫(xiě)的react代碼確實(shí)有點(diǎn)糟糕。

為了提高自己的react水平,閑暇之余就把以前的老項(xiàng)目拿出來(lái)分析優(yōu)化,看看都有哪些問(wèn)題。這里就以我以前做過(guò)的一個(gè)《投資日歷》為例做一次優(yōu)化記錄。

項(xiàng)目線上地址:https://www.itiger.com/activi...

優(yōu)化工具timeline/performance基礎(chǔ)使用教程:
https://developers.google.com...

chrome在版本57還是58的時(shí)候,將Timeline更名為performance

該項(xiàng)目主要的難點(diǎn)與性能瓶頸在于日歷的左右滑動(dòng)與切換。由于需求定制程度非常高,沒(méi)有合適的第三方日歷插件,所以就自己實(shí)現(xiàn)了一個(gè)。支持周日歷與月日歷的切換,支持左右滑動(dòng)切換日期。

滑動(dòng)效果僅支持移動(dòng)端

問(wèn)題出現(xiàn)在公司一款老的android測(cè)試機(jī),發(fā)現(xiàn)動(dòng)畫(huà)效果非??D。因此有了優(yōu)化的必要。

利用工具定位問(wèn)題

首先利用performance工具的的錄制功能錄制一段操作過(guò)程。
點(diǎn)擊左上角的黑色原點(diǎn)開(kāi)始錄制。錄制過(guò)程中,多次滑動(dòng)周日歷即可。然后大約5~10秒點(diǎn)擊stop按鈕停止錄制。

錄制結(jié)果如圖。

從上圖中我們可以發(fā)現(xiàn)以下問(wèn)題:

1、 窗格中出現(xiàn)了紅幀。出現(xiàn)紅幀表示頁(yè)面已經(jīng)超負(fù)荷,會(huì)出現(xiàn)卡頓,響應(yīng)緩慢等現(xiàn)象。
2、 大量的黃色區(qū)域,黃色區(qū)域越大,表示JavaScript的運(yùn)行過(guò)程中的壓力也越大。
3、 高額的內(nèi)存占用,以及不正常的波動(dòng)曲線(藍(lán)色)。詳細(xì)信息可以在上圖中的JS Heap中查看。26.6 ~ 71.6M。

我們可以在Main中觀察到當(dāng)前時(shí)刻的函數(shù)調(diào)用棧詳情。當(dāng)出現(xiàn)紅幀,選中紅幀區(qū)域,Main區(qū)域發(fā)現(xiàn)變化,變?yōu)楫?dāng)前選擇時(shí)段的函數(shù)調(diào)用棧詳情。我們會(huì)發(fā)現(xiàn)函數(shù)調(diào)用棧最上層有一個(gè)紅色三角形。點(diǎn)擊會(huì)在下面的Summary里發(fā)現(xiàn)對(duì)應(yīng)的信息以及警告。如下圖中的Warning: Recuring handler took 86.69 ms。

4、 層級(jí)很高的函數(shù)調(diào)用棧。查看紅色區(qū)域的函數(shù)調(diào)用棧,我們會(huì)發(fā)現(xiàn)大量的react組件方法被重復(fù)調(diào)用。

一步一步開(kāi)始優(yōu)化

從上面的分析就可以簡(jiǎn)單看出,雖然實(shí)現(xiàn)了非常復(fù)雜的功能,看上去很厲害的樣子,其實(shí)內(nèi)部非常糟糕。幾乎可以作為react用法的反面教材了。

優(yōu)化分析1

在上面的函數(shù)調(diào)用棧中,我們發(fā)現(xiàn)有一個(gè)方法出現(xiàn)的次數(shù)非常多,那就是receiveComponent。因此可以預(yù)想到某個(gè)組件里肯定使用了receiveComponent相關(guān)的生命周期的方法。檢查代碼,確實(shí)發(fā)現(xiàn)了幾處componentWillReceiveProps的使用。

// 每一次更新?tīng)顟B(tài)都會(huì)刷新一次,導(dǎo)致了大量的計(jì)算
componentWillReceiveProps(nextProps) {
    this.setState({
        navProcess: getNavigation(nextProps.currentData)
    })
}

剛開(kāi)始學(xué)習(xí)react時(shí)可能會(huì)認(rèn)為生命周期是一個(gè)學(xué)習(xí)難點(diǎn),我們不知道什么情況下去使用它們。慢慢的隨著經(jīng)驗(yàn)的增加,才發(fā)現(xiàn),生命周期方法是萬(wàn)萬(wàn)不能輕易使用的。特別是與props/state改變,與組件重新渲染相關(guān)的幾個(gè)生命周期,如componentWillReceiveProps, shouldComponentUpdate ,componentWillUpdate等。這個(gè)實(shí)際案例告訴我們,他們的使用,會(huì)造成高額的性能消耗。所以不到萬(wàn)不得已,不要輕易使用他們。

曾經(jīng)看到過(guò)一篇英文博文,分析的是寧愿多幾次render,也不要使用shouldComponentUpdate來(lái)優(yōu)化代碼。但是文章地址找不到,如果有其他看過(guò)的朋友請(qǐng)?jiān)谠u(píng)論里留言分享一下,感謝

而只有componentDidMount是非常常用的。

上面幾行簡(jiǎn)單的代碼,暴露了一個(gè)非??植赖膯?wèn)題。一個(gè)是使用了生命周期componentWillReceiveProps。而另一個(gè)則是在props改變的同時(shí),還修改了組件的state。我們知道當(dāng)props在父級(jí)被改變時(shí)會(huì)造成組件的重新渲染,而組件內(nèi)部的state的改變同樣也會(huì)造成組件的重新渲染,因此這幾句簡(jiǎn)單的代碼,讓組件發(fā)生了很多次冗余的渲染。

因此優(yōu)化的方向就朝這兩個(gè)方向努力。首先不能使用componentWillReceiveProps,其次我發(fā)現(xiàn)navProcess其實(shí)可以在父級(jí)組件中計(jì)算,并通過(guò)props傳遞下來(lái)。所以?xún)?yōu)化后的代碼如下:

function Index(props) {
    const { currentD, currentM, selectD, setDate, loading, error, process, navProcess } = props;
    return (
        
{ loading ? null : error ? : } {loading ? : null}
) } export default withWrapped(Index);

意外的驚喜是發(fā)現(xiàn)該組件最終優(yōu)化成為了一個(gè)無(wú)狀態(tài)組件,輕裝上陣,完美。

這樣優(yōu)化之后,重新渲染的發(fā)生少了好幾倍,運(yùn)行壓力自然減少很多。因此當(dāng)滑動(dòng)周日歷時(shí)已經(jīng)不會(huì)有紅幀發(fā)生了。但是月日歷由于DOM節(jié)點(diǎn)更多,仍然存在問(wèn)題,因此核心的問(wèn)題還不在這里。我們還得繼續(xù)觀察。

優(yōu)化分析2

在函數(shù)調(diào)用棧中我們可以很明顯的看到一個(gè)名為ani的方法。而這個(gè)方法是我自己寫(xiě)的運(yùn)動(dòng)實(shí)現(xiàn)。因此我得重點(diǎn)關(guān)注它的實(shí)現(xiàn)中是不是存在什么問(wèn)題。仔細(xì)瀏覽一遍,果然有問(wèn)題。

發(fā)現(xiàn)在ani方法的回調(diào)中,調(diào)用了2次setDate方法。

// 導(dǎo)致頂層高階組件多一次渲染,下層多很多次渲染
setDate(newCur, 0);
setDate({ year: newCur.year, month: newCur.month }, 1)

該setDate方法是在父級(jí)中定義用來(lái)修改父級(jí)state的方法。他的每一次調(diào)用都會(huì)引發(fā)由上自下的重新渲染,因此多次調(diào)用的代價(jià)是非常大的。所以我將要面臨的優(yōu)化就是想辦法將這兩次調(diào)用合并為一次。

先看看優(yōu)化以前setDate方法的定義是如何實(shí)現(xiàn)的。我想要通過(guò)不同的number來(lái)修改不同的state屬性。但是沒(méi)有考慮如果需要修改多個(gè)呢?

setDate = (date, number) => {
    if (number == 0) {
        this.setState({
            currentD: date,
            currentM: { year: date.year, month: date.month }
        })
    }

    if (number == 1) {
        this.setState({
            currentM: date
        })
    }

    if (number == 2) {
        _date = date;
        _month = { year: date.year, month: date.month };
        this.setState({
            currentD: _date,
            currentM: _month,
            selectD: _date
        })
        this.process(date);
    }
}

修改該方法為,傳遞一個(gè)對(duì)象字面量進(jìn)去進(jìn)行修改

setDate = (options) => {
    const state = { ...this.state, ...options };
    if (options.selectD) {
        _date = options.selectD;
        _month = { year: _date.year, month: _date.month }
        state.currentD = _date;
        state.currentM = _month;
        this.process(_date, state);
    } else {
        this.setState(state);
    }
}

該方法有兩處優(yōu)化,第一處優(yōu)化是傳入的參數(shù)調(diào)整,想要修改那一個(gè)就直接傳入,用法類(lèi)似setState。第二處優(yōu)化是在this.process方法中只調(diào)用一次this.setState,總之這樣處理的目的都是統(tǒng)一的,當(dāng)想要數(shù)據(jù)修改時(shí)只發(fā)生一次渲染。而之前的方法會(huì)導(dǎo)致3次甚至多次渲染。這樣優(yōu)化之后,性能自然會(huì)提升很多。

優(yōu)化分析3

但是優(yōu)化并沒(méi)有結(jié)束,因?yàn)樵黉浿埔欢尾榭?,仍然?huì)發(fā)現(xiàn)紅幀出現(xiàn)。
進(jìn)一步查看Calendar組件,發(fā)現(xiàn)每一次滑動(dòng)切換,都會(huì)發(fā)生4次渲染??隙ㄓ袉?wèn)題。

我的目的是最多發(fā)生兩次無(wú)法避免的渲染。多余的肯定是因?yàn)榇a的問(wèn)題導(dǎo)致的冗余渲染。因此繼續(xù)查看代碼。

發(fā)現(xiàn)在遞歸調(diào)用ani方法時(shí),this.timer并沒(méi)有被及時(shí)取消。

// 我的目的是每一次遞歸會(huì)調(diào)用一次requestAnimationFrame與cancelAnimationFrame
// 但是這樣寫(xiě)只會(huì)在遞歸結(jié)束時(shí)調(diào)用一次cancelAnimationFrame
if (offset == duration) {
    callback && callback();
    cancelAnimationFrame(this.timer);
} else {
    this.timer = requestAnimationFrame(ani);
}

因此修改如下:

ani = () => {
    ....
    if (offset == duration) {
        callback && callback();
    } else {
        this.timer = requestAnimationFrame(ani);
    }
    cancelAnimationFrame(this.timer);
}

這樣優(yōu)化之后,發(fā)現(xiàn)內(nèi)存占用下降一些,但是紅幀仍然存在。看來(lái)計(jì)算量并沒(méi)有下降。繼續(xù)優(yōu)化。

優(yōu)化分析4

發(fā)現(xiàn)Calendar組件中,根據(jù)props中的curDate,curMonth計(jì)算而來(lái)的weekInfo與monthInfo被寫(xiě)在了該組件的state中。由于state中數(shù)據(jù)的變化都會(huì)導(dǎo)致重新渲染,而我發(fā)現(xiàn)在代碼中有多處對(duì)他們進(jìn)行修改。

componentDidMount() {
    const { curDate, curMonth } = this.props

    this.setState({
        weekInfo: calendar.get3WeekInfo(curDate),
        monthInfo: calendar.get3MonthInfo(curMonth)
    })

    this.setMessageType(curDate, 0);
    this.setMessageType(curMonth, 1);
}

其實(shí)這種根據(jù)props中的參數(shù)計(jì)算而來(lái)的數(shù)據(jù)是萬(wàn)萬(wàn)不能寫(xiě)在state中的,因?yàn)閜rops數(shù)據(jù)的變化也會(huì)導(dǎo)致組件刷新重新渲染,因此一個(gè)數(shù)據(jù)變化就會(huì)導(dǎo)致不可控制的多次渲染。這個(gè)時(shí)候更好的方式是直接在render中計(jì)算。因此優(yōu)化如下:

render() {
    ...
    let info = type == 0 ? c.get3WeekInfo(curDate) : c.get3MonthInfo(curMonth);
    ...
}

優(yōu)化結(jié)果如下圖

與第一張圖對(duì)比,我們發(fā)現(xiàn),運(yùn)動(dòng)過(guò)程中出現(xiàn)的紅幀沒(méi)有了。二是窗格中黃色區(qū)域大量減少,表示js的計(jì)算量減少很多。三是內(nèi)存占用大幅降低,從最高的71M減少到了33M。內(nèi)存的增長(zhǎng)也更加平滑。

后續(xù)的優(yōu)化大致目的都是一樣。不再贅述。

最后總結(jié)一下:

盡量避免生命周期方法的使用,特別是與狀態(tài)更新相關(guān)的生命周期,使用時(shí)一定要慎重。

能通過(guò)props重新渲染組件,就不要在額外添加state來(lái)增加渲染壓力。

一切的優(yōu)化方向就是在實(shí)現(xiàn)功能的前提下減少重新渲染的發(fā)生。

這其中涉及到的技巧就需要大家在實(shí)戰(zhàn)中慢慢掌握了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92094.html

相關(guān)文章

  • 2017-07-08 前端日?qǐng)?bào)

    摘要:前端日?qǐng)?bào)精選精讀與提案知乎專(zhuān)欄第期認(rèn)識(shí)引擎記錄一次利用工具進(jìn)行性能優(yōu)化的真實(shí)案例簡(jiǎn)書(shū)中的使用規(guī)則教程繼承的實(shí)現(xiàn)方法個(gè)人文章中文譯組件渲染性能探索個(gè)人文章周刊第期表單性能的改進(jìn)實(shí)踐知乎專(zhuān)欄簡(jiǎn)單可重用的圖表庫(kù)知乎專(zhuān)欄 2017-07-08 前端日?qǐng)?bào) 精選 精讀 TC39 與 ECMAScript 提案 - 知乎專(zhuān)欄【第989期】認(rèn)識(shí) V8 引擎記錄一次利用 Timeline/Perform...

    王巖威 評(píng)論0 收藏0
  • JavaScript 啟動(dòng)性能瓶頸分析與解決方案

    摘要:?jiǎn)?dòng)性能瓶頸分析與解決方案翻譯自的,從屬于筆者的前端入門(mén)與工程實(shí)踐。我們必須要清醒地認(rèn)識(shí)到全面評(píng)測(cè)以挖掘出真正性能瓶頸的重要性。這可能是最佳的方式了,類(lèi)似于這樣的模式鼓勵(lì)基于路由的分組,目前被與廣泛使用。 JavaScript 啟動(dòng)性能瓶頸分析與解決方案 翻譯自 Addy Osmani 的 JavaScript Start-up Performance,從屬于筆者的Web 前端入門(mén)與工...

    SQC 評(píng)論0 收藏0
  • 使用Performance對(duì)頁(yè)面進(jìn)行分析優(yōu)化(實(shí)戰(zhàn)篇)

    摘要:在此,我們可以使用懶加載方式對(duì)其進(jìn)行優(yōu)化,僅展示其對(duì)應(yīng)類(lèi)型的圖,避免了不必要的資源浪費(fèi)和計(jì)算時(shí)間。 這篇文章將介紹下實(shí)際使用performance對(duì)頁(yè)面進(jìn)行優(yōu)化的過(guò)程??偟膩?lái)說(shuō),chrome performance工具讓我們更方便的發(fā)現(xiàn)在代碼運(yùn)行過(guò)程中的問(wèn)題在哪里,便于對(duì)一些可能注意不到的問(wèn)題進(jìn)行定位、分析和優(yōu)化。原文首發(fā)于個(gè)人博客 渲染優(yōu)化 首先,我們對(duì)進(jìn)入整個(gè)詳情頁(yè)進(jìn)行分析,整個(gè)頁(yè)...

    luodongseu 評(píng)論0 收藏0
  • 使用性能API快速分析web前端性能

    摘要:性能時(shí)間線以一個(gè)統(tǒng)一的接口獲取由和所收集的性能數(shù)據(jù)。瀏覽器支持下表列舉了當(dāng)前主流瀏覽器對(duì)性能的支持,其中標(biāo)注星號(hào)的內(nèi)容并非來(lái)自于性能工作小組。 頁(yè)面的性能問(wèn)題一直是產(chǎn)品開(kāi)發(fā)過(guò)程中的重要一環(huán),很多公司也一直在使用各種方式監(jiān)控產(chǎn)品的頁(yè)面性能。從控制臺(tái)工具、Fiddler抓包工具,到使用DOMContentLoaded和document.onreadystatechange這種侵入式j(luò)ava...

    mj 評(píng)論0 收藏0
  • React16性能改善原理一

    摘要:接下來(lái)看下偽代碼調(diào)度算法偽代碼原來(lái)這段寫(xiě)的匆忙且不好,重新更新了一篇講調(diào)度算法的大概實(shí)現(xiàn)性能改善的原理二。 問(wèn)題背景 React16 更新了底層架構(gòu),新架構(gòu)主要解決更新節(jié)點(diǎn)過(guò)多時(shí),頁(yè)碼卡頓的問(wèn)題。譬如如下代碼,根據(jù)用戶(hù)輸入的文字生成10000行數(shù)據(jù),用戶(hù)輸入框會(huì)出現(xiàn)卡頓現(xiàn)象。 class App extends React.Component { constructor( prop...

    zhangqh 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<