一道面試題是如何引起深層次的靈魂拷問?

Hello,豆皮粉們,大家的小可愛來了,這回約稿又獲得來自字節跳動的「songEagle | saucxs」 ,《一道面試題是如何引起深層次的靈魂拷問?》文章寫的由淺入深,從各個方面剖析一道經典面試題須要全方面考慮,儘量考察整個研發測涉及到的知識點,文章以點帶面,詳細敘述,可能存在不足的地方歡迎你們評論,後續再出一個遺漏地方的文章😘。css

圖片

做者:saucxs | songEaglehtml

來源:原創前端

1、前言

有這麼一道面試題,以下:java

面試題:請詳細介紹一下從輸入 URL 到頁面加載完成的過程?node

這道題的覆蓋面能夠很是廣,很適合做爲一道承載知識體系的題目。nginx

每個前端人員,若是要往更高階發展,必然會將本身的知識體系梳理一遍,沒有牢固的知識體系,沒法往更高處走!git

你不信這道題承載的知識體系龐大?往下看github

2、分析題幹web

在對於這道題上,若是對於面試官想要知道的是:簡單敘述仍是深刻敘述。面試

因此須要回答到關鍵詞上,否則多而雜,效果很差,抓不住重點。

接下來咱們從主幹流程和深刻的詳細敘述分別介紹,我以爲這道面試題可能須要15分鐘才能講完。

主幹流程回答:是基本功體現,知識概括能力,面面俱到,點到爲止。

詳細闡述:考察的各個知識點的掌握能力以及掌握到什麼程度。

3、主幹流程

在將瀏覽器渲染原理、JS運行機制、JS引擎解析流程梳理一遍後,感受就跟打通了任督二脈同樣,有了一個總體的架構,之前的知識點都連貫起來了。

一、從瀏覽器接收url到開啓網絡請求線程(涉及到:瀏覽器機制,線程和進程之間的關係等)

二、開啓網絡線程到發出一個完整的http請求(涉及到:dns查詢,tcp/ip請求,5層網絡協議棧等)

三、從服務器接收到請求到對應後臺接收到請求(涉及到:均衡負載,安全攔截,後臺內部的處理等)

四、後臺和前臺的http交互(涉及到:http頭,響應碼,報文結構,cookie等,能夠提下靜態資源的cookie優化,以及編碼解碼如gzip壓縮等)

五、緩存問題:http緩存(涉及到:涉及到http緩存頭部,etag,expired,cache-control等)

六、瀏覽器接收到http數據包後的解析流程(涉及到:html的詞法分析,而後解析成dom樹,同時解析css生成css規則樹,合併生成render樹。而後layout佈局、painting渲染、複合圖層的合成、GPU繪製、外連接處理、loaded和documentloaded等)

七、css可視化格式模型(涉及到:元素渲染規則,如:包含塊,控制框,BFC,IFC等概念)

八、js引擎解析過程(涉及到:js解釋階段,預處理階段,執行階段生成執行上下文,VO(全局對象),做用域鏈,回收機制等)

九、其餘(擴展其餘模塊:跨域,web安全等)

4、從瀏覽器接收到url到開啓網絡請求線程

涉及到:瀏覽器的進程和線程模型,js的運行機制。

一、瀏覽器是多進程的

(1)瀏覽器是多進程的;

(2)不一樣類型的標籤頁會開啓一個新的進程;

(3)相同類型的標籤頁會合併到一個進程中。

瀏覽器中各個進程以及做用:

一、瀏覽器進程:只有1個進程,(1)負責管理各個標籤的建立和銷燬;(2)負責瀏覽器頁面顯示;(3)負責資源的管理和下載;

二、第三方插件進程:能夠是多個進程,負責每個第三方插件的使用,每個第三方插件使用時候會建立一個對應的進程;

三、GPU進程:最多1個進程,負責3D繪製和硬件加速;

四、瀏覽器渲染進程:能夠是多個進程,瀏覽器的內核,每一個tab頁一個進程,主要負責HTML、,css,js等文件的解析,執行和渲染,以及事件處理等。

二、瀏覽器渲染進程(內核進程)

每個tab頁面是瀏覽器內核進程,而後這個每個進程是多線程的,它有幾大類子線程:

(1)GUI線程;(2)JS引擎線程;(3)事件觸發線程;(4)定時器線程;(5)異步的http網絡請求線程

圖片

能夠看出來JS引擎是內核進程中的一個線程,因此常說JS引擎時單線程的。

三、解析URL

輸入url後,會進行解析(URL是統一資源定位符)。

URL包括幾個部分:(1)protocol,協議頭,好比http,https,ftp等;(2)host,主機域名或者IP地址;(3)port,端口號;(4)path,目錄路徑;(5)query,查詢的參數;(6)fragment,#後邊的hash值,用來定位某一個位置。

四、網絡請求時單獨的線程

每一次網絡請求都是須要單獨開闢單獨的線程進行,好比URL解析到http協議,就會新建一個網絡線程去處理資源下載。

所以瀏覽器會根據解析出得協議,開闢一個網絡線程,前往請求資源。

5、開啓網絡線程到發出一個完整的http請求

包括:DNS查詢,tcp/ip請求構建,五層互聯網協議等等。

一、DNS查詢獲得IP

若是輸入的域名,須要DNS解析成IP,流程以下:

(1)瀏覽器有緩存,直接用瀏覽器緩存,沒有就去本機緩存,沒有就看是否是host。

(2)若是尚未,就向DNS域名服務器查詢(這個過程通過路由,路由也有緩存),查詢到對應的IP。

注意:一、域名查詢的時候有可能通過CDN調度器(若是CDN有存儲功能);

二、DNS解析是很耗時的,所以若是解析域名過多,首屏加載會變慢,能夠考慮使用dns-prefetch優化。

二、tcp/ip請求構建

http的本質就是tcp/ip請求構建。須要3次握手規則簡歷鏈接,以及斷開鏈接時候的4次揮手。

tcp將http長報文劃分爲短報文,經過3次握手與服務端創建鏈接,進行可靠的傳輸。

3次握手步驟:

客戶端:hello,你是server麼?服務端:hello,我是server,你是client麼 客戶端:yes,我是client 創建成功以後,接下來就是正式傳輸數據。

而後,等到斷開鏈接時,須要進行4次揮手(由於是全雙工的,因此須要4次握手)。

4次揮手步驟:

主動方:我已經關閉了向你那邊的主動通道了,只能被動接收了 被動方:收到通道關閉的信息 被動方:那我也告訴你,我這邊向你的主動通道也關閉了 主動方:最後收到數據,以後雙方沒法通訊

tcp/ip的併發限制

瀏覽器對同一域名下併發的tcp鏈接是有限制的(2-10個不等)。並且在http1.0中每每一個資源下載就須要對應一個tcp/ip請求。因此針對這個瓶頸,又出現了不少的資源優化方案。

get和post區別

get和post本質都是tcp/ip,可是除了http外層外,在tcp/ip層面也有區別。get會產生1個tcp數據包,post產生2個tcp數據包。

具體就是:

(1)get請求時,瀏覽器會把header和data一塊兒發送出去,服務器響應200(返回數據)。

(2)post請求時,瀏覽器首先發送headers,服務器響應100 continue,瀏覽器再發送data,服務器響應200(返回數據)。

三、五層網絡協議棧

客戶端發出http請求到服務器接收,中間會通過一系列的流程。

客戶端發送請求具體:從應用層發動http請求,到傳輸層經過三次握手簡歷tcp/ip鏈接,再到網絡層的ip尋址,再到數據鏈路層的封裝成幀,最後在物理層經過物理介質傳輸。

服務端接收請求具體:反過來。

五層網絡協議:

一、應用層(DNS,HTTP):DNS解析成IP併發送http請求;

二、傳輸層(TCP,UDP):創建TCP鏈接(3次握手);

三、網絡層(IP,ARP):IP尋址;

四、數據鏈路層(PPP):封裝成幀;

五、物理層(利用物理介質傳輸比特流):物理傳輸(經過雙絞線,電磁波等各類介質)。

其實也有一個完整的OSI七層框架,與之相比,多了會話層、表示層。

OSI七層框架:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層

表示層:主要處理兩個通訊系統中交互信息的表示方式,包括數據格式交換,數據加密和解密,數據壓縮和終端類型轉換等。

會話層:具體管理不一樣用戶和進程之間的對話,如控制登陸和註銷過程。

6、從服務器接收請求到對應後臺接收到請求

服務端接收到請求時,內部會有不少處理。

包括:均衡負載,

一、負載均衡

對於大型項目,併發訪問很大,一臺服務器吃不消,通常會有若干臺服務器組成一個集羣,而後配合反向代理實現均衡負載。均衡負載不止一種實現方式。

歸納的說:用戶發送的請求指向調度服務器(反向代理服務器,好比nginx的均衡負載),而後調度服務器根據實際的調度算法,分配不一樣的請求給對應的集羣中的服務器執行,而後調度服務器等待實際服務器的HTTP響應,而且反饋給用戶。

二、後臺處理

通常後臺都部署到容器中。過程以下:

(1)先是容器接收到請求(好比tomcat容器);

(2)而後對應容器中的後臺程序接收到請求(好比java程序);

(3)而後就是後臺本身的統一處理,處理完畢後響應結果。

具體歸納一下:

(1)通常有的後端有統一的驗證,好比安全攔截,跨域驗證;

(2)若是不符合驗證規則,就直接返回相應的http報文(拒絕請求等);

(3)若是驗證經過了,纔會進入到實際的後臺代碼,此時程序接收到請求,而後執行查詢數據庫,大量計算等等;

(4)等程序執行完畢後,會返回一個http響應包(通常這一步會通過多層封裝);

(5)而後將這個數據包從後端返回到前端,完成交互。

7、後臺和前臺的http交互

先後端的交互,http報文做爲信息的載體。

一、http報文結構

報文通常包括:通用頭部,請求/響應頭部,請求/響應體

1.1 通用頭部

Request Url: 請求的web服務器地址

Request Method: 請求方式 (Get、POST、OPTIONS、PUT、HEAD、DELETE、CONNECT、TRACE)

Status Code: 請求的返回狀態碼,如200表明成功

Remote Address: 請求的遠程服務器地址(會轉爲IP) 好比跨區拒絕時,methord爲option,狀態碼404/405。

其中method分爲兩批次:

HTTP1.0定義了三種請求方法:GET, POST 和 HEAD方法。以及幾種Additional Request Methods:PUT、DELETE、LINK、UNLINK

HTTP1.1定義了八種請求方法:GET、POST、HEAD、OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。好比有些狀態碼來判斷:

  1. 200——代表該請求被成功地完成,所請求的資源發送回客戶端

  2. 304——自從上次請求後,請求的網頁未修改過,請客戶端使用本地緩存

  3. 400——客戶端請求有錯(譬如能夠是安全模塊攔截)

  4. 401——請求未經受權

  5. 403——禁止訪問(譬如能夠是未登陸時禁止)

  6. 404——資源未找到

  7. 500——服務器內部錯誤

  8. 503——服務不可用

大體範圍

  1. 1xx——指示信息,表示請求已接收,繼續處理

  2. 2xx——成功,表示請求已被成功接收、理解、接受

  3. 3xx——重定向,要完成請求必須進行更進一步的操做

  4. 4xx——客戶端錯誤,請求有語法錯誤或請求沒法實現

  5. 5xx——服務器端錯誤,服務器未能實現合法的請求

圖片

1.2 請求頭/響應頭

經常使用的請求頭(部分)

圖片

經常使用的響應頭(部分)

圖片

通常來講,請求頭部和響應頭部是匹配分析的。

好比:

(1)請求頭部的Accept要和響應頭部的Content-Type匹配,不然會報錯;

(2)跨域請求中,請求頭部的Origin要匹配響應頭的Access-Control-Allow-Origin,不然會報跨域錯誤;

(3)使用緩存,請求頭部的if-modified-since,if-none-match分別和響應頭的Last-modified,etag對應。

1.3 請求/響應實體

http請求時,除了頭部,還有消息實體。

請求實體中會將一些須要的參數都放入進入(用於post請求)。

好比:(1)實體中能夠放參數的序列化形式(a=1&b=2這種),或者直接放表單(Form Data對象,上傳時能夠夾雜其餘以及文件)等等。

響應實體中,就是服務端須要傳給客戶端的內容。

通常如今的接口請求時,實體中就是對應信息的json格式,而像頁面請求這種,裏面就是直接放一個html的字符串,而後瀏覽器本身解析並渲染。

1.4 CRLF

CRLF(Carriage-Return Line-Feed),意思是回車換行,通常做爲分隔符存在。

請求頭和實體消息之間有一個CRLF分隔,響應頭部和響應實體之間用一個CRLF分隔。

下圖是對某請求的http報文結構的簡要分析:

二、 cookie以及優化

cookie是瀏覽器的一種本地存儲方式,通常用來幫助客戶端和服務端通訊的,經常使用來進行身份校驗,結合服務端的session使用。

圖片

通常來講,cookie是不容許存放敏感信息的(千萬不要明文存儲用戶名、密碼),由於很是不安全,若是必定要強行存儲,首先,必定要在cookie中設置httponly(這樣就沒法經過js操做了),另外能夠考慮rsa等非對稱加密(由於實際上,瀏覽器本地也是容易被攻克的,並不安全)

好比這樣的場景:

圖片

固然了,針對這種場景,是有優化方案的(多域名拆分)。具體作法就是:

(1)將靜態資源分組,分別放到不一樣的域名下(如static.base.com)

(2)而page.base.com(頁面所在域名)下請求時,是不會帶上static.base.com域名的cookie的,因此就避免了浪費

說到多域名拆分,還有一個問題?

(1)在移動端,若是請求的域名數過多,會下降請求速度(由於域名整套解析流程很浪費時間,並且移動端通常帶寬比不上PC)。

(2)這時候有個優化方案:dns-prefetch(這個是幹嗎的?就是讓瀏覽器空閒時提早解析dns域名,不過請合理使用)

關於cookie的交互,能夠看下圖總結

圖片

三、gzip壓縮

首先,gzip是請求頭裏的Accept-Encoding:瀏覽器支持的壓縮類型之一。gzip是一種壓縮格式,須要瀏覽器支持纔有效(通常瀏覽器都支持),並且gzip的壓縮率很好(高達70%);

而後gzip通常是apach,nginx,tomcat等web服務器開啓。

除了gzip的壓縮格式,還有deflate,沒有gzip高效,不流行。

因此通常只須要在服務器上開啓gzip壓縮,而後以後的請求都是基於gzip壓縮格式的,很是方便。

四、長鏈接和短鏈接

首先咱們看一下tcp/ip的定義:

(1)長鏈接:一個tcp/ip鏈接上能夠連續發送多個數據包,tcp鏈接保持期間,若是乜有數據包發送,須要雙方發檢測包以維持此鏈接,通常須要本身作在線維持(相似於心跳包)。

(2)短鏈接:通訊雙方有數據交互是,簡歷一個tcp鏈接,數據發送完成後,則斷開此tcp鏈接。

咱們再看一下http層面上:

(1)http1.0中,默認是使用的短鏈接,瀏覽器每進行一次http操做,就創建一次鏈接,任務結束就中斷鏈接,好比每個靜態資源請求都是一個單獨的鏈接

(2)http1.1開始,默認是使用長鏈接,長鏈接會設置connection: keep-alive,在長鏈接的狀況下,當一個網頁打開後,客戶端和服務端之間用於傳輸http的tcp鏈接不會關閉,若是客戶端再次訪問服務器這個頁面,會繼續使用這一條已經創建起來的鏈接。

注意:kee-alive不會永遠保持,他有一個持續時間,通常服務中進行配置,另外長鏈接是須要客戶端和服務器端都支持纔有效。

五、http2.0

http2.0不是https,它至關於http的下一代規範(https也多是http2.0規範)

比較一下http1.1和http2.0顯著不一樣地方:

(1)http1.1中,每請求一個資源,都是須要開啓一個tcp/ip鏈接的,因此對應的結果是:每個資源對應一個tcp/ip請求,因爲tcp/ip自己有個併發數的限制,資源一旦多了,速度會降低慢下來。

(2)http2.0中,一個tcp/ip請求能夠請求多個資源,也就說,只要一次tcp/ip請求,就能夠請求多個資源,分隔成更小的幀請求,速度明顯提高。

因此,若是http2.0全面應用的,不少http1.1中的優化方案無需用到(好比:精靈圖,靜態組員多域名拆分等)。

如今介紹一下http2.0的一些特性:

(1)多路複用(一個tcp/ip能夠請求多個資源);

(2)首部壓縮(http頭部壓縮,減小體積);

(3)二進制分幀(在應用層跟傳輸層之間增長一個二進制分幀層,改進傳輸性能,實現低延遲和高吞吐);

(4)服務器端推送(服務端能夠對客戶端的一個請求發出多個響應能夠主動通知客戶端);

(5)請求優先級(若是流被賦予了優先級,就會基於這個優先級來處理,有服務器決定須要多少資源來處理該請求)

六、https

https就是安全版本的http,好比一些支付操做服務基本上都是基於https的,由於http請求的安全係數過低了。

簡單來看,https和http區別是:在請求前,會創建ssl連接,確保接下來的通訊都是加密的,沒法輕易截取分析。

通常來講,須要將網站升級到https,須要後端支持(後端須要申請證書等),而後https的開銷比http大(由於要額外的簡歷安全連接和加密等),因此通常來講http2.0配合https的體驗更佳(http2.0更快)。

主要關注的就是SSL/TLS的握手流程,以下(簡述):

(1)瀏覽器請求創建SSL連接,並向服務端發送一個隨機數(client random)和客戶端支持的加密方法,好比是RSA加密,此時是明文傳輸。

(2)服務端從中選出一組加密算法和hash算法,回覆一個隨機數(server random),並將本身的身份信息以證書的形式發回給瀏覽器(證書中包含了網站地址,非對稱加密的公鑰,以及證書頒發機構等信息)。

(3)瀏覽器收到服務端證書後:

一、首先驗證證書的合法性(頒發機構是否合法,證書包含的網站是否和正在訪問的同樣),若是證書信任,瀏覽器會顯示一個小頭鎖,不然會有提示。

二、用戶接受到證書後(無論信任不信任),瀏覽器會產生一個新的隨機數(Premaster secret),而後證書中的公鑰以及制定的加密方法加密 Premastersecret(預主密碼),發送給服務器。

三、利用client random,server random 和 premaster secret 經過必定的算法生成HTTP連接數據傳輸的對稱加密key-‘sessionkey’

四、使用約定好的hash算法計算握手消息,並使用生成的session key 對消息進行加密,最後將以前生成的全部信息發送給服務端。

(4)服務端收到瀏覽器的回覆

一、利用已知的加密方式與本身的私鑰進行解密,獲取Premaster secret,

二、和瀏覽器相同規則生成session key,

三、使用session key 解密瀏覽器發來的握手消息,並驗證hash是否與瀏覽器發來的一致,

四、使用session key 加密一段握手消息,發送給瀏覽器

(5)瀏覽器解密並計算握手消息的hash值,若是與服務端發來的hash一致,此時握手結束。

以後全部的https通訊數據將由以前瀏覽器生成的session key並利用對稱加密算法進行加密

8、緩存問題:http緩存

http交互中,緩存很大程度上提高效率。

一、強緩存與弱緩存

緩存能夠簡單劃分爲兩種類型:強緩存(200 from cache)與協商緩存(304);

區別簡介一下:

圖片

對於協商緩存,可使用ctrl + F5強制刷新,使得協商緩存無效。

對於強制緩存,在未過時,必須更新資源路徑才能發送新的請求。

二、緩存頭部簡述

怎麼在代碼中區分強緩存和協商緩存?

經過不一樣的http的頭部控制。

屬於強制緩存的:

  1. (http1.1)Cache-Control/Max-Age

  2. (http1.0)Pragma/Expires

注意:cache_control的值:public,private,no-store,no-cache,max-age

屬於協商緩存的:

  1. (http1.1)If-None-Match/E-tag

  2. (http1.0)If-Modified-Since/Last-Modified

再提一點,其實HTML頁面中也有一個meta標籤能夠控制緩存方案-Pragma

  1. <METAHTTP-EQUIV="Pragma"CONTENT="no-cache">

不過,這種方案仍是比較少用到,由於支持狀況不佳,譬如緩存代理服務器確定不支持,因此不推薦。

三、緩存頭部區別

在http1.1中,出現了一些新內容,彌補http1.0不足。

http1.0中的緩存控制:

(1)Pragma:嚴格來講不算緩存控制的頭部,設置了no-cache會讓本地緩存失效(屬於編譯控制,來實現特定的指令)。

(2)Expires:服務端配置,屬於強緩存,用來控制在規定的時間以前,瀏覽器不會發送大量請求,而直接使用本地緩存,注意:Expires通常對應服務器端時間,好比:Expires:Fri, 30 Oct 1998 14:19:41

(3)If-Modified-Since/Last-modified:這兩個是成對出現的,屬於協商緩存。其中瀏覽器頭部是If-Modified-Since,而服務端是Last-Modified,發送請求時,若是這兩個匹配成功,表明服務器資源並無改變,服務端不會返回資源實體,而是返回頭部,告知瀏覽器使用本地緩存。Last-modifed指文件最後的修改時間,只能精確到1S之內。

http1.1中緩存的控制:

(1)cache-control :緩存的控制頭部,有nocache,max-age等多個取值。

(2)Max-Age:服務端配置的,用來控制強緩存的,在規定的時間內,瀏覽器不用發出請求,直接使用本地的緩存。Max-Age是cache-control的值,好比:cache-control: max-age=60*1000,值是絕對時間,瀏覽器本身計算。

(3)If-None-Match/E-tag:這兩個是成對的出現的,屬於協商緩存,其中瀏覽器頭部是If-None-Match,而服務端是E-tag,一樣,發出請求後,若是If-None-Match和E-tag匹配,表明內容沒有變化,告訴瀏覽器使用本地緩存,和Last-modified不一樣,E-tag更精確,它相似於指紋同樣,基於FileEtag INode Mtime Size生成的,就是說文件變,指紋就會變,沒有精確度的限制。

Cache-Control相比Expires?

一、都是強制緩存。

二、Expires使用服務端時間,由於存在時區,和瀏覽器本地時間能夠修改問題,在http1.1不推薦使用Expires;Cache-Control的Max-Age是瀏覽器端本地的絕對時間。

三、同時使用Cache-Control和Expires,Cache_control優先級高。

E-tag相比Last-Modified?

一、都是協商緩存。

二、Last-modified指的是服務端文件最後改變時間,缺陷是精確只能到1s,文件週期性的改變,致使緩存失效;E-tag是一種指紋機制,文件指紋,只要文件改變,E-tag馬上變,沒有精度限制。

三、帶有E-tag和Last-modified時候,E-tag優先級高。

各大緩存頭部的總體關係以下圖

圖片

9、解析頁面流程

前面提到是http交互,接下來是瀏覽器獲取到html,而後解析,渲染。

一、流程簡述

瀏覽器內核拿到內容後,渲染大體分爲如下幾步:

(1)解析html,構建DOM樹;同時解析CSS,生成CSS規則樹。

(2)合併DOM樹和CSS規則樹,生成Render樹。

(3)佈局Render樹(layout/reflow),負責各元素的尺寸,位置計算。

(4)繪製render樹(paint),繪製頁面像素信息。

(5)瀏覽器會將各層的信息發給GPU。GPU會將各層合成(composite),顯示在屏幕上。

以下圖:

圖片

二、html解析,構建DOM

這一步的流程是這樣的:瀏覽器解析HTML,構建DOM樹。實際上,稍微展開一下。

解析html到構建dom過程簡述以下:

Bytes -> characters -> tokens -> nodes ->DOM 好比,有這樣一個html頁面:

圖片

瀏覽器的處理以下:

圖片

列舉一下其中一些重點過程:

圖片

例如:body對象的父節點就是HTML對象,而後段略p對象的父節點就是body對象 最後的DOM樹:

圖片

三、css解析,構建css規則樹

CSS規則樹的生成也是相似

  1. Bytes→ characters → tokens → nodes → CSSOM

好比:style.css內容以下:

  1. body { font-size: 16px}

  2. p { font-weight: bold }

  3. span { color: red }

  4. p span { display: none }

  5. img { float: right }

最終的CSSOM樹就是

圖片

四、構建渲染樹

當DOM樹和CSSOM都有了後,就要開始構建渲染樹了。通常來講,渲染樹和DOM樹相對應的,但不是嚴格意義上的一一對應。

由於有一些不可見的DOM元素不會插入到渲染樹中,如head這種不可見的標籤或者display: none等

圖片

五、渲染

有了render樹,接下來就是開始渲染,基本流程以下:

圖片

圖中重要的四個步驟就是:

(1)計算CSS樣式 ;

(2)構建渲染樹 ;

(3)佈局,主要定位座標和大小,是否換行,各類position overflow z-index屬性 ;

(4)繪製,將圖像繪製出來。

而後,圖中的線與箭頭表明經過js動態修改了DOM或CSS,致使了從新佈局(Layout)或渲染(Repaint)

這裏Layout和Repaint的概念是有區別的:

(1)Layout,也稱爲Reflow,即迴流。通常意味着元素的內容、結構、位置或尺寸發生了變化,須要從新計算樣式和渲染樹。

(2)Repaint,即重繪。意味着元素髮生的改變只是影響了元素的一些外觀之類的時候(例如,背景色,邊框顏色,文字顏色等),此時只須要應用新樣式繪製這個元素就能夠了。

迴流的成本開銷要高於重繪,並且一個節點的迴流每每回致使子節點以及同級節點的迴流, 因此優化方案中通常都包括,儘可能避免迴流。

六、什麼引發迴流

1.頁面渲染初始化

2.DOM結構改變,好比刪除了某個節點

3.render樹變化,好比減小了padding

4.窗口resize

5.最複雜的一種:獲取某些屬性,引起迴流, 不少瀏覽器會對迴流作優化,會等到數量足夠時作一次批處理迴流, 可是除了render樹的直接變化,當獲取一些屬性時,瀏覽器爲了得到正確的值也會觸發迴流,這樣使得瀏覽器優化無效,包括

圖片

迴流必定伴隨着重繪,重繪卻能夠單獨出現。

優化方案:

(1)減小逐項更改樣式,作好一次性更改樣式。或者將樣式定義爲class,並一次性更新。

(2)避免循環操做dom,建立一個documentFragment或div,在他上面進行全部的dom操做,最後添加到window.document中。

(3)避免屢次讀取offset等屬性,沒法避免就將他們緩存到變量中。

(4)將複雜的元素絕對定位或者固定定位,使他們脫離文檔流,不然迴流代價很高。

注意:改變字體大小會引發迴流。

再看一個例子:

圖片

七、簡單層和複合層

上述中的渲染停止步於繪製,但實際上繪製這一步也沒有這麼簡單,它能夠結合複合層和簡單層的概念來說。

簡單介紹下:

(1)能夠默認只有一個複合層,全部的DOM節點都是在這個複合圖層下。

(2)若是開啓了硬件加速功能,能夠將某一個節點變成複合圖層。

(3)複合圖層之間的繪製互不干擾,直接GPU直接控制。

(4)簡單圖層中,就算是absolute等佈局,變化時不影響總體迴流,可是因爲在同一個圖層中,仍然會影響繪製的,所以作動畫時候性能仍然很低。並且複合層是獨立的,因此通常作動畫推薦使用硬件加速。

更多參考:segmentfault.com/a/119000001…

八、Chrome的調試

Chrome的開發者工具中,Performance中能夠看到詳細的渲染過程:

圖片

圖片

九、資源外鏈的下載

上面介紹了html解析,渲染流程。但實際上,在解析html時,會遇到一些資源鏈接,此時就須要進行單獨處理了。

簡單起見,這裏將遇到的靜態資源分爲一下幾大類(未列舉全部):

  1. 遇到外鏈的處理

  2. css 樣式資源

  3. js 腳本資源

  4. img 圖片類資源

如下針對每種狀況進行詳細說明:

  1. 遇到外鏈的處理

當遇到上述的外鏈時,會單獨開啓一個下載線程去下載資源(http1.1 中是每個資源的下載都要開啓一個 http 請求,對應一個 tcp/ip 連接)

    2.遇到 css 樣式資源

css 資源處理特色:

(1)css 下載時異步的,不會阻塞瀏覽器構建 DOM 樹;

(2)可是會阻塞渲染,也就是在構建 render 樹時,等到 css 下載解析後才進行(與瀏覽器優化有關,防止 css 規則不斷變化,避免重複的構建)

(3)有例外,遇到 media query 聲明的 css 是不會阻塞構建 render 

    3.遇到 js 腳本資源

JS 腳本資源的處理有幾個特色:

(1)阻塞瀏覽器的解析,也就是說發現一個外鏈腳本時,需等待腳本下載完成並執行後纔會繼續解析 HTML。

(2)瀏覽器的優化,通常現代瀏覽器有優化,在腳本阻塞時,也會繼續下載其它資源(固然有併發上限),可是雖然腳本能夠並行下載,解析過程仍然是阻塞的,也就是說必須這個腳本執行完畢後纔會接下來的解析,並行下載只是一種優化而已。

(3)defer 與 async,普通的腳本是會阻塞瀏覽器解析的,可是能夠加上 defer 或 async 屬性,這樣腳本就變成異步了,能夠等到解析完畢後再執行。

注意,defer 和 async 是有區別的:defer 是延遲執行,而 async 是異步執行。

簡單的說:

(1)async是異步執行,異步下載完畢後就會執行,不確保執行順序,必定在onload前,但不肯定在DOMContentLoaded事件的前或後。

(2)defer是延遲執行,在瀏覽器看起來的效果像是將腳本放在了body後面同樣(雖然按規範應該是在DOMContentLoaded事件前,但實際上不一樣瀏覽器的優化效果不同,也有可能在它後面)。

    4.遇到img圖片類資源

遇到圖片等資源時,直接就是異步下載,不會阻塞解析,下載完畢後直接用圖片替換原有src的地方

十、loaded和domcontentloaded

對比:

(1)DOMContentLoaded 事件觸發時,僅當DOM加載完成,不包括樣式表,圖片(譬如若是有async加載的腳本就不必定完成)。

(2)load 事件觸發時,頁面上全部的DOM,樣式表,腳本,圖片都已經加載完成了。

10、Reference

0、一道面試題是如何引起深層次的靈魂拷問? mp.weixin.qq.com/s/jy5CmIGHd…

一、github.com/saucxs/full…

二、blog.csdn.net/sinat_21455…

三、book.douban.com/subject/269…

四、github.com/kaola-fed/b…

相關文章
相關標籤/搜索