1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        聊一聊前端性能優(yōu)化

        共 5357字,需瀏覽 11分鐘

         ·

        2020-07-27 08:21

        什么是 CRP?

        CRP又稱關(guān)鍵渲染路徑,引用MDN對(duì)它的解釋:

        ?

        關(guān)鍵渲染路徑是指瀏覽器通過把 HTML、CSS 和 JavaScript 轉(zhuǎn)化成屏幕上的像素的步驟順序。優(yōu)化關(guān)鍵渲染路徑可以提高渲染性能。關(guān)鍵渲染路徑包含了 Document Object Model (DOM),CSS Object Model (CSSOM),渲染樹和布局。

        ?

        優(yōu)化關(guān)鍵渲染路徑可以提升首屏渲染時(shí)間。理解和優(yōu)化關(guān)鍵渲染路徑對(duì)于確?;亓骱椭乩L可以每秒 60 幀、確保高性能的用戶交互和避免無意義渲染至關(guān)重要。

        如何結(jié)合CRP進(jìn)行性能優(yōu)化?

        我想對(duì)于性能優(yōu)化,大家都不陌生,無論是平時(shí)的工作還是面試,是一個(gè)老生常談的話題。

        如果單純針對(duì)一些點(diǎn)去泛泛而談,我想是不太嚴(yán)謹(jǐn)?shù)摹?/p>

        今天我們結(jié)合一道非常經(jīng)典的面試題:從輸入U(xiǎn)RL到頁(yè)面展示,這中間發(fā)生了什么?來從其中的某些環(huán)節(jié),來深入談?wù)?code style="background-color:rgba(27,31,35,.047);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">前端性能優(yōu)化 CRP。

        從輸入 URL 到頁(yè)面展示,這中間發(fā)生了什么?

        這道題的經(jīng)典程度想必不用我多說,這里我用一張圖梳理了它的大致流程:63da91e1967a663ab3ea3563566f458a.webp這個(gè)過程可以大致描述為如下:

        1、URI 解析

        2、DNS 解析(DNS 服務(wù)器)

        3、TCP 三次握手(建立客戶端和服務(wù)器端的連接通道)

        4、發(fā)送 HTTP 請(qǐng)求

        5、服務(wù)器處理和響應(yīng)

        6、TCP 四次揮手(關(guān)閉客戶端和服務(wù)器端的連接)

        7、瀏覽器解析和渲染

        8、頁(yè)面加載完成

        本文我會(huì)從瀏覽器渲染過程、緩存、DNS 優(yōu)化幾方面進(jìn)行性能優(yōu)化的說明。

        瀏覽器渲染過程

        構(gòu)建 DOM 樹

        構(gòu)建DOM樹的大致流程梳理為下圖:6abc2759406787e939e084492c5590fc.webp

        我們以下面這段代碼為例進(jìn)行分析:


        <html>
        ??<head>
        ????<meta?name="viewport"?content="width=device-width,initial-scale=1">
        ????<link?href="style.css"?rel="stylesheet">
        ????<title>構(gòu)建DOM樹title>
        ??head>
        ??<body>
        ????<p>森林p>
        ????<div>之晨div>
        ??body>
        html>

        首先瀏覽器從磁盤或網(wǎng)絡(luò)中讀取?HTML?原始字節(jié),并根據(jù)文件的指定編碼將它們轉(zhuǎn)成字符。

        然后通過分詞器將字節(jié)流轉(zhuǎn)換為?Token,在Token(也就是令牌)生成的同時(shí),另一個(gè)流程會(huì)同時(shí)消耗這些令牌并轉(zhuǎn)換成?HTML head?這些節(jié)點(diǎn)對(duì)象,起始和結(jié)束令牌表明了節(jié)點(diǎn)之間的關(guān)系。fdea06b8c2767f38ef2f9d15708117d6.webp

        當(dāng)所有的令牌消耗完以后就轉(zhuǎn)換成了DOM(文檔對(duì)象模型)。

        最終構(gòu)建出的DOM結(jié)構(gòu)如下:c923db20a6d5cee8e0cfd95f0aaa3e08.webp

        構(gòu)建 CSSOM 樹

        DOM樹構(gòu)建完成,接下來就是CSSOM樹的構(gòu)建了。

        HTML的轉(zhuǎn)換類似,瀏覽器會(huì)去識(shí)別CSS正確的令牌,然后將這些令牌轉(zhuǎn)化成CSS節(jié)點(diǎn)。

        ?

        子節(jié)點(diǎn)會(huì)繼承父節(jié)點(diǎn)的樣式規(guī)則,這里對(duì)應(yīng)的就是層疊規(guī)則和層疊樣式表。

        ?

        構(gòu)建DOM樹的大致流程可梳理為下圖:77cf3b6599b1b4f4a5ce2c3e0fb13386.webp

        我們這里采用上面的HTML為例,假設(shè)它有如下 css:

        body?{
        ??font-size:?16px;
        }
        p?{
        ??font-weight:?bold;
        }
        div?{
        ??color:?orange;
        }

        那么最終構(gòu)建出的CSSOM樹如下:d92edfc220cc66a0b3cb9bf7ab33f2be.webp

        有了?DOM?和?CSSOM,接下來就可以合成布局樹(Render Tree)了。

        構(gòu)建渲染樹

        等?DOM?和?CSSOM?都構(gòu)建好之后,渲染引擎就會(huì)構(gòu)造布局樹。布局樹的結(jié)構(gòu)基本上就是復(fù)制?DOM?樹的結(jié)構(gòu),不同之處在于?DOM?樹中那些不需要顯示的元素會(huì)被過濾掉,如?display:none?屬性的元素、head?標(biāo)簽、script?標(biāo)簽等。

        復(fù)制好基本的布局樹結(jié)構(gòu)之后,渲染引擎會(huì)為對(duì)應(yīng)的?DOM?元素選擇對(duì)應(yīng)的樣式信息,這個(gè)過程就是樣式計(jì)算。

        樣式計(jì)算

        樣式計(jì)算的目的是為了計(jì)算出?DOM?節(jié)點(diǎn)中每個(gè)元素的具體樣式,這個(gè)階段大體可分為三步來完成。

        把 CSS 轉(zhuǎn)換為瀏覽器能夠理解的結(jié)構(gòu)

        和?HTML?文件一樣,瀏覽器也是無法直接理解這些純文本的?CSS?樣式,所以當(dāng)渲染引擎接收到?CSS?文本時(shí),會(huì)執(zhí)行一個(gè)轉(zhuǎn)換操作,將?CSS?文本轉(zhuǎn)換為瀏覽器可以理解的結(jié)構(gòu)——styleSheets

        轉(zhuǎn)換樣式表中的屬性值,使其標(biāo)準(zhǔn)化

        現(xiàn)在我們已經(jīng)把現(xiàn)有的 CSS 文本轉(zhuǎn)化為瀏覽器可以理解的結(jié)構(gòu)了,那么接下來就要對(duì)其進(jìn)行屬性值的標(biāo)準(zhǔn)化操作。

        什么是屬性值標(biāo)準(zhǔn)化?我們來看這樣的一段CSS

        body?{
        ??font-size:?2em;
        }
        div?{
        ??font-weight:?bold;
        }
        div?{
        ??color:?red;
        }

        可以看到上面的?CSS?文本中有很多屬性值,如 2em、bold、red,這些類型數(shù)值不容易被渲染引擎理解,所以需要將所有值轉(zhuǎn)換為渲染引擎容易理解的、標(biāo)準(zhǔn)化的計(jì)算值,這個(gè)過程就是屬性值標(biāo)準(zhǔn)化。

        那標(biāo)準(zhǔn)化后的屬性值是什么樣子的?

        6605e61c02aa6c0cc5296d5f07b65a0c.webp從圖中可以看到,2em?被解析成了?32px,bold?被解析成了?700,red?被解析成了?rgb(255,0,0)……

        計(jì)算出 DOM 樹中每個(gè)節(jié)點(diǎn)的具體樣式

        現(xiàn)在樣式的屬性已被標(biāo)準(zhǔn)化了,接下來就需要計(jì)算?DOM?樹中每個(gè)節(jié)點(diǎn)的樣式屬性了,如何計(jì)算呢?

        這其中涉及到兩點(diǎn):CSS 的繼承規(guī)則層疊規(guī)則

        這里由于不是本文的重點(diǎn),我簡(jiǎn)單做下說明:

        • CSS?繼承就是每個(gè)?DOM?節(jié)點(diǎn)都包含有父節(jié)點(diǎn)的樣式
        • 層疊是?CSS?的一個(gè)基本特征,它是一個(gè)定義了如何合并來自多個(gè)源的屬性值的算法。它在?CSS?處于核心地位,CSS?的全稱“層疊樣式表”正是強(qiáng)調(diào)了這一點(diǎn)。

        樣式計(jì)算完成之后,渲染引擎還需要計(jì)算布局樹中每個(gè)元素對(duì)應(yīng)的幾何位置,這個(gè)過程就是計(jì)算布局。

        計(jì)算布局

        現(xiàn)在,我們有?DOM?樹和?DOM?樹中元素的樣式,但這還不足以顯示頁(yè)面,因?yàn)槲覀冞€不知道?DOM?元素的幾何位置信息。那么接下來就需要計(jì)算出?DOM?樹中可見元素的幾何位置,我們把這個(gè)計(jì)算過程叫做布局。

        繪制

        通過樣式計(jì)算和計(jì)算布局就完成了最終布局樹的構(gòu)建。再之后,就該進(jìn)行后續(xù)的繪制操作了。

        到這里,瀏覽器的渲染過程就基本結(jié)束了,通過下面的一張圖來梳理下:4034da68983afa3863d14c0dc692aac8.webp

        到這里我們已經(jīng)把瀏覽器解析和渲染的完整流程梳理完成了,那么這其中有那些地方可以去做性能優(yōu)化呢?

        從瀏覽器的渲染過程中可以做的優(yōu)化點(diǎn)

        通常一個(gè)頁(yè)面有三個(gè)階段:加載階段、交互階段和關(guān)閉階段。

        • 加載階段,是指從發(fā)出請(qǐng)求到渲染出完整頁(yè)面的過程,影響到這個(gè)階段的主要因素有網(wǎng)絡(luò)和?JavaScript?腳本。
        • 交互階段,主要是從頁(yè)面加載完成到用戶交互的整合過程,影響到這個(gè)階段的主要因素是?JavaScript?腳本。
        • 關(guān)閉階段,主要是用戶發(fā)出關(guān)閉指令后頁(yè)面所做的一些清理操作。

        這里我們需要重點(diǎn)關(guān)注加載階段交互階段,因?yàn)橛绊懙轿覀凅w驗(yàn)的因素主要都在這兩個(gè)階段,下面我們就來逐個(gè)詳細(xì)分析下。

        加載階段

        我們先來分析如何系統(tǒng)優(yōu)化加載階段中的頁(yè)面,來看一個(gè)典型的渲染流水線,如下圖所示:2424ddac4e32424a4743b0ce787dd645.webp

        通過上面對(duì)瀏覽器渲染過程的分析我們知道JavaScript、首次請(qǐng)求的?HTML?資源文件、CSS?文件是會(huì)阻塞首次渲染的,因?yàn)樵跇?gòu)建?DOM?的過程中需要?HTML?和?JavaScript?文件,在構(gòu)造渲染樹的過程中需要用到?CSS?文件。

        這些能阻塞網(wǎng)頁(yè)首次渲染的資源稱為關(guān)鍵資源。而基于關(guān)鍵資源,我們可以繼續(xù)細(xì)化出三個(gè)影響頁(yè)面首次渲染的核心因素:

        • 關(guān)鍵資源個(gè)數(shù)。關(guān)鍵資源個(gè)數(shù)越多,首次頁(yè)面的加載時(shí)間就會(huì)越長(zhǎng)。
        • 關(guān)鍵資源大小。通常情況下,所有關(guān)鍵資源的內(nèi)容越小,其整個(gè)資源的下載時(shí)間也就越短,那么阻塞渲染的時(shí)間也就越短。
        • 請(qǐng)求關(guān)鍵資源需要多少個(gè)RTT(Round Trip Time)RTT?是網(wǎng)絡(luò)中一個(gè)重要的性能指標(biāo),表示從發(fā)送端發(fā)送數(shù)據(jù)開始,到發(fā)送端收到來自接收端的確認(rèn),總共經(jīng)歷的時(shí)延。

        了解了影響加載過程中的幾個(gè)核心因素之后,接下來我們就可以系統(tǒng)性地考慮優(yōu)化方案了??偟膬?yōu)化原則就是減少關(guān)鍵資源個(gè)數(shù)降低關(guān)鍵資源大小,降低關(guān)鍵資源的 RTT 次數(shù)

        • 如何減少關(guān)鍵資源的個(gè)數(shù)?一種方式是可以將?JavaScript?和?CSS?改成內(nèi)聯(lián)的形式,比如上圖的?JavaScript?和?CSS,若都改成內(nèi)聯(lián)模式,那么關(guān)鍵資源的個(gè)數(shù)就由 3 個(gè)減少到了 1 個(gè)。另一種方式,如果?JavaScript?代碼沒有?DOM?或者?CSSOM?的操作,則可以改成?sync?或者?defer?屬性
        • 如何減少關(guān)鍵資源的大小?可以壓縮?CSS?和?JavaScript?資源,移除?HTMLCSS、JavaScript?文件中一些注釋內(nèi)容
        • 如何減少關(guān)鍵資源?RTT?的次數(shù)?可以通過減少關(guān)鍵資源的個(gè)數(shù)和減少關(guān)鍵資源的大小搭配來實(shí)現(xiàn)。除此之外,還可以使用?CDN?來減少每次?RTT?時(shí)長(zhǎng)。

        交互階段

        接下來我們?cè)賮砹牧捻?yè)面加載完成之后的交互階段以及應(yīng)該如何去優(yōu)化。

        先來看看交互階段的渲染流水線:c661815ac60724e81610e778afddc385.webp其實(shí)這塊大致有以下幾點(diǎn)可以優(yōu)化:

        • 避免DOM的回流。也就是盡量避免重排重繪操作。

        • 減少 JavaScript 腳本執(zhí)行時(shí)間。有時(shí)JavaScript?函數(shù)的一次執(zhí)行時(shí)間可能有幾百毫秒,這就嚴(yán)重霸占了主線程執(zhí)行其他渲染任務(wù)的時(shí)間。針對(duì)這種情況我們可以采用以下兩種策略:

          • 一種是將一次執(zhí)行的函數(shù)分解為多個(gè)任務(wù),使得每次的執(zhí)行時(shí)間不要過久。
          • 另一種是采用?Web Workers
        • DOM操作相關(guān)的優(yōu)化。瀏覽器有渲染引擎JS引擎,所以當(dāng)用JS操作DOM時(shí),這兩個(gè)引擎要通過接口互相“交流”,因此每一次操作DOM(包括只是訪問DOM的屬性),都要進(jìn)行引擎之間解析的開銷,所以常說要減少 DOM 操作??偨Y(jié)下來有以下幾點(diǎn):

          • 緩存一些計(jì)算屬性,如let left = el.offsetLeft
          • 通過DOMclass來集中改變樣式,而不是通過style一條條的去修改。
          • 分離讀寫操作?,F(xiàn)代的瀏覽器都有渲染隊(duì)列的機(jī)制。
          • 放棄傳統(tǒng)操作DOM的時(shí)代,基于vue/react等采用virtual dom的框架
        • 合理利用 CSS 合成動(dòng)畫。合成動(dòng)畫是直接在合成線程上執(zhí)行的,這和在主線程上執(zhí)行的布局、繪制等操作不同,如果主線程被?JavaScript?或者一些布局任務(wù)占用,CSS?動(dòng)畫依然能繼續(xù)執(zhí)行。所以要盡量利用好?CSS?合成動(dòng)畫,如果能讓?CSS?處理動(dòng)畫,就盡量交給?CSS?來操作。

        • CSS選擇器優(yōu)化。我們知道CSS引擎查找是從右向左匹配的。所以基于此有以下幾條優(yōu)化方案:

          • 盡量不要使用通配符
          • 少用標(biāo)簽選擇器
          • 盡量利用屬性繼承特性
        • CSS屬性優(yōu)化。瀏覽器繪制圖像時(shí),CSS的計(jì)算也是耗費(fèi)性能的,一些屬性需瀏覽器進(jìn)行大量的計(jì)算,屬于昂貴的屬性(box-shadowsborder-radius、transforms、filters、opcity:nth-child等),這些屬性在日常開發(fā)中經(jīng)常用到,所以并不是說不要用這些屬性,而是在開發(fā)中,如果有其它簡(jiǎn)單可行的方案,那可以優(yōu)先選擇沒有昂貴屬性的方案。

        • 避免頻繁的垃圾回收。我們知道?JavaScript?使用了自動(dòng)垃圾回收機(jī)制,如果在一些函數(shù)中頻繁創(chuàng)建臨時(shí)對(duì)象,那么垃圾回收器也會(huì)頻繁地去執(zhí)行垃圾回收策略。這樣當(dāng)垃圾回收操作發(fā)生時(shí),就會(huì)占用主線程,從而影響到其他任務(wù)的執(zhí)行,嚴(yán)重的話還會(huì)讓用戶產(chǎn)生掉幀、不流暢的感覺。

        緩存

        緩存可以說是性能優(yōu)化中簡(jiǎn)單高效的一種優(yōu)化方式了。一個(gè)優(yōu)秀的緩存策略可以縮短網(wǎng)頁(yè)請(qǐng)求資源的距離,減少延遲,并且由于緩存文件可以重復(fù)利用,還可以減少帶寬,降低網(wǎng)絡(luò)負(fù)荷。下圖是瀏覽器緩存的查找流程圖:64ce56ef629a58af0a5030537d969487.webp瀏覽器緩存相關(guān)的知識(shí)點(diǎn)還是很多的,這里我有整理一張圖:7fc8ff32afbfc8bb3827d468f069fb2b.webp關(guān)于瀏覽器緩存的詳細(xì)介紹說明,可以參考我之前的這篇文章,這里就不贅述了。

        DNS 相關(guān)優(yōu)化

        DNS全稱Domain Name System。它是互聯(lián)網(wǎng)的“通訊錄”,它記錄了域名與實(shí)際ip地址的映射關(guān)系。每次我們?cè)L問一個(gè)網(wǎng)站,都要通過各級(jí)的DNS服務(wù)器查詢到該網(wǎng)站的服務(wù)器ip,然后才能訪問到該服務(wù)器。

        DNS相關(guān)的優(yōu)化一般涉及到兩點(diǎn):瀏覽器DNS緩存和DNS預(yù)解析。

        DNS緩存

        一圖勝千言:de33545a43f541a27c6396355d9ed5f7.webp

        • 瀏覽器會(huì)先檢查瀏覽器緩存(瀏覽器緩存有大小和時(shí)間限制),時(shí)間過長(zhǎng)可能導(dǎo)致IP地址變化,無法解析正確IP地址,過短就會(huì)讓瀏覽器重復(fù)解析域名,一般為幾分鐘。
        • 如果瀏覽器緩存沒有對(duì)應(yīng)域名,則會(huì)去操作系統(tǒng)緩存中查找。
        • 如果還沒有找到,域名就會(huì)發(fā)送到本地區(qū)的域名服務(wù)器(一般由互聯(lián)網(wǎng)供應(yīng)商提供,電信、聯(lián)通之類),一般在本地區(qū)的域名服務(wù)器上都能找到了。
        • 當(dāng)然也可能本地域名服務(wù)器也沒找到,那本地域名服務(wù)器就開始遞歸查找。

        一般而言,瀏覽器解析DNS需要20-120ms,因此DNS解析可優(yōu)化之處幾乎沒有。但存在這樣一個(gè)場(chǎng)景,網(wǎng)站有很多圖片在不同域名下,那如果在登錄頁(yè)就提前解析了之后可能會(huì)用到的域名,使解析結(jié)果緩存過,這樣縮短了DNS解析時(shí)間,提高網(wǎng)站整體上的訪問速度了,這就是DNS預(yù)解析。

        DNS預(yù)解析

        來看下 MDN 對(duì)于DNS預(yù)解析的定義吧:

        ?

        X-DNS-Prefetch-Control?頭控制著瀏覽器的?DNS?預(yù)讀取功能。DNS?預(yù)讀取是一項(xiàng)使瀏覽器主動(dòng)去執(zhí)行域名解析的功能,其范圍包括文檔的所有鏈接,無論是圖片的,CSS?的,還是?JavaScript?等其他用戶能夠點(diǎn)擊的?URL

        ?

        因?yàn)轭A(yù)讀取會(huì)在后臺(tái)執(zhí)行,所以?DNS?很可能在鏈接對(duì)應(yīng)的東西出現(xiàn)之前就已經(jīng)解析完畢。這能夠減少用戶點(diǎn)擊鏈接時(shí)的延遲。

        我們這里就簡(jiǎn)單看一下如何去做DNS預(yù)解析

        • 在頁(yè)面頭部加入,這樣瀏覽器對(duì)整個(gè)頁(yè)面進(jìn)行預(yù)解析
        <meta?http-equiv="x-dns-prefetch-control"?content="on">
        • 通過 link 標(biāo)簽手動(dòng)添加要解析的域名,比如:
        <link?rel="dns-prefetch"?href="http://img10.360buyimg.com"/>

        參考

        李兵 「瀏覽器工作原理與實(shí)踐」


        瀏覽 41
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日韩精品一区二区在线 | 一本大道AV片加勒比无码 | 五月色在线播放 | 大香蕉亚洲 | 国产精品久久久久久久久久红粉 | 人人操天天摸 | 亚洲欧美一区二区三区 | 成人综合婷婷国产精品久久 | 一区二区三区电影网 | 午夜三区 |