日期:2018-4-26
目標:瞭解從輸入URL
到頁面加載完成的過程當中都發生了什麼事情
總用時:一天
完成狀況:達成html
爲何會想要了解從輸入URL
到頁面加載完成的過程當中都發生了什麼事情這個問題呢,由於課程參考資料的Web 建站技術中HTML、HTML五、XHTML、CSS、SQL、JavaScript、PHP、ASP.NET、Web Services 是什麼中最高票答案中給出了下圖所示的網站訪問基本過程,張秋怡學姐的解答也十分易懂:面試
再者這個問題可謂是常見的面試題之一,而這張圖中只是給出了很是基本的一個先後端交互的過程,因爲本身有基礎,因此列出的相關概念也都基本理解了,因而就花些時間擴展一下數據庫
URL
的時候有沒有發現瀏覽器會給你一些你似曾相識且與你輸入的內容相匹配的網址呢?
其實咱們在瀏覽器中輸入URL
的時候,瀏覽器就會開始智能的匹配可能URL
,瀏覽器會從歷史記錄,書籤等地方,找到你已經輸入的字符串可能對應的URL
,而後給出智能提示windows
在輸好URL
後咱們會按下Enter
鍵,瀏覽器會發起請求,若是URL
是域名而不是IP
地址,將進行域名解析,所謂域名解析是指什麼呢?後端
IP
地址是網絡上標識站點的數字地址,爲了 方便記憶,採用域名來代替IP
地址標識站點地址,域名解析就是域名到IP
地址的轉換過程。
域名解析按下面的步驟進行(部份內容涉及到計算機網絡知識):瀏覽器
hosts
(windows
下路徑爲C:\Windows\System32\drivers\etc
)文件,做用是將一些經常使用的網址域名與其對應的IP
地址創建一個關聯「數據庫」。通常來講,系統會首先自動從hosts
文件中尋找對應的IP
地址,若是有的話就直接使用hosts
文件裏面的IP
地址,而後直接進行端口確認DNS
服務器的一個客戶,把待解析的域名放在DNS
請求報文中,以UDP
用戶數據報的方式發給本地DNS
服務器DNS
服務器查找到相應的域名的IP
地址,就把對應的IP
地址放在回答報文中返回若是上一步沒有找到,即本地DNS
服務器不知道被查詢域名的IP
地址,因爲主機向本地DNS
服務器的查詢是遞歸查詢,因此此時,本地DNS
服務器就會以DNS
客戶的身份向其餘根DNS
服務器繼續發出查詢請求報文。本地DNS
服務器向根DNS
服務器的查詢是迭代查詢,當找到相應域名的IP
地址後,就會把這個結果返回給最初發起查詢請求的瀏覽器緩存
遞歸查詢:在該模式下DNS
服務器接收到客戶機請求,必須返回一個準確的查詢結果給客戶機。若是該DNS
服務器本地沒有存儲被查詢的DNS
信息,那麼該服務器會(替客戶機)詢問其餘服務器,並將返回的查詢結果再返回給客戶機。
迭代查詢:在該模式下DNS
服務器接收到客戶機請求,若是該DNS
服務器本地沒有存儲被查詢的DNS
信息,DNS
服務器會向客戶機提供其餘可以解析查詢請求的DNS
服務器地址,讓客戶機再向這臺DNS
服務器提交請求,依次循環直到返回查詢的結果爲止。
IP
地址,能夠進行下一步了。IP
地址後,還要確認一下端口,默認端口是80
端口,一個服務器可能會提供不一樣的服務,這些服務經過端口來區分,能夠指定端口號瀏覽器獲得IP
地址並確認端口後,會向目標服務器發起HTTP
請求,HTTP
請求是經過TCP
鏈接來發送的(若是是HTTPS
則須要先創建SSL鏈接,再是TCP
鏈接,下面的討論基於HTTP
),具體以下服務器
瀏覽器會生成目標服務器的HTTP
請求報文,請求報文通常包含請求方法、請求URI
、協議版本、請求首部字段等內容,HTTP
請求準備好後,HTTP
請求報文從應用層傳到傳輸層後會被分割爲報文段,並會發起一條到達目標服務器的TCP
鏈接,開始TCP
三次握手,過程如圖所示:網絡
通俗的能夠理解爲:異步
A主動向B打電話:嗨,能聽到嗎(SYN=1,seq=x),而後A就開始等待B的回答(SYN-SENT狀態),此時A不知道B能不能聽到
B聽到A的話以後,能夠確認它能聽到A,可是它還要確認一下A能不能聽到他本身的聲音,因而B說:我能聽到你的聲音(ACK=1,ack=x+1),你能聽到個人聲音嗎(SYN=1,seq=y),而後B開始等待A的恢復(SYN-RECD狀態)
A聽到B的話以後,A能夠確認兩件事,一是B能聽到它說話,二是它也能聽到B說話,A已經能夠隨時說話和傾聽了(ESTABLISHED狀態)。可是此時的B還在等待中,並不知道A能不能聽到,因此此時A須要再回復B說:我能夠聽到你的聲音(ACK=1,ack=y+1),開始愉快的聊天吧~(seq=x+1),B聽到這句話後便也能夠隨時說話和傾聽了(ESTABLISHED狀態)
以後兩我的就能夠balabalabala....
HTTP
請求的請求報文是直接附在第三次握手的消息中穿插補充小知識,爲何是三次握手,而不是兩次四次?
有一種觀點是三次握手是基於TCP
協議的可靠性(Reliability
)要求,這是確認雙發都能進行收發的最小次數,兩次確認不了,四次多餘。可是並無徹底意義上的可靠,不論握手多少次都只能代表握手的時候是可靠的,不能保證後面數據傳輸時一直可靠,由於 信道是不可靠的,固然三次握手至少能夠代表它曾經可靠,這是兩次握手沒法完成的,而四次甚至更屢次握手僅僅是提升「它曾經可靠」這個結論的可信程度。因此這個握手也只是確保可靠的一個基本須要,TCP
協議的可靠性(注意區分完整性integrity
)更多的是由校驗和、定時器超時重傳、確認機制在《計算機網絡》一書中也有講過這個問題,給出的解釋是:三次握手是爲了防止失效的鏈接請求報文段被服務端接收,從而產生錯誤。具體例子以下所述:
client
發出的一個鏈接請求報文段並無丟失,而是在某個網絡結點長時間的滯留了,以至延誤到鏈接釋放之後的某個時間纔到達server
。原本這是一個早已失效的報文段。但server
收到此失效的鏈接請求報文段後,就誤認爲是client
再次發出的一個新的鏈接請求。因而就向client
發出確認報文段,贊成創建鏈接。
假設不採用「三次握手」,那麼只要server
發出確認,新的鏈接就創建了。可是因爲如今client
並無發出創建鏈接的請求,所以不會理睬server
的確認,也不會向server
發送數據。而server
卻覺得新的鏈接已經創建,並一直等待client
發來數據。這樣,server
的不少資源就白白浪費掉了。
採用「三次握手」的辦法能夠防止上述現象發生。例如剛纔那種狀況,client
不會向server
的確認發出確認。server
因爲收不到確認,就知道client
並無要求創建鏈接
IP
和端口,可是數據總不可能飛過去吧?HTTP
請求報文段會從傳輸層傳到網絡層,在網絡層被封裝成IP
數據包,網絡層規定了經過怎樣的路徑(所謂的傳輸路線)到達目標服務器,並把數據包傳送給對方。IP
數據包會進一步傳到下一層 --- 數據鏈路層,而後會再次被封裝到MAC
數據幀結構中,因爲IP
地址間的通訊依賴於MAC
地址(網卡所屬的固定地址),因此MAC
數據幀結構中會有通過ARP
協議解析後的MAC
地址(不必定是目標服務器的MAC
地址,由於實際上通訊的雙方在同一局域網(LAN)內的狀況是不多的,通常都會通過路由中轉)。MAC
數據幀再向下傳,便會到達物理層,這裏要注意物理層考慮的是怎樣才能在鏈接各類計算機的傳輸媒體上傳輸數據比特流,而不是指具體的傳輸媒體。 物理層須要確保原始的數據可在各類物理媒體上傳輸,它規定了傳輸媒體的機械特性、電氣特性、功能特性、過程特性:
常見的傳輸媒體有雙絞線、電纜、光纜、無線信道等,物理層的任務就是要讓數據在這些傳輸媒體上都能能進行傳輸
MAC
地址匹配,數據經過傳輸媒體到達目標服務器的物理層,物理層接收數據比特流而後向上傳送到服務器的數據鏈路層,在數據鏈路層MAC
數據幀將進行封裝的逆操做,還原成IP
數據包以後向上傳送到網絡層,網絡層也進行封裝的逆操做還原成HTTP
請求報文段(分割後的一小段一小段的),而後這些報文段向上傳到傳輸層,在傳輸層按原來的序號從新組裝成完整的HTTP
請求報文,再向上傳到應用層,應用層的HTTP
協議便會開始對請求進行處理PHP
、JAVA
等語言進行處理等,等處理完成後,會返回一個HTTP
響應,它生成一個HTTP
響應報文,與HTTP
請求報文結構相似,而後這個響應報文會「走過」請求報文來時的路到達瀏覽器瀏覽器接收HTTP
響應,而後有可能釋放TCP
鏈接,也有可能從新使用這個TCP
鏈接發送新的請求(持久鏈接),此處瞭解一下TCP
鏈接的釋放,不一樣於TCP
鏈接創建的三次握手,TCP
鏈接的釋放是四次揮手,客戶端和服務器端均可以發起關閉請求,也存在二者同時發起關閉請求的狀況,圖中爲客戶端A主動發起關閉請求:
一樣通俗的解釋一波:
A對B要傳的文件已經傳完了,因而他對B說:我要傳的文件已經傳完了,我要準備下線了(seq=u,FIN=1)。而後A就等待B的回覆(FIN-WAIT-1狀態)
B看到A的消息後,回覆A說:知道了,可是我還有文件給你(ACK=1,ack=u+1,seq=v)。B進入等他文件傳完的狀態(CLOSE-WAIT狀態)。
A收到B的回覆以後,下線不了了,因而繼續等待着B的文件傳完(FIN-WAIT-2狀態)
幾分鐘後,B的文件傳完了,此時他對A說:個人文件傳完了,我也要下線了(seq=w,FIN=1,ACK=1,ack=u+1),而後B等待A的回覆來確認真的能夠下線了(LAST-ACK狀態)
A收到B的回覆後,便對A說:好的,那你下線吧(ACK=1,seq=u+1,ack=w+1)。此時A會等待一段時間(2MSL,TIME-WAIT狀態),B收到後就直接下線了(CLOSE狀態),而後2MSL時間到了以後,A也下線(CLOSE狀態)
TCP
鏈接是全雙工模式,服務器B收到A的斷開請求時,僅僅代表A沒有東西傳給服務器B了,但此時服務器B可能向A的傳輸還沒結束,因此服務器B要先給A一個確認收到A的斷開請求的ACK
報文,而後繼續向A把信息傳完,等傳完以後服務器B再向A發送斷開請求的報文段,等A收到並回復ACK
報文後再釋放鏈接。TCP
鏈接,而非單方面的釋放。TCP
創建鏈接的第二次握手,而釋放鏈接時卻必須分開。最後一次握手後A爲何要等2MSL
?
首先解釋一下MSL
,MSL
是指最長報文段壽命,RFC793建議爲兩分鐘,但實際上可據實際狀況而定,也就是說一個報文段最久可存在的時間是MSL
ACK
報文可以到達服務器B,若是這個ACK
報文丟失了,服務器B沒有收到,B會超時重傳第三次握手的FIN+ACK
報文給A,這個時候處於等待的A就能夠收到這個重傳的FIN+ACK
報文,並再次發送ACK
報文給服務器B,而且從新啓動2MSL計時器,最終結果是A和B都正常進入CLOSE狀態。若是A發完ACK
報文後就直接釋放了A-->B的鏈接,那麼A就收不到B重傳的FIN+ACK
報文,也不能從新發送ACK`報文,那麼B就沒法按正常步驟釋放B-->A的鏈接MSL
,因此A在發送完最後一個ACK
報文段以後,再通過時間2MSL
,本鏈接持續的時間內所產生的全部報文段都將在網絡中消失,這樣這些舊的報文段便不會出如今下一個新的鏈接中瀏覽器以後會檢查HTTP
的響應狀態,主要經過響應碼來判斷
1xx: 表示通知信息的,好比請求收到了或正在處理
2xx:表示成功,操做被成功接收並處理
3xx:表示重定向,通常完成請求還必須採起進一步的行動
4xx:表示客戶端的差錯
5xx:表示服務器的差錯
HTTP
報頭信息解碼響應,決定如何處理這些響應,並展示響應,以響應爲一個HTML
爲例HTML
文檔,最開始會遇到<!DOCTYPE>
聲明,而後根據<!DOCTYPE>
聲明瀏覽器就知道該用哪一種規範來解析這個文檔再繼續邊加載邊解析,邊生成DOM
樹,加載過程當中遇到外部CSS
文件,瀏覽器便會另外發出一個請求,來獲取CSS
文件(過程和上面說的同樣),獲取CSS
後會生成CSS Rule
樹。DOM
樹和CSS Rule
樹生成Render
樹,頁面能夠開始邊加載邊渲染了
DOM
樹的關係:那些不可見的DOM
元素(如<head>…</head>
,display=none
的元素)不會被插入渲染樹中;還有像一些節點是絕對定位或浮動,這些節點會在文本流以外,所以他們會在渲染樹和DOM
樹的不一樣位置,渲染樹標識出真實的位置,並用一個佔位結構標識出他們原來的位置,而DOM
樹上是他們原來的位置layout
)和"繪製"(paint
)這兩個步驟,所謂"佈局"是指給出每一個DOM
節點在瀏覽器窗口中的準確位置,"繪製"是指遍歷Render
樹將佈局好的DOM
節點繪製在屏幕上。
瀏覽器繼續加載渲染,若是遇到<script>
標籤,瀏覽器會當即執行(暫不考慮defer
及async
屬性),此時會出現頁面阻塞,不只要等待文檔中JS
文件下載加載完畢,還要等待JS
解析執行完畢,才能夠恢復HTML
文檔的加載解析。
JS
修改DOM
樹,須要從新構建DOM
樹的狀況,DOM
樹改變瀏覽器須要回過頭來從新渲染這部分代碼,因此瀏覽器但願經過阻塞其餘內容的下載和呈現,來避免出現更多的沒必要要的Reflow
(稱爲迴流或者重排)<script>
放在的<head>
中,則<body>
標籤沒法被加載,那麼頁面天然就沒法渲染了,所以這將致使在該JS
代碼徹底執行完以前,頁面都是一片空白,用戶體驗很是很差,通常我看到長時間的空白頁面,我都很是想直接關閉它。所以會推薦將全部<script>
標籤儘量放到<body>
標籤的底部,以儘可能減小對整個頁面下載的影響,此時雖然還會存在一個腳本阻塞另外一個腳本的問題,可是用戶體驗比上面的好不少,由於用戶看到了大部份內容,而不是空白defer
屬性至關於告訴瀏覽器當即下載,延遲執行。它使得加載後續文檔元素的過程將和JS
文件的加載並行進行(異步),可是JS
文件的執行要在整個頁面解析完成以後,DOMContentLoaded
事件觸發以前完成,執行順序爲出現的前後順序。(高程中指出現實中不必定會按照順序執行,也不必定會在DOMContentLoaded
事件觸發以前完成,所以最好只包含一個延遲腳本,這多是與瀏覽器的實現有關,具體什麼狀況下會出現我還不知道???)async
屬性至關於告訴瀏覽器當即下載執行,而且頁面的加載渲染不須要等待該腳本加載和執行,它們二者會異步進行。標記爲async
的腳本不會按照它們出現的前後順序執行,而是誰先下載完了誰就先執行,它們必定會在頁面的load
事件觸發以前執行,但可能會在DOMContentLoaded
事件觸發以前或以後執行。基於前面所說的一點緣由,異步腳本最好不要修改DOM
,若是由多個異步腳本,它們之間最好沒有依賴關係HTML
文檔。Reflow
</html>
碰面了,這次的頁面加載渲染過程完成,瀏覽器也是很累了,而後會當即觸發DOMContentLoaded
事件,該事件是在造成完整的DOM
樹以後就會觸發,而不會理會圖像、JS
文件、CSS
文件或其餘資源是否已經下載完畢JS
文件、CSS
文件等外部資源都加載完成後會觸發load
事件用戶在頁面上進行交互時,可能會致使頁面進行Repaint
或Reflow
Repaint
:若是隻是改變了某個元素的背景顏色,文字顏色等,不影響元素周圍或內部佈局的屬性,將只會引發瀏覽器的Repaint
,重繪某一部分Reflow
:若是某個部分發生了的變化影響了佈局,那瀏覽器就須要倒回去從新渲染,每次Reflow
必然會致使Repaint
原本只是想了解了解,結果一入深似海,看似簡單的操做背後藏着數不清的小動做,文中也只是涉及了一部分,還有不少相關的過程沒有涉及到,可是能力有限,仍是慢慢來,暫時就先告一段落,文中若有錯誤還請指正哦~