面試中常常會被問到這個問題吧,唉,我最開始被問到的時候也就能大概說一些流程。被問得多了,本身就想去找找這個問題的全面回答,因而乎搜了不少資料和網上的文章,根據那些文章寫一個總結。css
寫得很差,或者有意見的直接噴,不用走流程。也歡迎大佬指點html
首先這不是小問題,能把裏面的過程說清楚真的很麻煩,而後下面我把這些知識點,按流程的形式總結的:前端
- 從瀏覽器接收url到開啓網絡請求線程
- 開啓網絡線程到發出一個完整的http請求
- 從服務器接收到請求到對應後臺接收到請求
- 後臺和前臺的http交互
- http的緩存問題
- 瀏覽器接收到http數據包後的解析流程
- CSS的可視化格式模型
- JS引擎解析過程
- 跨域、web安全、hybrid等等
1. 從瀏覽器接收url到開啓網絡請求線程
多進程的瀏覽器java
瀏覽器是多進程的,有一個主控進程,以及每個tab頁面都會新開一個進程(某些狀況下多個tab會合並進程)。node
進程可能包括主控進程,插件進程,GPU,tab頁(瀏覽器內核)等等。nginx
- Browser進程:瀏覽器的主進程(負責協調、主控),只有一個
- 第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才建立
- GPU進程:最多一個,用於3D繪製
- 瀏覽器渲染進程(內核):默認每一個Tab頁面一個進程,互不影響,控制頁面渲染,腳本執行,事件處理等(有時候會優化,如多個空白tab會合併成一個進程)
多線程的瀏覽器內核web
每個tab頁面能夠看做是瀏覽器內核進程,而後這個進程是多線程的,它有幾大類子線程:面試
- GUI渲染線程
- JS引擎線程
- 事件觸發線程
- 定時器觸發線程
- http異步網絡請求線程
解析URL算法
輸入URL後,會進行解析(URL的本質就是統一資源定位符)數據庫
URL通常包括幾大部分:
- protocol,協議頭,譬若有http,ftp,https等
- host,主機域名或IP地址
- port,端口號
- path,目錄路徑
- query,即查詢參數
- 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攻擊的問題,你們自行搜索把,這個問題的解決方案也超級多,官方文檔也介紹很詳細,這兒就不整了
參考:參考文章
有大佬也總結過了很詳細的內容,我照搬了點兒內容,勿噴,只是學習一下。