做者:李佳曉 原文:學而思網校技術團隊css
合格的開發者知道怎麼作,而優秀的開發者知道爲何這麼作。html
這句話來自《web性能權威指南》,我一直很喜歡,而本文嘗試從瀏覽器渲染原理探討如何進行性能提高。
全文將從網絡通訊以及頁面渲染兩個過程去探討瀏覽器的行爲及在此過程當中咱們能夠針對那些點進行優化,有些的不足之處還請各位不吝雅正。前端
關於瀏覽器渲染機制已是老生常談,並且網上現有資料中有很是多的優秀資料對此進行闡述。遺憾的是網上的資料參差不齊,常常在不一樣的文檔中對同一件事的描述出現了極大的差別。懷着嚴謹求學的態度通過大量資料的查閱和請教,將會在後文總結出一個完整的流程。linux
DOM樹的構建是從接受到文檔開始的,先將字節轉化爲字符,而後字符轉化爲標記,接着標記構建dom樹。這個過程被分爲標記化和樹構建
而這是一個漸進的過程。爲達到更好的用戶體驗,呈現引擎會力求儘快將內容顯示在屏幕上。它沒必要等到整個 HTML 文檔解析完畢以後,就會開始構建呈現樹和設置佈局。在不斷接收和處理來自網絡的其他內容的同時,呈現引擎會將部份內容解析並顯示出來。
參考文檔:
http://taligarsiel.com/Projec...nginx
這三個過程在實際進行的時候又不是徹底獨立,而是會有交叉。會形成一邊加載,一邊解析,一邊渲染的工做現象。
參考文檔:web
http://www.jianshu.com/p/2d52...算法
css的解析是自右至左逆向解析的,嵌套越多越增長瀏覽器的工做量,而不會越快。
由於若是正向解析,例如「div div p em」,咱們首先就要檢查當前元素到 html 的整條路徑,找到最上層的 div,再往下找,若是遇到不匹配就必須回到最上層那個 div,往下再去匹配選擇器中的第一個 div,回溯若干次才能肯定匹配與否,效率很低。
逆向匹配則不一樣,若是當前的 DOM 元素是 div,而不是 selector 最後的 em,那隻要一步就能排除。只有在匹配時,纔會不斷向上找父節點進行驗證。
打個好比 p span.showing
你認爲從一個p元素下面找到全部的span元素並判斷是否有class showing快,仍是找到全部的span元素判斷是否有class showing而且包括一個p父元素快
參考文檔:
http://www.imooc.com/code/4570chrome
當瀏覽器拿到HTTP報文時呈現引擎將開始解析 HTML 文檔,並將各標記逐個轉化成「內容樹」上的 DOM 節點。同時也會解析外部 CSS 文件以及樣式元素中的樣式數據。HTML 中這些帶有視覺指令的樣式信息將用於建立另外一個樹結構:呈現樹。瀏覽器將根據呈現樹進行佈局繪製。數據庫
以上就是頁面渲染的大體流程。那麼瀏覽器從用戶輸入網址以後到底作了什麼呢?如下將會進行一個完整的梳理。鑑於本文是前端向的因此梳理內容會有所偏重。而從輸入到呈現能夠分爲兩個部分:網絡通訊和頁面渲染跨域
咱們首先來看網絡通訊部分:
若是用戶輸入的是ip地址則直接進入第三條。但去記錄毫無規律且冗長的ip地址顯然不是易事,因此一般都是輸入的域名,此時就會進行dns解析。所謂DNS(Domain Name System)指域名系統。因特網上做爲域名和IP地址相互映射的一個分佈式數據庫,可以使用戶更方便的訪問互聯網,而不用去記住可以被機器直接讀取的IP數串。經過主機名,最終獲得該主機名對應的IP地址的過程叫作域名解析(或主機名解析)。這個過程以下所示:
瀏覽器會首先搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有2分鐘左右,且只能容納1000條緩存)。
拿到域名對應的IP地址以後,User-Agent(通常是指瀏覽器)會以一個隨機端口(1024< 端口 < 65535)向服務器的WEB程序(經常使用的有httpd,nginx等)80端口發起TCP的鏈接請求。這個鏈接請求(原始的http請求通過TCP/IP4層模型的層層封包)到達服務器端後(這中間經過各類路由設備,局域網內除外),進入到網卡,而後是進入到內核的TCP/IP協議棧(用於識別該鏈接請求,解封包,一層一層的剝開),還有可能要通過Netfilter防火牆(屬於內核的模塊)的過濾,最終到達WEB程序,最終創建了TCP/IP的鏈接。
tcp創建鏈接和關閉鏈接均須要一個完善的確認機制,咱們通常將鏈接稱爲三次握手,而鏈接關閉稱爲四次揮手。而不管是三次握手仍是四次揮手都須要數據從客戶端到服務器的一次完整傳輸。將數據從客戶端到服務端經歷的一個完整時延包括:
以上的延遲總和就是客戶端到服務器的總延遲時間。所以每一次的鏈接創建和斷開都是有巨大代價的。所以去掉沒必要要的資源和資源合併(包括js及css資源合併、雪碧圖等)纔會成爲性能優化繞不開的方案。可是好消息是隨着協議的發展咱們將對性能優化這個主題有着新的見解和思考。雖然還未到來,但也不遠了。若是你感到好奇那就接着往下看。
如下簡述下tcp創建鏈接的過程:
此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。握手過程當中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP鏈接一旦創建,在通訊雙方中的任何一方主動關閉鏈接以前,TCP鏈接都將被一直保持下去
這裏注意, 三次握手是不攜帶數據的,而是在握手完畢纔開始數據傳輸。所以若是每次數據請求都須要從新進行完整的tcp鏈接創建,通訊時延的耗時是難以估量的!這也就是爲何咱們老是能聽到資源合併減小請求次數的緣由。
下面來看看HTTP如何在協議層面幫咱們進行優化的:
在http1.0時代,每一個TCP鏈接只能發送一個請求。發送數據完畢,鏈接就關閉,若是還要請求其餘資源,就必須再新建一個鏈接。 TCP鏈接的新建成本很高,由於須要客戶端和服務器三次握手,而且開始時發送速率較慢(TCP的擁塞控制開始時會啓動慢啓動算法)。在數據傳輸的開始只能發送少許包,並隨着網絡狀態良好(無擁塞)指數增加。但遇到擁塞又要從新從1個包開始進行傳輸。
如下圖爲例,慢啓動時第一次數據傳輸只能傳輸一組數據,獲得確認後傳輸2組,每次翻倍,直到達到閾值16時開始啓用擁塞避免算法,既每次獲得確認後數據包只增長一個。當發生網絡擁塞後,閾值減半從新開始慢啓動算法。
所以爲避免tcp鏈接的三次握手耗時及慢啓動引發的發送速度慢的狀況,應儘可能減小tcp鏈接的次數。
而HTTP1.0每一個數據請求都須要從新創建鏈接的特色使得HTTP 1.0版本的性能比較差。隨着網頁加載的外部資源愈來愈多,這個問題就愈發突出了。 爲了解決這個問題,有些瀏覽器在請求時,用了一個非標準的Connection字段。 Kepp-alive 一個能夠複用的TCP鏈接就創建了,直到客戶端或服務器主動關閉鏈接。可是,這不是標準字段,不一樣實現的行爲可能不一致,所以不是根本的解決辦法。
http1.1(如下簡稱h1.1) 版的最大變化,就是引入了持久鏈接(persistent connection),即TCP鏈接默認不關閉,能夠被多個請求複用,不用聲明Connection: keep-alive。 客戶端和服務器發現對方一段時間沒有活動,就能夠主動關閉鏈接。不過,規範的作法是,客戶端在最後一個請求時,發送Connection: close,明確要求服務器關閉TCP鏈接。 目前,對於同一個域名,大多數瀏覽器容許同時創建6個持久鏈接。相比與http1.0,1.1的頁面性能有了巨大提高,由於省去了不少tcp的握手揮手時間。下圖第一種是tcp創建後只能發一個請求的http1.0的通訊狀態,而擁有了持久鏈接的h1.1則避免了tcp握手及慢啓動帶來的漫長時延。
從圖中能夠看到相比h1.0,h1.1的性能有所提高。然而雖然1.1版容許複用TCP鏈接,可是同一個TCP鏈接裏面,全部的數據通訊是按次序進行的。服務器只有處理完一個迴應,纔會進行下一個迴應。要是前面的迴應特別慢,後面就會有許多請求排隊等着。這稱爲"隊頭堵塞"(Head-of-line blocking)。 爲了不這個問題,只有三種方法:一是減小請求數,二是同時多開持久鏈接。這致使了不少的網頁優化技巧,好比合並腳本和樣式表、將圖片嵌入CSS代碼、域名分片(domain sharding)等等。若是HTTP協議能繼續優化,這些額外的工做是能夠避免的。三是開啓pipelining,不過pipelining並非救世主,它也存在很多缺陷:
鑑於以上種種緣由,pipelining的支持度並不友好。能夠看看chrome對pipelining的描述:
https://www.chromium.org/deve...
2015年,HTTP/2 發佈。它不叫 HTTP/2.0,是由於標準委員會不打算再發布子版本了,下一個新版本將是 HTTP/3。HTTP2將具備如下幾個主要特色:
就這幾個點咱們分別討論一下:
就多工來看:雖然http1.1支持了pipelining,可是仍然會有隊首阻塞問題,若是瀏覽器同時發出http請求請求和css,服務器端處理css請求耗時20ms,可是由於先請求資源是html,此時的css儘管已經處理好了但仍不能返回,而須要等待html處理好一塊兒返回,此時的客戶端就處於盲等狀態,而事實上若是服務器先處理好css就先返回css的話,瀏覽器就能夠開始解析css了。而多工的出現就解決了http以前版本協議的問題,極大的提高了頁面性能。縮短了通訊時間。咱們來看看有了多工以後有那些影響:
就頭部壓縮來看:HTTP/1.1 版的頭信息是ASCII編碼,也就是不通過壓縮的,當咱們請求只攜帶少許數據時,http頭部可能要比載荷要大許多,尤爲是有了很長的cookie以後這一點尤其顯著,頭部壓縮毫無疑問能夠對性能有很大提高。
就服務器推送來看:少去了資源請求的時間,服務端能夠將可能用到的資源推送給服務端以待使用。這項能力幾乎是革新了以前應答模式的認知,對性能提高也有巨大幫助。
所以不少優化都是在基於tcp及http的一些問題來避免和繞過的。事實上多數的優化都是針對網絡通訊這個部分在作。
以上是網絡通訊部分,接下來將會對頁面渲染部分進行敘述。
注意,異步執行是指下載。執行js時仍然會阻塞。
渲染樹構建完畢後將會進行佈局。佈局使用流模型的Layout算法。所謂流模型,便是指Layout的過程只需進行一遍便可完成,後出如今流中的元素不會影響前出如今流中的元素,Layout過程只需從左至右從上至下一遍完成便可。但實際實現中,流模型會有例外。Layout是一個遞歸的過程,每一個節點都負責本身及其子節點的Layout。Layout結果是相對父節點的座標和尺寸。其過程能夠簡述爲:
HTML解析是一個將字節轉化爲字符,字符解析爲標記,標記生成節點,節點構建樹的過程。。CSS樣式的解析則因爲複雜的樣式層疊而變得複雜。對此不一樣的渲染引擎在處理上有所差別,後文將會就這點進行詳細講解
標記化算法:
是詞法分析過程,將輸入內容解析成多個標記。HTML標記包括起始標記、結束標記、屬性名稱和屬性值。標記生成器識別標記,傳遞給樹構造器,而後接受下一個字符以識別下一個標記;如此反覆直到輸入的結束。
該算法的輸出結果是 HTML 標記。該算法使用狀態機來表示。每個狀態接收來自輸入信息流的一個或多個字符,並根據這些字符更新下一個狀態。當前的標記化狀態和樹結構狀態會影響進入下一狀態的決定。這意味着,即便接收的字符相同,對於下一個正確的狀態也會產生不一樣的結果,具體取決於當前的狀態。
樹構建算法:
在樹構建階段,以 Document 爲根節點的 DOM 樹也會不斷進行修改,向其中添加各類元素。
標記生成器發送的每一個節點都會由樹構建器進行處理。規範中定義了每一個標記所對應的 DOM 元素,這些元素會在接收到相應的標記時建立。這些元素不只會添加到 DOM 樹中,還會添加到開放元素的堆棧中。此堆棧用於糾正嵌套錯誤和處理未關閉的標記。其算法也能夠用狀態機來描述。這些狀態稱爲「插入模式」。
如下將會舉一個例子來分析這兩個階段:
標記化:初始狀態是數據狀態。
仍是以上的例子,咱們來看看樹構建
樹構建:樹構建階段的輸入是一個來自標記化階段的標記序列。
在此階段,瀏覽器會將文檔標註爲交互狀態,並開始解析那些處於「deferred」模式的腳本,也就是那些應在文檔解析完成後才執行的腳本。而後,文檔狀態將設置爲「完成」,一個「加載」事件將隨之觸發。
完整解析過程以下圖:
每個呈現器都表明了一個矩形的區域,一般對應於相關節點的 CSS 框,這一點在 CSS2 規範中有所描述。它包含諸如寬度、高度和位置等幾何信息。就是咱們 CSS 裏常提到的盒子模型。構建呈現樹時,須要計算每個呈現對象的可視化屬性。這是經過計算每一個元素的樣式屬性來完成的。因爲應用規則涉及到至關複雜的層疊規則,因此給樣式樹的構建形成了巨大的困難。爲何說它複雜?由於同一個元素可能涉及多條樣式,就須要判斷最終到底哪條樣式生效。首先咱們來了解一下css的樣式層疊規則
根據不一樣的樣式來源優先級排列從小到大:
!important 規則1:根據 CSS2.1 規範中的描述,’!important’ 能夠提升樣式的優先級,它對樣式優先級的影響是巨大的。
注意,’!important’ 規則在 IE7 之前的版本中是被支持不完善。所以,常常被用做 CSS hack2。
若是來源和重要性相同則根據CSS specificity來進行斷定。
特殊性的值能夠看做是一個由四個數組成的一個組合,用 a,b,c,d 來表示它的四個位置。 依次比較 a,b,c,d 這個四個數比較其特殊性的大小。好比,a 值相同,那麼 b 值大的組合特殊性會較大,以此類推。 注意,W3C 中並非把它做爲一個 4 位數來看待的。
a,b,c,d 值的肯定規則:
在這裏咱們來看一個W3C給出的例子:
那麼在以下例子中字體的顯示應當爲綠色:
總結爲表格的話計算規則以下:
爲了簡化樣式計算,Firefox 還採用了另外兩種樹:規則樹和樣式上下文樹。Webkit 也有樣式對象,但它們不是保存在相似樣式上下文樹這樣的樹結構中,只是由 DOM 節點指向此類對象的相關樣式。
樣式上下文包含端值。要計算出這些值,應按照正確順序應用全部的匹配規則,並將其從邏輯值轉化爲具體的值。例如,若是邏輯值是屏幕大小的百分比,則須要換算成絕對的單位。規則樹的點子真的很巧妙,它使得節點之間能夠共享這些值,以免重複計算,還能夠節約空間。
全部匹配的規則都存儲在樹中。路徑中的底層節點擁有較高的優先級。規則樹包含了全部已知規則匹配的路徑。規則的存儲是延遲進行的。規則樹不會在開始的時候就爲全部的節點進行計算,而是隻有當某個節點樣式須要進行計算時,纔會向規則樹添加計算的路徑。
這個想法至關於將規則樹路徑視爲詞典中的單詞。若是咱們已經計算出以下的規則樹:
假設咱們須要爲內容樹中的另外一個元素匹配規則,而且找到匹配路徑是 B - E - I(按照此順序)。因爲咱們在樹中已經計算出了路徑 A - B - E - I - L,所以就已經有了此路徑,這就減小了如今所需的工做量。
那麼Firefox是如何解決樣式計算難題的呢?接下來看一個樣例,假設咱們有以下HTML代碼:
而且咱們有以下規則:
爲了簡便起見,咱們只須要填充兩個結構:color 結構和 margin 結構。color 結構只包含一個成員(即「color」),而 margin 結構包含四條邊。
造成的規則樹以下圖所示(節點的標記方式爲「節點名 : 指向的規則序號」):
上下文樹以下圖所示(節點名 : 指向的規則節點):
假設咱們解析 HTML 時遇到了第二個 <div> 標記,咱們須要爲此節點建立樣式上下文,並填充其樣式結構。
通過規則匹配,咱們發現該 <div> 的匹配規則是第 一、2 和 6 條。這意味着規則樹中已有一條路徑可供咱們的元素使用,咱們只須要再爲其添加一個節點以匹配第 6 條規則(規則樹中的 F 節點)。
咱們將建立樣式上下文並將其放入上下文樹中。新的樣式上下文將指向規則樹中的 F 節點。
如今咱們須要填充樣式結構。首先要填充的是 margin 結構。因爲最後的規則節點 (F) 並無添加到 margin 結構,咱們須要上溯規則樹,直至找到在先前節點插入中計算過的緩存結構,而後使用該結構。咱們會在指定 margin 規則的最上層節點(即 B 節點)上找到該結構。
咱們已經有了 color 結構的定義,所以不能使用緩存的結構。因爲 color 有一個屬性,咱們無需上溯規則樹以填充其餘屬性。咱們將計算端值(將字符串轉化爲 RGB 等)並在此節點上緩存通過計算的結構。
第二個 元素處理起來更加簡單。咱們將匹配規則,最終發現它和以前的 span 同樣指向規則 G。因爲咱們找到了指向同一節點的同級,就能夠共享整個樣式上下文了,只需指向以前 span 的上下文便可。
對於包含了繼承自父代的規則的結構,緩存是在上下文樹中進行的(事實上 color 屬性是繼承的,可是 Firefox 將其視爲 reset 屬性,並緩存到規則樹上)。
例如,若是咱們在某個段落中添加 font 規則:
那麼,該段落元素做爲上下文樹中的 div 的子代,就會共享與其父代相同的 font 結構(前提是該段落沒有指定 font 規則)。
在 Webkit 中沒有規則樹,所以會對匹配的聲明遍歷 4 次。首先應用非重要高優先級的屬性(因爲做爲其餘屬性的依據而應首先應用的屬性,例如 display),接着是高優先級重要規則,而後是普通優先級非重要規則,最後是普通優先級重要規則。這意味着屢次出現的屬性會根據正確的層疊順序進行解析。最後出現的最終生效。
樣式樹和DOM樹鏈接在一塊兒造成一個渲染樹,渲染樹用來計算可見元素的佈局而且做爲將像素渲染到屏幕上的過程的輸入。值得一提的是,Gecko 將視覺格式化元素組成的樹稱爲「框架樹」。每一個元素都是一個框架。Webkit 使用的術語是「渲染樹」,它由「呈現對象」組成。 Webkit 和 Gecko 使用的術語略有不一樣,但總體流程是基本相同的。
接下來未來看一下兩種渲染引擎的工做流程:
Webkit 主流程:
Mozilla 的 Gecko 呈現引擎主流程
雖然 Webkit 和 Gecko 使用的術語略有不一樣,但總體流程是基本相同的。
Gecko 將視覺格式化元素組成的樹稱爲「框架樹」。每一個元素都是一個框架。
Webkit 使用的術語是「呈現樹」,它由「呈現對象」組成。
對於元素的放置,Webkit 使用的術語是「佈局」,而 Gecko 稱之爲「重排」。
對於鏈接 DOM 節點和可視化信息從而建立呈現樹的過程,Webkit 使用的術語是「附加」。有一個細微的非語義差異,就是 Gecko 在 HTML 與 DOM 樹之間還有一個稱爲「內容槽」的層,用於生成 DOM 元素。咱們會逐一論述流程中的每一部分。
Repaint(重繪)——屏幕的一部分要重畫,好比某個CSS的背景色變了。可是元素的幾何尺寸沒有變。
Reflow(重排)——意味着元件的幾何尺寸變了,咱們須要從新驗證並計算Render Tree。是Render Tree的一部分或所有發生了變化。這就是Reflow,或是Layout。reflow 會從這個root frame開始遞歸往下,依次計算全部的結點幾何尺寸和位置,在reflow過程當中,可能會增長一些frame,好比一個文本字符串必需被包裝起來。
onload事件——當 onload 事件觸發時,頁面上全部的DOM,樣式表,腳本,圖片,flash都已經加載完成了。
DOMContentLoaded事件——當 DOMContentLoaded 事件觸發時,僅當DOM加載完成,不包括樣式表,圖片,flash。
首屏時間——當瀏覽器顯示第一屏頁面所消耗的時間,在國內的網絡條件下,一般一個網站,若是「首屏時間」在2秒之內是比較優秀的,5秒之內用戶能夠接受,10秒以上就不可容忍了。
白屏時間——指瀏覽器開始顯示內容的時間。可是在傳統的採集方式裏,是在HTML的頭部標籤結尾裏記錄時間戳,來計算白屏時間。在這個時刻,瀏覽器開始解析身體標籤內的內容。而現代瀏覽器不會等待CSS樹(全部CSS文件下載和解析完成)和DOM樹(整個身體標籤解析完成)構建完成纔開始繪製,而是立刻開始顯示中間結果。因此常常在低網速的環境中,觀察到頁面由上至下緩慢顯示完,或者先顯示文本內容後再重繪成帶有格式的頁面內容。
本文的主題在於從瀏覽器的渲染過程談頁面優化。瞭解瀏覽器如何通訊並將拿到的數據如何進行解析渲染,本節將從網絡通訊、頁面渲染、資源預取及如何除了以上方案外,如何藉助chrome來針對一個頁面進行實戰優化四個方面來談。
從網絡通訊過程入手能夠作的優化
減小DNS查找
每一次主機名解析都須要一次網絡往返,從而增長請求的延遲時間,同時還會阻塞後續請求。
重用TCP鏈接
儘量使用持久鏈接,以消除 TCP 握手和慢啓動延遲;
減小HTTP重定向
HTTP 重定向極費時間,特別是不一樣域名之間的重定向,更加費時;這裏面既有額外的 DNS 查詢、TCP 握手,還有其餘延遲。最佳的重定向次數爲零。
使用 CDN(內容分發網絡)
把數據放到離用戶地理位置更近的地方,能夠顯著減小每次 TCP 鏈接的網絡延遲,增大吞吐量。
去掉沒必要要的資源
任何請求都不如沒有請求快。說到這,全部建議都無需解釋。延遲是瓶頸,最快的速度莫過於什麼也不傳輸。然而,HTTP 也提供了不少額外的機制,好比緩存和壓縮,還有與其版本對應的一些性能技巧。
在客戶端緩存資源
應該緩存應用資源,從而避免每次請求都發送相同的內容。(瀏覽器緩存)
傳輸壓縮過的內容
傳輸前應該壓縮應用資源,把要傳輸的字節減至最少:確保每種要傳輸的資源採用最好的壓縮手段。(Gzip,減小60%~80%的文件大小)
消除沒必要要的請求開銷
減小請求的 HTTP 首部數據(好比HTTPcookie),節省的時間至關於幾回往返的延遲時間。
並行處理請求和響應
請求和響應的排隊都會致使延遲,不管是客戶端仍是服務器端。這一點常常被忽視,但卻會無謂地致使很長延遲。
針對協議版本採起優化措施
HTTP 1.x 支持有限的並行機制,要求打包資源、跨域分散資源,等等。相對而言,
HTTP 2.0 只要創建一個鏈接就能實現最優性能,同時無需針對 HTTP 1.x 的那些優化方法。
可是壓縮、使用緩存、減小dns等的優化方案不管在哪一個版本都一樣適用
你須要瞭解的資源預取
preload :能夠對當前頁面所需的腳本、樣式等資源進行預加載,而無需等到解析到 script 和 link 標籤時才進行加載。這一機制使得資源能夠更早的獲得加載並可用,且更不易阻塞頁面的初步渲染,進而提高性能。
用法文檔:
https://developer.mozilla.org...
prefetch:prefetch 和 preload 同樣,都是對資源進行預加載,可是 prefetch 通常預加載的是其餘頁面會用到的資源。 固然,prefetch 不會像 preload 同樣,在頁面渲染的時候加載資源,而是利用瀏覽器空閒時間來下載。當進入下一頁面,就可直接從 disk cache 裏面取,既不影響當前頁面的渲染,又提升了其餘頁面加載渲染的速度。
用法文檔:
https://developer.mozilla.org...
subresource: 被Chrome支持了有一段時間,而且已經有些搔到預加載當前導航/頁面(所含有的資源)的癢處了。但它有一個問題——沒有辦法處理所獲取內容的優先級(as也並不存在),因此最終,這些資源會以一個至關低的優先級被加載,這使得它能提供的幫助至關有限
prerender:prerender 就像是在後臺打開了一個隱藏的 tab,會下載全部的資源、建立DOM、渲染頁面、執行js等等。若是用戶進入指定的連接,隱藏的這個頁面就會立馬進入用戶的視線。 可是要注意,必定要在十分肯定用戶會點擊某個連接時才使用該特性,不然客戶端會無故的下載不少資源和渲染這個頁面。 正如任何提早動做同樣,預判老是有必定風險出錯。若是提早的動做是昂貴的(好比高CPU、耗電、佔用帶寬),就要謹慎使用了。
preconnect: preconnect 容許瀏覽器在一個 HTTP 請求正式發給服務器前預先執行一些操做,這包括
dns-prefetch:經過 DNS 預解析來告訴瀏覽器將來咱們可能從某個特定的 URL 獲取資源,當瀏覽器真正使用到該域中的某個資源時就能夠儘快地完成 DNS 解析
這些屬性雖然並不是全部瀏覽器都支持,可是不支持的瀏覽器也只是不處理而已,而是別的話則會省去不少時間。所以,合理的使用資源預取能夠顯著提升頁面性能。
高效合理的css選擇符能夠減輕瀏覽器的解析負擔。
由於css是逆向解析的因此應當避免多層嵌套。
避免使用通配規則。如 *{} 計算次數驚人!只對須要用到的元素進行選擇
儘可能少的去對標籤進行選擇,而是用class。如:#nav li{},能夠爲li加上nav_item的類名,以下選擇.nav_item{}
不要去用標籤限定ID或者類選擇符。如:ul#nav,應該簡化爲#nav
儘可能少的去使用後代選擇器,下降選擇器的權重值。後代選擇器的開銷是最高的,儘可能將選擇器的深度降到最低,最高不要超過三層,更多的使用類來關聯每個標籤元素。
考慮繼承。瞭解哪些屬性是能夠經過繼承而來的,而後避免對這些屬性重複指定規則
從js層面談頁面優化
①解決渲染阻塞
若是在解析HTML標記時,瀏覽器遇到了JavaScript,解析會中止。只有在該腳本執行完畢後,HTML渲染纔會繼續進行。因此這阻塞了頁面的渲染。
解決方法:在標籤中使用 async或defer特性
②減小對DOM的操做
對DOM操做的代價是高昂的,這在網頁應用中的一般是一個性能瓶頸。
解決辦法:修改和訪問DOM元素會形成頁面的Repaint和Reflow,循環對DOM操做更是罪惡的行爲。因此請合理的使用JavaScript變量儲存內容,考慮大量DOM元素中循環的性能開銷,在循環結束時一次性寫入。
減小對DOM元素的查詢和修改,查詢時可將其賦值給局部變量。
③使用JSON格式來進行數據交換
JSON是一種輕量級的數據交換格式,採用徹底獨立於語言的文本格式,是理想的數據交換格式。同時,JSON是 JavaScript原生格式,這意味着在 JavaScript 中處理 JSON數據不須要任何特殊的 API 或工具包。
④讓須要常常改動的節點脫離文檔流
由於重繪有時確實不可避免,因此只能儘量限制重繪的影響範圍。
如何藉助chrome針對性優化頁面
首先打開控制檯,點擊Audits一欄,會看到以下表單。在選取本身須要模擬測試的狀況後點擊run audits,便可開始頁面性能分析。
而後將會獲得分析結果及優化建議:
咱們能夠逐項根據現有問題進行優化,如性能類目(performance)中的第一項優化建議延遲加載屏幕外圖像(defer offscreen images),點擊後就能看到詳情如下詳情:
而具體頁面的指標優化能夠根據給出的建議進行逐條優化。目前提供的性能分析及建議的列表包括性能分析、漸進式web應用、最佳實踐、無障礙訪問及搜索引擎優化五個部分。基本上涵蓋了常見優化方案及性能點的方方面面,開發時合理使用也能更好的提高頁面性能
相信以上優化方案之因此行之有效的緣由大均可以在本文中找出緣由。理論是用來指導實踐的,即不能閉門造車式的埋頭苦幹,也不能絕不實踐的誇誇其談。這樣纔會造成完整的知識體系,讓知識體系樹更加龐大。知道該如何優化是一回事,真正合理應用是另外一回事,要有好的性能,要着手於能作的每一件「小事」。
性能優化是一門藝術,更是一門綜合藝術。這其中涉及不少知識點。而這些知識點都有不少不錯的文章進行了總結。若是你想深刻探究或許這裏推薦的文章會給你啓發。
HTTP2詳解:
https://www.jianshu.com/p/e57...
TCP擁塞控制:
https://www.cnblogs.com/losby...
頁面性能分析網站:
https://gtmetrix.com/analyze....
Timing官方文檔:
https://www.w3.org/TR/navigat...
chrome中的高性能網絡: