輸入URL到瀏覽器顯示頁面的過程,蒐集各方面資料總結一下

面試中常常會被問到這個問題吧,唉,我最開始被問到的時候也就能大概說一些流程。被問得多了,本身就想去找找這個問題的全面回答,因而乎搜了不少資料和網上的文章,根據那些文章寫一個總結。css


寫得很差,或者有意見的直接噴,不用走流程。也歡迎大佬指點html


首先這不是小問題,能把裏面的過程說清楚真的很麻煩,而後下面我把這些知識點,按流程的形式總結的:前端

  1. 從瀏覽器接收url到開啓網絡請求線程
  2. 開啓網絡線程到發出一個完整的http請求
  3. 從服務器接收到請求到對應後臺接收到請求
  4. 後臺和前臺的http交互
  5. http的緩存問題
  6. 瀏覽器接收到http數據包後的解析流程
  7. CSS的可視化格式模型
  8. JS引擎解析過程
  9. 跨域、web安全、hybrid等等

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

多進程的瀏覽器java

瀏覽器是多進程的,有一個主控進程,以及每個tab頁面都會新開一個進程(某些狀況下多個tab會合並進程)。node

進程可能包括主控進程,插件進程,GPU,tab頁(瀏覽器內核)等等。nginx

  1. Browser進程:瀏覽器的主進程(負責協調、主控),只有一個
  2. 第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才建立
  3. GPU進程:最多一個,用於3D繪製
  4. 瀏覽器渲染進程(內核):默認每一個Tab頁面一個進程,互不影響,控制頁面渲染,腳本執行,事件處理等(有時候會優化,如多個空白tab會合併成一個進程)

多線程的瀏覽器內核web

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

  1. GUI渲染線程
  2. JS引擎線程
  3. 事件觸發線程
  4. 定時器觸發線程
  5. http異步網絡請求線程

解析URL算法

輸入URL後,會進行解析(URL的本質就是統一資源定位符)數據庫

URL通常包括幾大部分:

  1. protocol,協議頭,譬若有http,ftp,https等
  2. host,主機域名或IP地址
  3. port,端口號
  4. path,目錄路徑
  5. query,即查詢參數
  6. fragment,即 #後的hash值,通常用來定位到某個位置

網絡請求都是單獨的線程

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

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

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

DNS查詢獲得IP

若是輸入的是域名,須要進行dns解析成IP,大體流程:

若是瀏覽器有緩存,直接使用瀏覽器緩存,不然使用本機緩存,再沒有的話就是用host
若是本地沒有,就向dns域名服務器查詢(固然,中間可能還會通過路由,也有緩存等),查詢到對應的IP
注意,域名查詢時有多是通過了CDN調度器的(若是有cdn存儲功能的話)。

並且,須要知道dns解析是很耗時的,所以若是解析域名過多,會讓首屏加載變得過慢,能夠考慮 dns-prefetch優化。
這一塊能夠深刻展開,具體請去網上搜索,這裏就不佔篇幅了(網上能夠看到很詳細的解答)。

tcp/ip請求

http的本質就是 tcp/ip請求。
須要瞭解三次握手規則創建鏈接以及斷開鏈接時的四次揮手。
tcp將http長報文劃分爲短報文,經過三次握手與服務端創建鏈接,進行可靠傳輸。

三次握手:
1.客戶端給服務器發確實是當前服務器
2.服務器給客戶端迴應,我是你要訪問的當前服務器
3.客戶端迴應,我是客戶端

四次揮手:
1.發起者:關閉主動傳輸信息的通道,只能接收信息
2.接受者:收到通道關閉的信息
3.接受者:也關閉主動傳輸信息的通道
4.發起者:接收到數據,關閉通道,雙方沒法通訊

tcp/ip的併發限制

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

get和post的區別

這個東西網上的資料也不少,這兒就大概描述一下在tcp/ip層面的區別,在http層面的區別請讀者自行搜索:
get和post本質都是tcp/ip。
get會產生一個tcp數據包,post兩個。

具體就是:

get請求時,瀏覽器會把 headers和 data一塊兒發送出去,服務器響應200(返回數據),
post請求時,瀏覽器先發送 headers,服務器響應 100continue,瀏覽器再發送 data,服務器響應200(返回數據)。

而後有讀者可能之前瞭解過OSI的七層:物理層、 數據鏈路層、 網絡層、 傳輸層、 會話層、 表示層、 應用層

這兒就不班門弄虎了,列一下內容,須要深刻理解的讀者請自行搜索,計算機網絡相關的資料。

1.應用層(dns,http) DNS解析成IP併發送http請求
2.傳輸層(tcp,udp) 創建tcp鏈接(三次握手)
3.網絡層(IP,ARP) IP尋址
4.數據鏈路層(PPP) 封裝成幀
5.物理層(利用物理介質傳輸比特流) 物理傳輸(而後傳輸的時候經過雙絞線,電磁波等各類介質)
6.表示層:主要處理兩個通訊系統中交換信息的表示方式,包括數據格式交換,數據加密與解密,數據壓縮與終端類型轉換等
7.會話層:它具體管理不一樣用戶和進程之間的對話,如控制登錄和註銷過程

3. 從服務器接收到請求到對應後臺接收到請求

後端的操做有點多,我這兒也就不秀本身知識面低下了,哈哈

負載均衡

對於大型的項目,因爲併發訪問量很大,因此每每一臺服務器是吃不消的,因此通常會有若干臺服務器組成一個集羣,而後配合反向代理實現負載均衡。(聽說如今node在微服務的項目方面愈來愈猛,大併發也不在話下,正在研究node,但願後面能寫一個心得)

簡單的說:用戶發起的請求都指向調度服務器(反向代理服務器,譬如安裝了nginx控制負載均衡),而後調度服務器根據實際的調度算法,分配不一樣的請求給對應集羣中的服務器執行,而後調度器等待實際服務器的HTTP響應,並將它反饋給用戶。

後臺的處理

通常後臺都是部署到容器中的,因此通常爲:

1.先是容器接受到請求(如tomcat容器)
2.而後對應容器中的後臺程序接收到請求(如java程序)
3.而後就是後臺會有本身的統一處理,處理完後響應響應結果

歸納下:
1.通常有的後端是有統一的驗證的,如安全攔截,跨域驗證
2.若是這一步不符合規則,就直接返回了相應的http報文(如拒絕請求等)
3.而後當驗證經過後,纔會進入實際的後臺代碼,此時是程序接收到請求,而後執行(譬如查詢數據庫,大量計算等等)
4.等程序執行完畢後,就會返回一個http響應包(通常這一步也會通過多層封裝)
5.而後就是將這個包從後端發送到前端,完成交互

4.後臺和前臺的http交互

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

http報文結構
報文通常包括了: 通用頭部, 請求/響應頭部, 請求/響應體。學過計算機網絡的讀者應超級熟悉。

通用頭部
這也是開發人員見過的最多的信息,包括以下:

Request Url: 請求的web服務器地址
Request Method: 請求方式(Get、POST、OPTIONS、PUT、HEAD、DELETE、CONNECT、TRACE)
Status Code: 請求的返回狀態碼,如200表明成功
Remote Address: 請求的遠程服務器地址(會轉爲IP)
譬如,在跨域拒絕時,多是method爲 options,狀態碼爲 404/405等(固然,實際上可能的組合有不少)。

其中,Method的話通常分爲兩批次:

HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五種請求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。

相信知道RESTFUL的讀者應該很熟悉,如今在前端後端開發使用頻繁的也就是get,post,put,delete,也是咱們熟知的四大操做"增刪改查"。

狀態碼:這是進行請求和迴應的關鍵信息,官方有最全的狀態碼信息,這兒就列幾個常見的:

200——代表該請求被成功地完成,所請求的資源發送回客戶端
304——自從上次請求後,請求的網頁未修改過,請客戶端使用本地緩存
400——客戶端請求有錯(譬如能夠是安全模塊攔截)
401——請求未經受權
403——禁止訪問(譬如能夠是未登陸時禁止)
404——資源未找到
500——服務器內部錯誤
503——服務不可用

其餘的請讀者自行去搜索官方介紹。

對於狀態碼:
數字1開頭的表示:請求已經接收,繼續處理
數字2開頭的表示:請求成功,已經被服務器成功處理
數字3開頭的表示:須要客戶端採起進一步的操做才能完成請求
數字4開頭的表示:客戶端看起來可能發生了錯誤,妨礙了服務器的處理
數字5開頭的:表示服務器在處理請求的過程當中有錯誤或者異常狀態發生,也有多是服務器意識到以當前的軟硬件資源沒法完成對請求的處理

請求/響應頭部

請求和響應頭部也是分析時經常使用到的。經常使用的請求頭部(部分):

Accept: 接收類型,表示瀏覽器支持的MIME類型(對標服務端返回的Content-Type)
Accept-Encoding:瀏覽器支持的壓縮類型,如gzip等,超出類型不能接收
Content-Type:客戶端發送出去實體內容的類型
Cache-Control: 指定請求和響應遵循的緩存機制,如no-cache
If-Modified-Since:對應服務端的Last-Modified,用來匹配看文件是否變更,只能精確到1s以內,http1.0中
Expires:緩存控制,在這個時間內不會請求,直接使用緩存,http1.0,並且是服務端時間
Max-age:表明資源在本地緩存多少秒,有效時間內不會請求,而是使用緩存,http1.1中
If-None-Match:對應服務端的ETag,用來匹配文件內容是否改變(很是精確),http1.1中
Cookie:有cookie而且同域訪問時會自動帶上
Connection:當瀏覽器與服務器通訊時對於長鏈接如何進行處理,如keep-alive
Host:請求的服務器URL
Origin:最初的請求是從哪裏發起的(只會精確到端口),Origin比Referer更尊重隱私
Referer:該頁面的來源URL(適用於全部類型的請求,會精確到詳細頁面地址,csrf攔截經常使用到這個字段)
User-Agent:用戶客戶端的一些必要信息,如UA頭部等

經常使用的響應頭部:

Access-Control-Allow-Headers: 服務器端容許的請求Headers
Access-Control-Allow-Methods: 服務器端容許的請求方法
Access-Control-Allow-Origin: 服務器端容許的請求Origin頭部(譬如爲*)
Content-Type:服務端返回的實體內容的類型
Date:數據從服務器發送的時間
Cache-Control:告訴瀏覽器或其餘客戶,什麼環境能夠安全的緩存文檔
Last-Modified:請求資源的最後修改時間
Expires:應該在何時認爲文檔已通過期,從而再也不緩存它
Max-age:客戶端的本地資源應該緩存多少秒,開啓了Cache-Control後有效
ETag:請求變量的實體標籤的當前值
Set-Cookie:設置和頁面關聯的cookie,服務器經過這個頭部把cookie傳給客戶端
Keep-Alive:若是客戶端有keep-alive,服務端也會有響應(如timeout=38)
Server:服務器的一些相關信息

請求頭部和響應頭部是有對應關係的:例如
1.請求頭部的 Accept要和響應頭部的 Content-Type匹配,不然會報錯。
2.跨域請求時,請求頭部的 Origin要匹配響應頭部的 Access-Control-Allow-Origin,不然會報跨域錯誤。
3.在使用緩存時,請求頭部的 If-Modified-Since、 If-None-Match分別和響應頭部的 Last-Modified、 ETag對應。

更多的對應關係請讀者自行搜索。

請求/響應實體

作http請求時,除了頭部,還有消息實體,通常來講,請求實體中會將一些須要的參數都放入進入(用於post請求)。譬如實體中能夠放參數的序列化形式( a=1&b=2這種),或者直接放表單對象( FormData對象,上傳時能夠夾雜參數以及文件),等等。

而通常響應實體中,就是放服務端須要傳給客戶端的內容。通常如今的接口請求時,實體中就是對於的信息的json格式。

cookie以及優化

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

經常使用的場景以下:

用戶登錄後,服務端會生成一個session,session中有對於用戶的信息(如用戶名、密碼等),而後會有一個sessionid(至關因而服務端的這個session對應的key),而後服務端在登陸頁面中寫入cookie,值就是:jsessionid=xxx,而後瀏覽器本地就有這個cookie了,之後訪問同域名下的頁面時,自動帶上cookie,自動檢驗,在有效時間內無需二次登錄。

通常來講,cookie是不容許存放敏感信息的(千萬不要明文存儲用戶名、密碼),由於很是不安全,若是必定要強行存儲,首先,必定要在cookie中設置 httponly(這樣就沒法經過js操做了)。

另外,因爲在同域名的資源請求時,瀏覽器會默認帶上本地的cookie,針對這種狀況,在某些場景下是須要優化的。

例如如下場景:

客戶端在域名A下有cookie(這個能夠是登錄時由服務端寫入的)
而後在域名A下有一個頁面,頁面中有不少依賴的靜態資源(都是域名A的,譬若有20個靜態資源)
此時就有一個問題,頁面加載,請求這些靜態資源時,瀏覽器會默認帶上cookie
也就是說,這20個靜態資源的http請求,每個都得帶上cookie,而實際上靜態資源並不須要cookie驗證
此時就形成了較爲嚴重的浪費,並且也下降了訪問速度(由於內容更多了)

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

將靜態資源分組,分別放到不一樣的子域名下
而子域名請求時,是不會帶上父級域名的cookie的,因此就避免了浪費

說到了多域名拆分,這裏再提一個問題,那就是:

在移動端,若是請求的域名數過多,會下降請求速度(由於域名整套解析流程是很耗費時間的,並且移動端通常帶寬都比不上pc)
此時就須要用到一種優化方案: dns-prefetch(讓瀏覽器空閒時提早解析dns域名,不過也請合理使用,勿濫用)

gzip壓縮

首先,明確 gzip是一種壓縮格式,須要瀏覽器支持纔有效(不過通常如今瀏覽器都支持),並且gzip壓縮效率很好(高達70%左右)。而後gzip通常是由 apache、 tomcat等web服務器開啓。

固然服務器除了gzip外,也還會有其它壓縮格式(如deflate,沒有gzip高效,且不流行),因此通常只須要在服務器上開啓了gzip壓縮,而後以後的請求就都是基於gzip壓縮格式的,很是方便。

長鏈接與短鏈接

首先看 tcp/ip層面的定義:

長鏈接:一個tcp/ip鏈接上能夠連續發送多個數據包,在tcp鏈接保持期間,若是沒有數據包發送,須要雙方發檢測包以維持此鏈接,通常須要本身作在線維持(相似於心跳包)
短鏈接:通訊雙方有數據交互時,就創建一個tcp鏈接,數據發送完成後,則斷開此tcp鏈接

而後在http層面:

http1.0中,默認使用的是短鏈接,也就是說,瀏覽器沒進行一次http操做,就創建一次鏈接,任務結束就中斷鏈接,譬如每個靜態資源請求時都是一個單獨的鏈接
http1.1起,默認使用長鏈接,使用長鏈接會有這一行 Connection:keep-alive,在長鏈接的狀況下,當一個網頁打開完成後,客戶端和服務端之間用於傳輸http的tcp鏈接不會關閉,若是客戶端再次訪問這個服務器的頁面,會繼續使用這一條已經創建的鏈接

注意: keep-alive不會永遠保持,它有一個持續時間,通常在服務器中配置(如apache),另外長鏈接須要客戶端和服務器都支持時纔有效。

http 2.0

http2.0不是https,它至關因而http的下一代規範(譬如https的請求能夠是http2.0規範的)。而後簡述下http2.0與http1.1的顯著不一樣點:

http1.1中,每請求一個資源,都是須要開啓一個tcp/ip鏈接的,因此對應的結果是,每個資源對應一個tcp/ip請求,因爲tcp/ip自己有併發數限制,因此當資源一多,速度就顯著慢下來
http2.0中,一個tcp/ip請求能夠請求多個資源,也就是說,只要一次tcp/ip請求,就能夠請求若干個資源,分割成更小的幀請求,速度明顯提高。

因此,若是http2.0全面應用,不少http1.1中的優化方案就無需用到了(譬如打包成精靈圖,靜態資源多域名拆分等)。
而後簡述下http2.0的一些特性:

多路複用(即一個tcp/ip鏈接能夠請求多個資源)
首部壓縮(http頭部壓縮,減小體積)
二進制分幀(在應用層跟傳送層之間增長了一個二進制分幀層,改進傳輸性能,實現低延遲和高吞吐量)
服務器端推送(服務端能夠對客戶端的一個請求發出多個響應,能夠主動通知客戶端)
請求優先級(若是流被賦予了優先級,它就會基於這個優先級來處理,由服務器決定須要多少資源來處理該請求。)

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- session key
    使用約定好的HASH算法計算握手消息,並使用生成的 session key對消息進行加密,最後將以前生成的全部信息發送給服務端。
4.服務端收到瀏覽器的回覆
    利用已知的加解密方式與本身的私鑰進行解密,獲取 Premastersecret
    和瀏覽器相同規則生成 session key
    使用 session key解密瀏覽器發來的握手消息,並驗證Hash是否與瀏覽器發來的一致
    使用 session key加密一段握手消息,發送給瀏覽器
5.瀏覽器解密並計算握手消息的HASH,若是與服務端發來的HASH一致,此時握手過程結束,

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

http的緩存

先後端的http交互中,使用緩存能很大程度上的提高效率,並且基本上對性能有要求的前端項目都是必用緩存的。

強緩存與弱緩存
緩存能夠簡單的劃分紅兩種類型: 強緩存( 200fromcache)與 協商緩存( 304)
區別以下:

強緩存( 200fromcache)時,瀏覽器若是判斷本地緩存未過時,就直接使用,無需發起http請求
協商緩存( 304)時,瀏覽器會向服務端發起http請求,而後服務端告訴瀏覽器文件未改變,讓瀏覽器使用本地緩存

對於協商緩存,使用 Ctrl+F5強制刷新可使得緩存無效。可是對於強緩存,在未過時時,必須更新資源路徑才能發起新的請求(更改了路徑至關因而另外一個資源了,這也是前端工程化中經常使用到的技巧)。

緩存頭部簡述
上述提到了強緩存和協商緩存,那它們是怎麼區分的呢?答案是經過不一樣的http頭部控制。
緩存中經常使用的幾個頭部:

If-None-Match/E-tag
If-Modified-Since/Last-Modified
Cache-Control/Max-Age
Prama/Expires

屬於強緩存控制的:

(http1.1) Cache-Control/Max-Age
(http1.0) Pragma/Expires

注意: Max-Age不是一個頭部,它是 Cache-Control頭部的值。

屬於協商緩存控制的:

(http1.1) If-None-Match/E-tag
(http1.0) If-Modified-Since/Last-Modified

能夠看到,上述有提到 http1.1和 http1.0,這些不一樣的頭部是屬於不一樣http時期的。

頭部的區別
首先明確,http的發展是從http1.0到http1.1,而在http1.1中,出了一些新內容,彌補了http1.0的不足。

http1.0中的緩存控制:

Pragma:嚴格來講,它不屬於專門的緩存控制頭部,可是它設置 no-cache時可讓本地強緩存失效(屬於編譯控制,來實現特定的指令,主要是由於兼容http1.0,因此之前又被大量應用)
Expires:服務端配置的,屬於強緩存,用來控制在規定的時間以前,瀏覽器不會發出請求,而是直接使用本地緩存,注意,Expires通常對應服務器端時間,如 Expires:Fri,30Oct199814:19:41
If-Modified-Since/Last-Modified:這兩個是成對出現的,屬於協商緩存的內容,其中瀏覽器的頭部是 If-Modified-Since,而服務端的是 Last-Modified,它的做用是,在發起請求時,若是 If-Modified-Since和 Last-Modified匹配,那麼表明服務器資源並未改變,所以服務端不會返回資源實體,而是隻返回頭部,通知瀏覽器可使用本地緩存。 Last-Modified,顧名思義,指的是文件最後的修改時間,並且只能精確到 1s之內

http1.1中的緩存控制:

Cache-Control:緩存控制頭部,有no-cache、max-age等多種取值
Max-Age:服務端配置的,用來控制強緩存,在規定的時間以內,瀏覽器無需發出請求,直接使用本地緩存,注意,Max-Age是Cache-Control頭部的值,不是獨立的頭部,譬如 Cache-Control:max-age=3600,並且它值得是絕對時間,由瀏覽器本身計算
If-None-Match/E-tag:這兩個是成對出現的,屬於協商緩存的內容,其中瀏覽器的頭部是 If-None-Match,而服務端的是 E-tag,一樣,發出請求後,若是 If-None-Match和 E-tag匹配,則表明內容未變,通知瀏覽器使用本地緩存,和Last-Modified不一樣,E-tag更精確,它是相似於指紋同樣的東西,基於 FileEtagINodeMtimeSize生成,也就是說,只要文件變,指紋就會變,並且沒有1s精確度的限制。

Max-Age相比Expires?

Expires使用的是服務器端的時間,可是有時候會有這樣一種狀況-客戶端時間和服務端不一樣步。那這樣,可能就會出問題了,形成了瀏覽器本地的緩存無用或者一直沒法過時,因此通常http1.1後不推薦使用 Expires。而 Max-Age使用的是客戶端本地時間的計算,所以不會有這個問題,所以推薦使用 Max-Age。

注意,若是同時啓用了 Cache-Control與 Expires, Cache-Control優先級高。

E-tag相比Last-Modified?

Last-Modified:
    代表服務端的文件最後什麼時候改變的
    它有一個缺陷就是隻能精確到1s,
    而後還有一個問題就是有的服務端的文件會週期性的改變,致使緩存失效
E-tag:
    是一種指紋機制,表明文件相關指紋
    只有文件變纔會變,也只要文件變就會變,
    也沒有精確時間的限制,只要文件一遍,立馬E-tag就不同了
    若是同時帶有 E-tag和 Last-Modified,服務端會優先檢查 E-tag。

6. 瀏覽器接收到http數據包後的解析流程

渲染流程大體以下:

1.解析HTML,構建DOM樹
2.解析CSS,生成CSS規則樹
3.合併DOM樹和CSS規則,生成render樹
4.佈局render樹(Layout/reflow),負責各元素尺寸、位置的計算
5.繪製render樹(paint),繪製頁面像素信息
6.瀏覽器會將各層的信息發送給GPU,GPU會將各層合成(composite),顯示在屏幕上

找了個圖:
圖片描述

HTML解析,構建DOM

整個渲染步驟中,HTML解析是第一步。簡單的理解,這一步的流程是這樣的:瀏覽器解析HTML,構建DOM樹。

Bytes → characters → tokens → nodes → DOM
假設有下面這樣一個代碼

<html>  
    <head>    
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link href="style.css" rel="stylesheet">
        <title>Critical Path</title>
    </head>
    <body>    
        <p>Hello<span>web performance</span> students!</p>
        <div><img src="awesome-photo.jpg"></div>  
    </body>
</html>

瀏覽器的處理以下:
圖片描述

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

Conversion轉換:瀏覽器將得到的HTML內容(Bytes)基於他的編碼轉換爲單個字符
Tokenizing分詞:瀏覽器按照HTML規範標準將這些字符轉換爲不一樣的標記token。每一個token都有本身獨特的含義以及規則集
Lexing詞法分析:分詞的結果是獲得一堆的token,此時把他們轉換爲對象,這些對象分別定義他們的屬性和規則
DOM構建:由於HTML標記定義的就是不一樣標籤之間的關係,這個關係就像是一個樹形結構同樣。例如:body對象的父節點就是HTML對象,而後段略p對象的父節點就是body對象

最後的DOM樹以下:
圖片描述

生成CSS規則
Bytes → characters → tokens → nodes → CSSOM
有以下css代碼:

body { font-size: 16px }
p { font-weight: bold }
span { color: red}
p span { display: none }
img { float: right }

cssom樹:

圖片描述

當DOM樹和CSSOM都有了後,就要開始構建渲染樹了。

圖片描述

而後從渲染樹開始生成咱們看到的html頁面。
在這個過程當中又一個小問題,從新構建和渲染頁面:

從新構建,也稱爲Reflow,即迴流。通常意味着元素的內容、結構、位置或尺寸發生了變化,須要從新計算樣式和渲染樹
渲染頁面,也稱爲Repaint,即重繪。意味着元素髮生的改變只是影響了元素的一些外觀之類的時候(例如,背景色,邊框顏色,文字顏色等),此時只須要應用新樣式繪製這個元素就能夠了

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

什麼會引發迴流?
1.頁面渲染初始化
2.DOM結構改變,好比刪除了某個節點
3.render樹變化,好比減小了padding
4.窗口resize
5.最複雜的一種:獲取某些屬性,引起迴流

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

1.offset(Top/Left/Width/Height)
2.scroll(Top/Left/Width/Height)
3.cilent(Top/Left/Width/Height)
4.width,height
5.調用了getComputedStyle()或者IE的currentStyle

迴流必定伴隨着重繪,重繪卻能夠單獨出現。因此通常會有一些優化方案,如:

1.減小逐項更改樣式,最好一次性更改style,或者將樣式定義爲class並一次性更新
2.避免循環操做dom,建立一個documentFragment或div,在它上面應用全部DOM操做,最後再把它添加到window.document
3.避免屢次讀取offset等屬性。沒法避免則將它們緩存到變量
4.將複雜的元素絕對定位或固定定位,使得它脫離文檔流,不然迴流代價會很高

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

var s = document.body.style;
s.padding = "2px";    // 迴流+重繪
s.border = "1px solid red";    // 再一次 迴流+重繪
s.color = "blue";    // 再一次重繪
s.backgroundColor = "#ccc";    // 再一次 重繪
s.fontSize = "14px";    // 再一次 迴流+重繪
// 添加node,再一次 迴流+重繪
document.body.appendChild(document.createTextNode('abc!'));

資源外鏈的下載

上面介紹了html解析,渲染流程。但實際上,在解析html時,會遇到一些資源鏈接,此時就須要進行單獨處理了。簡單起見,這裏將遇到的靜態資源分爲一下幾大類(未列舉全部):

CSS樣式資源
JS腳本資源
img圖片類資源
遇到外鏈時的處理

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

遇到CSS樣式資源

CSS資源的處理有幾個特色:

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

有例外, media query聲明的CSS是不會阻塞渲染的

遇到JS腳本資源

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

阻塞瀏覽器的解析,也就是說發現一個外鏈腳本時,需等待腳本下載完成並執行後纔會繼續解析HTML
瀏覽器的優化,通常現代瀏覽器有優化,在腳本阻塞時,也會繼續下載其它資源(固然有併發上限),可是雖然腳本能夠並行下載,解析過程仍然是阻塞的,也就是說必須這個腳本執行完畢後纔會接下來的解析,並行下載只是一種優化而已
defer與async,普通的腳本是會阻塞瀏覽器解析的,可是能夠加上defer或async屬性,這樣腳本就變成異步了,能夠等到解析完畢後再執行

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

簡單的說(不展開):

async是異步執行,異步下載完畢後就會執行,不確保執行順序,必定在 onload前,但不肯定在 DOMContentLoaded事件的前或後
defer是延遲執行,在瀏覽器看起來的效果像是將腳本放在了 body後面同樣(雖然按規範應該是在 DOMContentLoaded事件前,但實際上不一樣瀏覽器的優化效果不同,也有可能在它後面)

遇到img圖片類資源

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

loaded和domcontentloaded

簡單的對比:

DOMContentLoaded 事件觸發時,僅當DOM加載完成,不包括樣式表,圖片(譬如若是有async加載的腳本就不必定完成)
load 事件觸發時,頁面上全部的DOM,樣式表,腳本,圖片都已經加載完成了

7. CSS的可視化格式模型

html元素按什麼規則渲染,接下來提到的內容來揭曉

CSS中規定每個元素都有本身的盒子模型(至關於規定了這個元素如何顯示)
而後可視化格式模型則是把這些盒子按照規則擺放到頁面上,也就是如何佈局
換句話說,盒子模型規定了怎麼在頁面裏擺放盒子,盒子的相互做用等等

說到底: CSS的可視化格式模型就是規定了瀏覽器在頁面中如何處理文檔樹。
關鍵字:

包含塊(Containing Block)
控制框(Controlling Box)
BFC(Block Formatting Context)
IFC(Inline Formatting Context)
定位體系
浮動

CSS有三種定位機制: 普通流, 浮動, 絕對定位

包含塊(Containing Block)

一個元素的box的定位和尺寸,會與某一矩形框有關,這個框就稱之爲包含塊。元素會爲它的子孫元素建立包含塊,可是,並非說元素的包含塊就是它的父元素,元素的包含塊與它的祖先元素的樣式等有關係。

好比:

根元素是最頂端的元素,它沒有父節點,它的包含塊就是初始包含塊
static和relative的包含塊由它最近的塊級、單元格或者行內塊祖先元素的內容框(content)建立
fixed的包含塊是當前可視窗口
absolute的包含塊由它最近的position 屬性爲 absolute、 relative或者 fixed的祖先元素建立
    若是其祖先元素是行內元素,則包含塊取決於其祖先元素的 direction特性
    若是祖先元素不是行內元素,那麼包含塊的區域應該是祖先元素的內邊距邊界

控制框(Controlling Box)

塊級元素和塊框以及行內元素和行框的相關概念
塊框:

塊級元素會生成一個塊框( BlockBox),塊框會佔據一整行,用來包含子box和生成的內容
塊框同時也是一個塊包含框( ContainingBox),裏面要麼只包含塊框,要麼只包含行內框(不能混雜),若是塊框內部有塊級元素也有行內元素,那麼行內元素會被匿名塊框包圍

若是一個塊框在其中包含另一個塊框,那麼咱們強迫它只能包含塊框,所以其它文本內容生成出來的都是匿名塊框(而不是匿名行內框)。

行內框:

一個行內元素生成一個行內框
行內元素能排在一行,容許左右有其它元素

display的幾個屬性也能夠影響不一樣框的生成:

block,元素生成一個塊框
inline,元素產生一個或多個的行內框
inline-block,元素產生一個行內級塊框,行內塊框的內部會被看成塊塊來格式化,而此元素自己會被看成行內級框來格式化(這也是爲何會產生 BFC)
none,不生成框,再也不格式化結構中,固然了,另外一個 visibility:hidden則會產生一個不可見的框

BFC(Block Formatting Context)

FC即格式上下文,它定義框內部的元素渲染規則,比較抽象,好比:

FC像是一個大箱子,裏面裝有不少元素
箱子能夠隔開裏面的元素和外面的元素(因此外部並不會影響FC內部的渲染)
內部的規則能夠是:如何定位,寬高計算,margin摺疊等等
不一樣類型的框參與的FC類型不一樣,譬如塊級框對應BFC,行內框對應IFC。

注意,並非說全部的框都會產生FC,而是符合特定條件纔會產生,只有產生了對應的FC後纔會應用對應渲染規則。

BFC規則:

在塊格式化上下文中,每個元素左外邊與包含塊的左邊相接觸(對於從右到左的格式化,右外邊接觸右邊),即便存在浮動也是如此(因此浮動元素正常會直接貼近它的包含塊的左邊,與普通元素重合),除非這個元素也建立了一個新的BFC。

總結幾點BFC特色:

內部 box在垂直方向,一個接一個的放置
box的垂直方向由 margin決定,屬於同一個BFC的兩個box間的margin會重疊
BFC區域不會與 floatbox重疊(可用於排版)
BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此
計算BFC的高度時,浮動元素也參與計算(不會浮動坍塌)

如何觸發BFC?

根元素
float屬性不爲 none
position爲 absolute或 fixed
display爲 inline-block, flex, inline-flex, table, table-cell, table-caption
overflow不爲 visible

這裏提下, display:table,它自己不產生BFC,可是它會產生匿名框(包含 display:table-cell的框)。

IFC(Inline Formatting Context)

IFC即行內框產生的格式上下文。

IFC規則

在行內格式化上下文中,框一個接一個地水平排列,起點是包含塊的頂部。水平方向上的 margin,border 和 padding 在框之間獲得保留,框在垂直方向上能夠以不一樣的方式對齊:它們的頂部或底部對齊,或根據其中文字的基線對齊。

行框

包含那些框的長方形區域,會造成一行,叫作行框。行框的寬度由它的包含塊和其中的浮動元素決定,高度的肯定由行高度計算規則決定。

行框的規則:

若是幾個行內框在水平方向沒法放入一個行框內,它們能夠分配在兩個或多個垂直堆疊的行框中(即行內框的分割)
行框在堆疊時沒有垂直方向上的分割且永不重疊
行框的高度老是足夠容納所包含的全部框。不過,它可能高於它包含的最高的框(例如,框對齊會引發基線對齊)
行框的左邊接觸到其包含塊的左邊,右邊接觸到其包含塊的右邊

結合補充下IFC規則

浮動元素可能會處於包含塊邊緣和行框邊緣之間,儘管在相同的行內格式化上下文中的行框一般擁有相同的寬度(包含塊的寬度),它們可能會因浮動元素縮短了可用寬度,而在寬度上發生變化。

同一行內格式化上下文中的行框一般高度不同(如,一行包含了一個高的圖形,而其它行只包含文本),當一行中行內框寬度的總和小於包含它們的行框的寬,它們在水平方向上的對齊,取決於 text-align 特性。空的行內框應該被忽略。

即不包含文本,保留空白符,margin/padding/border非0的行內元素,以及其餘常規流中的內容(好比,圖片,inline blocks 和 inline tables),而且不是以換行結束的行框,必須被看成零高度行框對待。

總結:

行內元素老是會應用IFC渲染規則
行內元素會應用IFC規則渲染,譬如 text-align能夠用來居中等
塊框內部,對於文本這類的匿名元素,會產生匿名行框包圍,而行框內部就應用IFC渲染規則
行內框內部,對於那些行內元素,同樣應用IFC渲染規則
另外, inline-block,會在元素外層產生IFC(因此這個元素是能夠經過 text-align水平居中的),固然,它內部則按照BFC規則渲染

相比BFC規則來講,IFC可能更加抽象(由於沒有那麼條理清晰的規則和觸發條件),但總的來講,它就是行內元素自身如何顯示以及在框內如何擺放的渲染規則,這樣描述應該更容易理解。

關於css的一些別的規則,你們能夠去搜搜:

如常規流,浮動,絕對定位等區別
如浮動元素不包含在常規流中
如相對定位,絕對定位, Fixed定位等區別
如 z-index的分層顯示機制等

8. JS引擎解析過程

這個部分的內容請參考這兒:JS引擎解析過程
直接略過了

9. 跨域、web安全

跨域

爲何會跨域:

在瀏覽器同源策略限制下,向不一樣源(不一樣協議、不一樣域名或者不一樣端口)發送XHR請求,瀏覽器認爲該請求不受信任,禁止請求,具體表現爲請求後不正常響應

舉個栗子:
圖片描述

那要怎麼搞呢?網上的解決辦法也不少,這兒列一些:

1.jsonp
2.cors
3.document.domain
4.POSTmessage

想深刻看的能夠瀏覽一下這個 跨域的經常使用解決方式

web安全

這個東西,咱們在面試的時候確定會被問到xss攻擊的問題,你們自行搜索把,這個問題的解決方案也超級多,官方文檔也介紹很詳細,這兒就不整了

參考:參考文章

有大佬也總結過了很詳細的內容,我照搬了點兒內容,勿噴,只是學習一下。

相關文章
相關標籤/搜索