性能優(yōu)化啟示錄

為什么要進(jìn)行性能優(yōu)化

  • 57%的?戶更在乎??在3秒內(nèi)是否完成加載

  • 52%的在線?戶認(rèn)為??打開速度影響到他們對(duì)?站的忠實(shí)度

  • 每慢1秒造成?? PV 降低11%,?戶滿意度也隨之降低降低16%

  • 近半數(shù)移動(dòng)?戶因?yàn)樵?0秒內(nèi)仍未打開??從?放棄。

性能優(yōu)化學(xué)徒工

雅?軍規(guī)踐?

  • html數(shù)量控制
    能盡量用CSS解決的就用CSS解決。(陰影阔涉,漸變)

  • 壓縮仆嗦,合并,MD5魂仍,CDN
    接下來請(qǐng)大家思考一個(gè)問題沃但,為什么CDN對(duì)于前端這么重要磁滚?

image.png
  • 還有一個(gè)很重要的點(diǎn)就是離線緩存
    打開谷歌控制臺(tái)的application


    application

localStorage存儲(chǔ)本地?cái)?shù)據(jù)

//需求是什么?
//假設(shè)我們需要請(qǐng)求a.xx3322.js
//在本地存儲(chǔ)localstorage存儲(chǔ)key為a.js,對(duì)應(yīng)的value是a.xx3322.js
//key為a.xx3322.js,對(duì)應(yīng)的value就是我們的目標(biāo)js代碼
//所以就不需要<script src = "a.xx3322.js">
//可以用下面代碼來實(shí)現(xiàn)

//webpack打包出來
var res = {
    "a.js":"a.xx3322.js"
}

function activePage(){
    for(let item of res){
        const js = localStorage[item.key]
        //如果本地沒有發(fā)請(qǐng)求宵晚,再發(fā)一次請(qǐng)求緩存
        //本地存在的話恨旱,就要判斷一下當(dāng)前的版本號(hào)
        //更新我們的資源
        if(js == item.value){
            eval(js)
        }else{
            fetch(item.value).then(function(res){
                localStorage['a.js'] = "a.xx3322.js";
                localStorage['a.xx3322.js'] = res;
            })
        }
    }

}
activePage();

現(xiàn)在我們可以使用basket.js來實(shí)現(xiàn)上述代碼的功能

https://github.com/addyosmani/basket.js
http://www.wenjiangs.com/article/basket-js.html
使用basket.js這個(gè)庫(kù)就能輕松管理了。

簡(jiǎn)單來說 Basket.js 是一個(gè)腳本緩存器坝疼,使用本地儲(chǔ)存 localStorage 緩存 JavaScript 文件,如果腳本以前在本地緩存過谆沃,那么他將會(huì)被快速的加載到頁(yè)面中钝凶,如果沒有緩存過,那么就使用 XHR 異步加載到頁(yè)面中

HTML5 規(guī)范建議存儲(chǔ)限額為 5MB 的本地存儲(chǔ),但瀏覽器可以實(shí)現(xiàn)他們自己的配額耕陷,如果他們希望掂名。如果超出了配額,瀏覽器可能無法在緩存中存儲(chǔ)項(xiàng)目哟沫。如果發(fā)生這種情況饺蔑,Bask.js 將從最舊的緩存中刪除條目,然后重試嗜诀。有些像 Opera 這樣的瀏覽器會(huì)要求用戶在超過設(shè)定閾值時(shí)增加配額猾警。

配合使用前端離線緩存方案 localForage

緩存策略

緩存的優(yōu)先級(jí)
cache-control > expire > etag > last-modified

image.png

他們的關(guān)系面試一定會(huì)問到。
回去需要用nginx體現(xiàn)一下

網(wǎng)站協(xié)議

HTTP2協(xié)議

  • HTTP2協(xié)議的特點(diǎn)

  • 使用二進(jìn)制格式傳輸隆敢,更高效发皿、更緊湊。
    TTP 2.0 中所有加強(qiáng)性能的核心點(diǎn)在于此拂蝎。在之前的 HTTP 版本中穴墅,我們是通過文本的方式傳輸數(shù)據(jù)。在 HTTP 2.0 中引入了新的編碼機(jī)制温自,所有傳輸?shù)臄?shù)據(jù)都會(huì)被分割玄货,并采用二進(jìn)制格式編碼茅坛。

  • 對(duì)報(bào)頭壓縮蛇数,降低開銷。
    在 HTTP 1.X 中缘滥,我們使用文本的形式傳輸 header券躁,在 header 攜帶 cookie 的情況下惩坑,可能每次都需要重復(fù)傳輸幾百到幾千的字節(jié)。

    在 HTTP 2.0 中也拜,使用了 HPACK 壓縮格式對(duì)傳輸?shù)?header 進(jìn)行編碼以舒,減少了 header 的大小。并在兩端維護(hù)了索引表慢哈,用于記錄出現(xiàn)過的 header 蔓钟,后面在傳輸過程中就可以傳輸已經(jīng)記錄過的 header 的鍵名,對(duì)端收到數(shù)據(jù)后就可以通過鍵名找到對(duì)應(yīng)的值卵贱。

  • 多路復(fù)用滥沫,一個(gè)網(wǎng)絡(luò)連接實(shí)現(xiàn)并行請(qǐng)求。
    在 HTTP 2.0 中键俱,有兩個(gè)非常重要的概念兰绣,分別是幀(frame)和流(stream)。

    幀代表著最小的數(shù)據(jù)單位编振,每個(gè)幀會(huì)標(biāo)識(shí)出該幀屬于哪個(gè)流缀辩,流也就是多個(gè)幀組成的數(shù)據(jù)流。

    多路復(fù)用,就是在一個(gè) TCP 連接中可以存在多條流臀玄。換句話說瓢阴,也就是可以發(fā)送多個(gè)請(qǐng)求,對(duì)端可以通過幀中的標(biāo)識(shí)知道屬于哪個(gè)請(qǐng)求健无。通過這個(gè)技術(shù)荣恐,可以避免 HTTP 舊版本中的隊(duì)頭阻塞問題,極大的提高傳輸性能累贤。

    HTTP/2對(duì)同?域名下所有請(qǐng)求都是基于流叠穆,也就是說同?域名不管訪問多少?件,也只建??路連接畦浓。同樣Apache的最?連接數(shù)為300痹束,因?yàn)橛辛诉@個(gè)新特性,最?的并發(fā)就可以提升到300讶请,?原來提升了6倍5凰弧(本http1.x的話每個(gè)用戶可能就會(huì)占據(jù)5-6個(gè)請(qǐng)求)

  • 服務(wù)器主動(dòng)推送,減少請(qǐng)求的延遲 夺溢。
    在 HTTP 2.0 中论巍,服務(wù)端可以在客戶端某個(gè)請(qǐng)求后,主動(dòng)推送其他資源风响。

    可以想象以下情況嘉汰,某些資源客戶端是一定會(huì)請(qǐng)求的,這時(shí)就可以采取服務(wù)端 push 的技術(shù)状勤,提前給客戶端推送必要的資源鞋怀,這樣就可以相對(duì)減少一點(diǎn)延遲時(shí)間。當(dāng)然在瀏覽器兼容的情況下你也可以使用 prefetch 持搜。

  • 默認(rèn)使用加密密似。

小字為先

性能優(yōu)化其實(shí)就可以用這四個(gè)字來概括,“小字為先” 葫盼,也就是將大的東西變小

渲染中性能優(yōu)化

重繪

先來了解一下開發(fā)者工具下中的隱藏技能


控制臺(tái)下面有個(gè)rendering的選項(xiàng)(如果沒有的話可以在上面的performance右邊的工具欄選項(xiàng)選擇more tools中的rendering進(jìn)行添加)

image.png

Paint Flashing 高亮顯示網(wǎng)頁(yè)中需要被重繪的部分残腌。
Layer Borders 顯示Layer邊界。
FPS Meter 每一秒的幀細(xì)節(jié)贫导,幀速率的分布信息和GPU的內(nèi)存使用情況抛猫。

image.png

Scrolling Performance Issues 分析鼠標(biāo)滾動(dòng)時(shí)的性能問題,會(huì)顯示使屏幕滾動(dòng)變慢的區(qū)域孩灯。
Emulate CSS Media 仿真CSS媒體類型闺金,查看不同的設(shè)備上CSS樣式效果,可能的媒體類型選項(xiàng)有print峰档、screen掖看。

將paint flashing選項(xiàng)打勾之后點(diǎn)擊刷新的時(shí)候可以看到頁(yè)面綠了一下匣距,正是因?yàn)檫@是網(wǎng)頁(yè)中需要被重繪的部分。
上個(gè)代碼(重點(diǎn)部分)

<div class="container">
    <div class="ball" id="ball">
    </div>
</div>

<script>
    var ball = document.getElementById('ball');
    ball.classList.add('ball');
    ball.classList.add('ball-running');

</script>

<style>
    .container{
        position: relative;
        min-height: 400px;
    }

    .ball{
        position: absolute;
        top: 0;
        left: 0;
        width: 100px;
        height: 100px;
        background-color: blueviolet;
        border-radius: 50%;
        box-shadow: 0 0 5px rgba(245, 172, 172, 0.75)
    }
    .ball-running{
        animation: run-around 4s infinite;
    }
    @keyframes run-around {
        0%{
            top: 0;
            left: 0;
        }
        25%{
            top: 0;
            left: 200px;
        }
        50%{
            top: 200px;
            left: 200px;
        }
        75%{
            top: 200px;
            left: 0;
        }
    }
</style>

上面主要就是實(shí)現(xiàn)一個(gè)小球運(yùn)動(dòng)的效果哎壳,當(dāng)我們已經(jīng)在rendering里面選中了paint flashing的時(shí)候,我們可以看到小球運(yùn)動(dòng)起來的效果是外面包裹著一層綠色尚卫,說明這是要進(jìn)行重繪的區(qū)域

image.png

使用Chrome DevTools的performance面板可以記錄和分析頁(yè)面在運(yùn)行時(shí)的所有活動(dòng)归榕。
https://www.cnblogs.com/xiaohuochai/p/9182710.html
image.png

loading:加載時(shí)間
scripting:腳本執(zhí)行時(shí)間
rendering:重排時(shí)間
painting:重繪時(shí)間
idle:空閑時(shí)間,網(wǎng)站性能越好吱涉,空閑時(shí)間越長(zhǎng)

網(wǎng)站的渲染流程


將上面的圖從summary切換到event log

image.png

event log按照時(shí)間先后來排序
可以看到網(wǎng)站的渲染流程是這樣的:

  1. 獲取DOM進(jìn)行分層
  2. 對(duì)每個(gè)圖層節(jié)點(diǎn)進(jìn)行樣式的計(jì)算 Recalculate Style
  3. 為每個(gè)對(duì)應(yīng)的節(jié)點(diǎn)圖形和位置 重排Layout
  4. 對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行繪制并添加到圖層位圖中 Paint
    (并不是每個(gè)圖層都會(huì)GPU進(jìn)行參與)
    只有Composite Layers才會(huì)讓GPU參與
  5. 將這個(gè)位圖上傳至GPU 旋轉(zhuǎn)刹泄、縮放、偏移怎爵、修改透明

所以渲染過程總的來說是這樣的:Layout -》 Paint -》 Composite Layers

我們說DOM會(huì)進(jìn)行分層特石,那么什么元素會(huì)獨(dú)立成層呢?


根元素鳖链、position姆蘸、transfrom、半透明元素芙委、CSS濾鏡逞敷、Video 、Overflow

我們說GPU跑起來會(huì)比CPU快灌侣,那么哪些元素屬性會(huì)讓GPU參與進(jìn)來呢推捐?


CSS3D、Video侧啼、Webgl(https://github.com/lgwebdream/gpu.js)牛柒、CSS濾鏡、transfrom

CPU和GPU到底有什么區(qū)別呢痊乾?


https://www.zhihu.com/question/19903344

CPU即中央處理器皮壁,GPU即圖形處理器。其次符喝,要解釋兩者的區(qū)別闪彼,要先明白兩者的相同之處:兩者都有總線和外界聯(lián)系,有自己的緩存體系协饲,以及數(shù)字和邏輯運(yùn)算單元畏腕。一句話,兩者都為了完成計(jì)算任務(wù)而設(shè)計(jì)茉稠。

總結(jié)一下:
相同之處:總線和外界聯(lián)系描馅、緩存體系、數(shù)字和邏輯與預(yù)算單元而线、計(jì)算而生
不同之處:CPU主要負(fù)責(zé)和操作系統(tǒng)應(yīng)用程序铭污,GPU顯示數(shù)據(jù)相關(guān)
http://www.sohu.com/a/200435336_463987
還要推一推gpu.js這個(gè)庫(kù)
https://github.com/gpujs/gpu.js
GPU.js is a JavaScript Acceleration library for GPGPU (General purpose computing on GPUs) in JavaScript. GPU.js will automatically compile simple JavaScript functions into shader language and run them on the GPU. In case a GPU is not available, the functions will still run in regular JavaScript.
也就是說GPU.js會(huì)自動(dòng)的將簡(jiǎn)單的js函數(shù)翻譯成shader 語言并放在GPU上面運(yùn)行他們恋日。

像素管道

像素管道是網(wǎng)頁(yè)性能優(yōu)化的靈魂,讓我們來看看什么是像素管道

image.png

上圖就是像素管道嘹狞,通常我們會(huì)使用JS修改一些樣式岂膳,隨后瀏覽器會(huì)進(jìn)行樣式計(jì)算,然后進(jìn)行布局磅网,繪制谈截,最后將各個(gè)圖層合并在一起完成整個(gè)渲染的流程,這期間的每一步都有可能導(dǎo)致頁(yè)面卡頓涧偷。

注意簸喂,并不是所有的樣式改動(dòng)都需要經(jīng)歷這五個(gè)步驟。舉例來說:如果在JS中修改了元素的幾何屬性(寬度燎潮、高度等)喻鳄,那么瀏覽器需要需要將這五個(gè)步驟都走一遍。但如果您只是修改了文字的顏色确封,則布局(Layout)是可以跳過去的除呵。

除了最后的合成,前面四個(gè)步驟在不同的場(chǎng)景下都可以被跳過隅肥。例如:CSS動(dòng)畫就可以跳過JS運(yùn)算竿奏,它不需要執(zhí)行JS。

通過錄制performance我們可以看到主線程的任務(wù)腥放。
我們可以放大主線程從而精準(zhǔn)的看到每一幀瀏覽器都執(zhí)行了哪些任務(wù)以及每個(gè)任務(wù)耗費(fèi)了多長(zhǎng)時(shí)間泛啸。如下圖所示:

image.png

我們?nèi)绾胃倪M(jìn)上面的代碼呢,減少重繪呢秃症?

使用transform來代替top和left

我們可以看到https://csstriggers.com/transform上面對(duì)于transform的描述是
Changing transform does not trigger any geometry changes or painting, which is very good. This means that the operation can likely be carried out by the compositor thread with the help of the GPU.
也就是說改變transform并不會(huì)觸發(fā)幾何圖形的更改或者重繪候址,transform的操作可以合成器線程在GPU的幫助下執(zhí)行。

css-triggers給出了不同的CSS屬性被更改后會(huì)觸發(fā)像素管道的那些步驟种柑。
簡(jiǎn)單來說岗仑,像素管道經(jīng)歷的步驟越多,渲染時(shí)間就越長(zhǎng)聚请,單個(gè)步驟內(nèi)也可能因?yàn)槟硞€(gè)原因而變得耗時(shí)很長(zhǎng)荠雕。

將上面代碼的keyframes部分更改成:

        0%{
            transform: translate(0)
        }
        25%{
            transform: translate(200px,0)
        }
        50%{
            transform: translate(200px,200px)
        }
        75%{
            transform: translate(0,200px)
        }

我們此時(shí)看到小球在運(yùn)動(dòng),但是已經(jīng)沒有綠色了驶赏,已經(jīng)沒有重排了Uū啊!

image.png

神奇C喊8俏摹!
我們?cè)賮砜纯翠浿频膕ummary
image.png

我的天吶蚯姆!真神奇五续。換了transforms每次運(yùn)動(dòng)就不用重繪重排了洒敏,都是合成層和GPU在操作了,而且只要GPU開啟疙驾,速度就會(huì)快很多凶伙。
https://csstriggers.com/這個(gè)網(wǎng)站拿好不送!K椤镊靴!

總結(jié)
CSS動(dòng)畫我們可以通過降低繪制區(qū)域并且使transform屬性來完成動(dòng)畫,同時(shí)我們需要管理好圖層链韭,因?yàn)槔L制和圖層管理都需要成本,通常我們需要根據(jù)具體情況進(jìn)行權(quán)衡并做出最好的選擇煮落。

重排

什么會(huì)引起重排敞峭?


  1. 添加或者刪除元素的時(shí)候
  2. 元素的位置發(fā)生改變
  3. 元素的-webkit-box-sizing: border-box;不會(huì)讓我們的盒子發(fā)生太多的變化
    如果用標(biāo)準(zhǔn)盒子模型的話,盒子越來越大
  4. 頁(yè)面初始化
  5. 內(nèi)容變化(沒有撐開盒)
  6. js 讀取一下幾個(gè)值 offset蝉仇、scroll旋讹、width、getComputerStyle
    為什么js讀取的時(shí)候會(huì)引起重排轿衔?
    var ele = document.getElementById('myDiv');
    ele.style.borderLeft = '1px';
    ele.style.borderRight = '2px';
    ele.style.padding = '5px';

乍一想沉迹,元素的樣式改變了三次,每次改變都會(huì)引起重排和重繪害驹,所以總共有三次重排重繪過程鞭呕,但是瀏覽器并不會(huì)這么笨,它會(huì)把三次修改“保存”起來(大多數(shù)瀏覽器通過隊(duì)列化修改并批量執(zhí)行來優(yōu)化重排過程)宛官,一次完成葫松!但是,有些時(shí)候你可能會(huì)(經(jīng)常是不知不覺)強(qiáng)制刷新隊(duì)列并要求計(jì)劃任務(wù)立即執(zhí)行底洗。獲取布局信息的操作會(huì)導(dǎo)致隊(duì)列刷新腋么,比如:

  • offsetTop, offsetLeft, offsetWidth, offsetHeight

  • scrollTop, scrollLeft, scrollWidth, scrollHeight

  • clientTop, clientLeft, clientWidth, clientHeight

  • getComputedStyle() (currentStyle in IE)
    將上面的代碼稍加修改,

    var ele = document.getElementById('myDiv');
    ele.style.borderLeft = '1px';
    ele.style.borderRight = '2px';
    
    // here use offsetHeight
    // ...
    ele.style.padding = '5px';
    

因?yàn)閛ffsetHeight屬性需要返回最新的布局信息亥揖,因此瀏覽器不得不執(zhí)行渲染隊(duì)列中的“待處理變化”并觸發(fā)重排以返回正確的值(即使隊(duì)列中改變的樣式屬性和想要獲取的屬性值并沒有什么關(guān)系)珊擂,所以上面的代碼,前兩次的操作會(huì)緩存在渲染隊(duì)列中待處理费变,但是一旦offsetHeight屬性被請(qǐng)求了摧扇,隊(duì)列就會(huì)立即執(zhí)行,所以總共有兩次重排與重繪胡控。所以盡量不要在布局信息改變時(shí)做查詢扳剿。

我們可以使用requestAnimationFrame,拆開來寫昼激,就給了瀏覽器優(yōu)化的機(jī)會(huì)了庇绽。

  var ele = document.getElementById('myDiv');
  // here use offsetHeight
  // ...
  requestAnimationFrame(function(){
      ele.style.padding = '5px';
      ele.style.borderLeft = '1px';
      ele.style.borderRight = '2px';
  })

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame
requestAnimationFrame告訴瀏覽器——你希望執(zhí)行一個(gè)動(dòng)畫锡搜,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動(dòng)畫。該方法需要傳入一個(gè)回調(diào)函數(shù)作為參數(shù)瞧掺,該回調(diào)函數(shù)會(huì)在瀏覽器下一次重繪之前執(zhí)行

頁(yè)面加載性能優(yōu)化

必須知道的概念
TTFB(Time To First Byte ):?字節(jié)時(shí)間
FP(First Paint ):?次繪制耕餐,僅有?個(gè)div根節(jié)點(diǎn)。
FCP(First Contentful Paint): ?次有內(nèi)容的繪制辟狈,包含??的基本框架肠缔,但沒有數(shù)據(jù)內(nèi)容。
FMP(First Meaningful Paint):?次有意義的繪制哼转,包含??所有元素及數(shù)據(jù)
TTI(Time To Interactive):可交互時(shí)間
Long tasks:超過了 50ms 的任務(wù)
SSR&&CSR:服務(wù)端渲染和客戶端渲染
Isomorphic JavaScript:同構(gòu)化

image.png

image.png

FCP.png

FMP.png

類比于VUE
image.png

created 類比于 FP(首次繪制明未,只有一個(gè)app空節(jié)點(diǎn))
mounted 類比于 FMP(頁(yè)面基本框架繪制完成)

Performance — 前端性能監(jiān)控利器
Performance是一個(gè)做前端性能監(jiān)控離不開的API,最好在頁(yè)面完全加載完成之后再使用壹蔓,因?yàn)楹芏嘀当仨氃陧?yè)面完全加載之后才能得到趟妥。最簡(jiǎn)單的辦法是在window.onload事件中讀取各種數(shù)據(jù)。
https://www.cnblogs.com/bldxh/p/6857324.html
https://cloud.tencent.com/developer/news/301840
從輸入url到用戶可以使用頁(yè)面的全過程時(shí)間統(tǒng)計(jì)佣蓉,會(huì)返回一個(gè)PerformanceTiming對(duì)象披摄,單位均為毫秒。

image.png

每一個(gè)performance.timing屬性都表示一個(gè)頁(yè)面事件(例如頁(yè)面發(fā)送了請(qǐng)求)或者頁(yè)面加載(例如當(dāng)DOM開始加載)勇凭,測(cè)量以毫秒的形式從1970年1月1日的午夜開始疚膊。結(jié)果為0表示該事件未發(fā)生(例如redirectEnd或者redirectStart等)
image.png

其中有個(gè)方法叫getEntries()
獲取所有資源請(qǐng)求的時(shí)間數(shù)據(jù),這個(gè)函數(shù)返回一個(gè)按startTime排序的對(duì)象數(shù)組,數(shù)組成員除了會(huì)自動(dòng)根據(jù)所請(qǐng)求資源的變化而改變以外虾标,還可以用mark(),measure()方法自定義添加寓盗,該對(duì)象的屬性中除了包含資源加載時(shí)間還有以下五個(gè)屬性。
name:資源名稱夺巩,是資源的絕對(duì)路徑或調(diào)用mark方法自定義的名稱
startTime:開始時(shí)間
duration:加載時(shí)間
entryType:資源類型贞让,entryType類型不同數(shù)組中的對(duì)象結(jié)構(gòu)也不同!具體見下
initiatorType:誰發(fā)起的請(qǐng)求柳譬,具體見下
話不多說喳张,咱們來寫寫代碼。

    <style>
    body{
        background-color: greenyellow;
    }
    </style>

    const obsever = new PerformanceObserver((list)=>{
        for(const entry of list.getEntries()){
            console.log(entry.entryType);
            console.log(entry.startTime);
            console.log(entry.duration);
        }
    })

    obsever.observe({entryTypes:['paint']});

結(jié)果如下:


image.png

或者可以直接通過window.performance.getEntriesByType("paint")就可以取得FP和FCP的值

image.png

FMP主要用來給頁(yè)面打點(diǎn)美澳。

五分鐘擼一個(gè)前端性能監(jiān)控工具
http://web.jobbole.com/94938/

聊聊performance中的long task
什么是 long task?

image.png

https://www.itcodemonkey.com/article/10654.html

簡(jiǎn)單而言销部,任何在瀏覽器中執(zhí)行超過 50 ms 的任務(wù),都是 long task制跟。

那么 long task這個(gè)時(shí)間是怎么得來的舅桩?
因?yàn)闉g覽器是單線程,這意味著同一時(shí)間主線程只能處理一個(gè)任務(wù)雨膨,如果一個(gè)任務(wù)執(zhí)行時(shí)間太長(zhǎng)擂涛,瀏覽器就無法執(zhí)行其他任務(wù),用戶就會(huì)感覺瀏覽器被卡死聊记,因?yàn)樗妮斎氲貌坏饺魏雾憫?yīng)撒妈。

為了100ms內(nèi)能給出相應(yīng)恢暖,將空閑周期執(zhí)行的任務(wù)限制在50ms意味著,即使用戶的輸入行為發(fā)生在任務(wù)剛執(zhí)行時(shí)狰右。瀏覽器仍有50ms來相應(yīng)用戶的輸入杰捂。

long task 會(huì)長(zhǎng)時(shí)間占據(jù)主線程資源,進(jìn)而阻礙了其他關(guān)鍵任務(wù)的執(zhí)行/響應(yīng)棋蚌,造成頁(yè)面卡頓嫁佳。

常見場(chǎng)景如:

不斷計(jì)算 DOM 元素的大小、位置谷暮,并且根據(jù)結(jié)果對(duì)頁(yè)面進(jìn)行 relayout蒿往;

一次性生成十分龐大的 DOM 元素,如大型表單湿弦;

1000000次的循環(huán)計(jì)算熄浓;

long task的基本屬性
Long Tasks API 定義了 PerformanceLongTaskTiming接口,用于描述 long task省撑。
一般而言,name + attribution 就可以基本定位出 long task 的來源:

name:告訴我們來源是 <script/> 還是 <iframe/> 俯在?self -> <script/>竟秫;same-origin-xxx + cross-origin-xxx -> <iframe/>

attribution:到底是哪個(gè) <iframe/>?

如何使用跷乐?

const obsever = new PerformanceObserver((list)=>{
    for(const entry of list.getEntries()){
        console.log(entry.entryType);
        console.log(entry.startTime);
        console.log(entry.duration);
        console.log(JSON.stringify(entry.attribution))
    }
})

obsever.observe({entryTypes:['longtask']});
image.png

還發(fā)現(xiàn)了一篇不錯(cuò)的文章肥败,前端性能優(yōu)化標(biāo)準(zhǔn)https://yq.aliyun.com/articles/598162

CSR SSR 預(yù)渲染 同構(gòu)的優(yōu)點(diǎn)和缺點(diǎn)

image.png

NodeJs性能優(yōu)化

什么是內(nèi)存泄漏?
不再用到的變量/內(nèi)存愕提,沒有及時(shí)釋放馒稍,就叫做內(nèi)存泄漏。

image.png

內(nèi)存泄漏的表現(xiàn)
隨著內(nèi)存泄漏的增長(zhǎng)浅侨,V8對(duì)垃圾收集器越來越具有攻擊性纽谒,這會(huì)使你的應(yīng)用運(yùn)行速度變慢。

內(nèi)存泄漏可能觸發(fā)其他類型的失敗如输,可能會(huì)耗盡文件描述符鼓黔,還可能會(huì)突然不能建立新的數(shù)據(jù)庫(kù)連接。

壓力測(cè)試尋找內(nèi)存泄漏
https://www.cnblogs.com/ycyzharry/p/8372168.html
https://github.com/wg/wrk
wrk支持大多數(shù)類UNIX系統(tǒng)不见,不支持windows澳化。

還有更專業(yè)的JMeter

查找node內(nèi)存泄漏工具
memwatch + heapdump

如果不發(fā)生特別大的內(nèi)存泄漏問題,這兩個(gè)工具是不會(huì)跳出來的稳吮。

memwatch.on('leak',function(info){
    var file = './tmp/heapsnapshot';
    heapdump.writeSnapshot(file,function(err){
        if(err)console.log(err);
        else console.error('Wrote snapshot',file);
    })
})

//通過Diff的方式找到真正的元兇

var hd = new memwatch.HeapDiff();
var diff = hd.end()

//一個(gè)狀態(tài)時(shí)間發(fā)射器
memwatch.on('stats',function(stats){
    //數(shù)據(jù)包括
    usage_trend(使用趨勢(shì))
    current_base(當(dāng)前基數(shù))
    estimated_base(預(yù)期基數(shù))
    num_full_gc(完整的垃圾回收次數(shù))
    num_inc_gc(增長(zhǎng)的垃圾回收次數(shù))
    heap_compactions(內(nèi)存壓縮次數(shù))
    min(最小)
    max(最大)
})

http://www.linkdata.se/sourcecode/memwatch/ memwatch的源代碼下載地址

Nodejs編碼規(guī)范


慎用內(nèi)存緩存

函數(shù)內(nèi)的變量是可以隨著函數(shù)執(zhí)行被回收的缎谷,但是全局不行。所以避免使用對(duì)象作為緩存灶似,可以移步到Redis等列林。

Redis 是完全開源免費(fèi)的瑞你,遵守BSD協(xié)議,是一個(gè)高性能的key-value數(shù)據(jù)庫(kù)席纽。

Redis 與其他 key - value 緩存產(chǎn)品有以下三個(gè)特點(diǎn):

Redis支持?jǐn)?shù)據(jù)的持久化捏悬,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤中,重啟的時(shí)候可以再次加載進(jìn)行使用润梯。
Redis不僅僅支持簡(jiǎn)單的key-value類型的數(shù)據(jù)过牙,同時(shí)還提供list,set纺铭,zset寇钉,hash等數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)。
Redis支持?jǐn)?shù)據(jù)的備份舶赔,即master-slave模式的數(shù)據(jù)備份扫倡。

http://www.runoob.com/redis/redis-intro.html

關(guān)于隊(duì)列消費(fèi)不及時(shí)

比如我們用log4來收集日志,如果日志的產(chǎn)生速度大于文件寫入的速度竟纳。就容易產(chǎn)生內(nèi)存泄漏撵溃。訪問已經(jīng)結(jié)束了,服務(wù)器的log4日志還在不停的寫锥累。
解決方式:
監(jiān)控隊(duì)列的長(zhǎng)度一旦堆積就報(bào)警或者拒絕新的要求缘挑。
所以的異步調(diào)用都有超時(shí)回調(diào),一旦達(dá)到時(shí)間調(diào)用未得到結(jié)果就報(bào)警桶略。

關(guān)于閉包

node如果有閉包從而產(chǎn)生內(nèi)存泄露服務(wù)器就很容易掛语淘,相對(duì)于在瀏覽器的js的閉包,node的閉包的處理顯得更加重要际歼。

解決方式:

  1. weakmap可以立即回收某個(gè)變量惶翻。
let b = new Object()
let wm = new Weakmap()
wm.set(b,new Array(5*1024*1024))
b = null
  1. perf_hooks(性能鉤子)
    是nodejs中的一個(gè)api。

The Performance Timing API provides an implementation of the W3C Performance Timeline specification. The purpose of the API is to support collection of high resolution performance metrics. This is the same Performance API as implemented in modern Web browsers.
提供的功能就類似于JS中的那個(gè)new PerformanceObserver

const obs = new PerformanceObserver((items) => {
    console.log(items.getEntries()[0].duration);
    performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });

performance.mark('A');
doSomeLongRunningProcess(() => {
    performance.mark('B');
    performance.measure('A to B', 'A', 'B');
});

詳細(xì)內(nèi)容參照http://nodejs.cn/api/perf_hooks.html

總結(jié)

對(duì)于nodejs應(yīng)用的測(cè)試

  1. node --inspect app.js
  2. chrome://inspect/#devices
  3. 沒經(jīng)過壓力測(cè)試的代碼只完成10%
  4. 準(zhǔn)確計(jì)算QPS未雨綢繆
  5. 合理利用壓力測(cè)試工具
  6. 注意緩存隊(duì)列及其他耗時(shí)較長(zhǎng)的代碼
  7. 開發(fā)健壯的NodeJs應(yīng)用

https://www.cnblogs.com/rubylouvre/p/3633404.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹅心,一起剝皮案震驚了整個(gè)濱河市吕粗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旭愧,老刑警劉巖溯泣,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異榕茧,居然都是意外死亡垃沦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門用押,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肢簿,“玉大人,你說我怎么就攤上這事〕爻洌” “怎么了桩引?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)收夸。 經(jīng)常有香客問我坑匠,道長(zhǎng),這世上最難降的妖魔是什么卧惜? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任厘灼,我火速辦了婚禮,結(jié)果婚禮上咽瓷,老公的妹妹穿的比我還像新娘设凹。我一直安慰自己,他們只是感情好茅姜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布闪朱。 她就那樣靜靜地躺著,像睡著了一般钻洒。 火紅的嫁衣襯著肌膚如雪奋姿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天素标,我揣著相機(jī)與錄音胀蛮,去河邊找鬼。 笑死糯钙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的退腥。 我是一名探鬼主播任岸,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼狡刘!你這毒婦竟也來了享潜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤嗅蔬,失蹤者是張志新(化名)和其女友劉穎剑按,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澜术,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艺蝴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸟废。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猜敢。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缩擂,到底是詐尸還是另有隱情鼠冕,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布胯盯,位于F島的核電站懈费,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏博脑。R本人自食惡果不足惜憎乙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望趋厉。 院中可真熱鬧寨闹,春花似錦、人聲如沸君账。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡数。三九已至椭蹄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間净赴,已是汗流浹背绳矩。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留玖翅,地道東北人翼馆。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像金度,于是被迫代替她去往敵國(guó)和親应媚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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