Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 91视频最新网站,av在线天堂网,国产精品亚洲精品久久成人

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          如何使用JavaScript解析URL

          Web 開發(fā)中,有許多情況需要解析 URL,這篇主要學(xué)習(xí)如何使用 URL 對(duì)象實(shí)現(xiàn)這一點(diǎn)。

          開始

          創(chuàng)建一個(gè)以下內(nèi)容的 HTML 文件,并在瀏覽器中打開。

          <html> 
           <head> 
           <title>JavaScript URL parsing</title> 
           </head> 
           <body> 
           <script> 
           // 激動(dòng)人心的代碼即將寫在這里 
           </script> 
           </body> 
          </html> 
          

          如果你想嘗試本文中的任何內(nèi)容,可以將其放在 <script> 標(biāo)記中,保存,重新加載頁面,看看會(huì)發(fā)生什么! 在本教程中,將使用 console.log 來打印所需要的內(nèi)容,你可以打開開發(fā)都工具,來查看內(nèi)容。

          什么是 URL

          這應(yīng)該是相當(dāng)簡(jiǎn)單的,但讓我們說清楚。 URL 是網(wǎng)頁的地址,可以在瀏覽器中輸入以獲取該網(wǎng)頁的唯一內(nèi)容。 可以在地址欄中看到它:



          URL 是統(tǒng)一資源定位符,對(duì)可以從互聯(lián)網(wǎng)上得到的資源的位置和訪問方法的一種簡(jiǎn)潔的表示,是互聯(lián)網(wǎng)上標(biāo)準(zhǔn)資源的地址。互聯(lián)網(wǎng)上的每個(gè)文件都有一個(gè)唯一的 URL,它包含的信息指出文件的位置以及瀏覽器應(yīng)該怎么處理它。

          此外,如果你不熟悉基本 URL 路徑的工作方式,可以查看此文學(xué)習(xí)。

          URL 不都長(zhǎng)的一樣的

          這是一個(gè)快速提醒 - 有時(shí) URL 可能非常奇怪,如下:

          :1234/page/?a=b

          file:///Users/username/folder/file.png

          獲取當(dāng)前URL

          獲取當(dāng)前頁面的 URL 非常簡(jiǎn)單 - 我們可以使用 window.location。

          試著把這個(gè)添加到我們形如寫的的腳本中:

          console.log(window.location); 
          

          查看瀏覽器的控制臺(tái):



          不是你想要的?這是因?yàn)樗环祷啬阍跒g覽器中看到的實(shí)際 URL 地址——它返回的是一個(gè) URL 對(duì)象。使用這個(gè) URL 對(duì)象,我們可以解析 URL 的不同部分,接下來就會(huì)講到。

          創(chuàng)建 URL 對(duì)象

          很快就會(huì)看到,可以使用 URL 對(duì)象來了解 URL 的不同部分。如果你想對(duì)任何 URL 執(zhí)行此操作,而不僅僅是當(dāng)前頁面的 URL,該怎么辦? 我們可以通過創(chuàng)建一個(gè)新的 URL 對(duì)象來實(shí)現(xiàn)。 以下是如何創(chuàng)建一個(gè):

          var myURL = new URL('https://example.com'); 
          

          就這么簡(jiǎn)單! 可以打印 myURL 來查看 myURL 的內(nèi)容:

          console.log(myURL); 
          



          出于本文的目的,將 myURL 設(shè)置為這個(gè)值:

          var myURL = new URL('https://example.com:4000/folder/page.html?x=y&a=b#section-2') 
          

          將其復(fù)制并粘貼到 <script> 元素中,以便你可以繼續(xù)操作! 這個(gè) URL 的某些部分可能不熟悉,因?yàn)樗鼈儾⒉豢偸潜皇褂?- 但你將在下面了解它們,所以不要擔(dān)心!

          URL 對(duì)象的結(jié)構(gòu)

          使用 URL 對(duì)象,可以非常輕松地獲取 URL 的不同部分。 以下是你可以從 URL 對(duì)象獲得的所有內(nèi)容。 對(duì)于這些示例,我們將使用上面設(shè)置的 myURL。

          href

          URL 的 href 基本上是作為字符串(文本)的整個(gè) URL。如果你想把頁面的 URL 作為字符串而不是 URL 對(duì)象,你可以寫 window.location.href。

          console.log(myURL.href); 
          // Output: "https://example.com:4000/folder/page.html?x=y&a=b#section-2" 
          

          協(xié)議 (protocol)

          URL的協(xié)議是一開始的部分。這告訴瀏覽器如何訪問該頁面,例如通過 HTTP 或 HTTPS。 但是還有很多其他協(xié)議,比如 ftp(文件傳輸協(xié)議)和 ws(WebSocket)。通常,網(wǎng)站將使用 HTTP 或 HTTPS。

          雖然如果你的計(jì)算機(jī)上打開了文件,你可能正在使用文件協(xié)議! URL對(duì)象的協(xié)議部分包括:,但不包括 //。 讓我們看看 myURL 吧!

          console.log(myURL.protocol); 
          // Output: "https:" 
          

          主機(jī)名(hostname)

          主機(jī)名是站點(diǎn)的域名。 如果你不熟悉域名,則它是在瀏覽器中看到的URL的主要部分 - 例如 google.com 或codetheweb.blog。

          console.log(myURL.hostname); 
          // Output: "example.com" 
          

          端口(port)

          URL 的端口號(hào)位于域名后面,用冒號(hào)分隔(例如 example.com:1234)。 大多數(shù)網(wǎng)址都沒有端口號(hào),這種情況非常罕見。

          端口號(hào)是服務(wù)器上用于獲取數(shù)據(jù)的特定“通道” - 因此,如果我擁有 example.com,我可以在多個(gè)不同的端口上發(fā)送不同的數(shù)據(jù)。 但通常域名默認(rèn)為一個(gè)特定端口,因此不需要端口號(hào)。 來看看 myURL 的端口號(hào):

          console.log(myURL.port); 
          // Output: "4000" 
          

          主機(jī)(host)

          主機(jī)只是主機(jī)名和端口放在一起,嘗試獲取 myURL 的主機(jī):

          console.log(myURL.host); 
          // Output: "example.com:4000" 
          

          來源(origin)

          origin 由 URL 的協(xié)議,主機(jī)名和端口組成。 它基本上是整個(gè) URL,直到端口號(hào)結(jié)束,如果沒有端口號(hào),到主機(jī)名結(jié)束。

          console.log(myURL.origin); 
          // Output: "https://example.com:4000" 
          

          pathname(文件名)

          pathname 從域名的最后一個(gè) “/” 開始到 “?” 為止,是文件名部分,如果沒有 “?” ,則是從域名最后的一個(gè) “/” 開始到 “#” 為止 , 是文件部分, 如果沒有 “?” 和 “#” , 那么從域名后的最后一個(gè) “/” 開始到結(jié)束 , 都是文件名部分。

          console.log(myURL.pathname); 
          // Output: "/folder/page.html" 
          

          錨點(diǎn)(hash)

          從 “#” 開始到最后,都是錨部分。可以將哈希值添加到 URL 以直接滾動(dòng)到具有 ID 為該值的哈希值 的元素。 例如,如果你有一個(gè) id 為 hello 的元素,則可以在 URL 中添加 #hello 就可以直接滾動(dòng)到這個(gè)元素的位置上。通過以下方式可以在 URL 獲取 “#” 后面的值:

          console.log(myURL.hash); 
          // Output: "#section-2" 
          

          查詢參數(shù) (search)

          你還可以向 URL 添加查詢參數(shù)。它們是鍵值對(duì),意味著將特定的“變量”設(shè)置為特定值。 查詢參數(shù)的形式為 key=value。 以下是一些 URL 查詢參數(shù)的示例:

          ?key1=value1&key2=value2&key3=value3 
          

          請(qǐng)注意,如果 URL 也有 錨點(diǎn)(hash),則查詢參數(shù)位于 錨點(diǎn)(hash)(也就是 ‘#’)之前,如我們的示例 URL 中所示:

          console.log(myURL.search); 
          // Output: "?x=y&a=b" 
          

          但是,如果我們想要拆分它們并獲取它們的值,那就有點(diǎn)復(fù)雜了。

          使用 URLSearchParams 解析查詢參數(shù)

          要解析查詢參數(shù),我們需要?jiǎng)?chuàng)建一個(gè) URLSearchParams 對(duì)象,如下所示:

          var searchParams = new URLSearchParams(myURL.search); 
          

          然后可以通過調(diào)用 searchParams.get('key')來獲取特定鍵的值。 使用我們的示例網(wǎng)址 - 這是原始搜索參數(shù):

          ?x=y&a=b 
          

          因此,如果我們調(diào)用 searchParams.get('x'),那么它應(yīng)該返回 y,而 searchParams.get('a')應(yīng)該返回 b,我們來試試吧!

          console.log(searchParams.get('x')); 
          // Output: "y" 
          console.log(searchParams.get('a')); 
          // Output: "b" 
          

          擴(kuò)展

          獲取 URL 的中參數(shù)

          方法一:正則法

          function getQueryString(name) { 
           var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i'); 
           var r = window.location.search.substr(1).match(reg); 
           if (r != null) { 
           return unescape(r[2]); 
           } 
           return null; 
          } 
          // 這樣調(diào)用: 
          alert(GetQueryString("參數(shù)名1")); 
          alert(GetQueryString("參數(shù)名2")); 
          alert(GetQueryString("參數(shù)名3")); 
          

          方法二:split拆分法

          function GetRequest() { 
           var url = location.search; //獲取url中"?"符后的字串 
           var theRequest = new Object(); 
           if (url.indexOf("?") != -1) { 
           var str = url.substr(1); 
           strstrs = str.split("&"); 
           for(var i = 0; i < strs.length; i ++) { 
           theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]); 
           } 
           } 
           return theRequest; 
          } 
          var Request = new Object(); 
          Request = GetRequest(); 
          // var 參數(shù)1,參數(shù)2,參數(shù)3,參數(shù)N; 
          // 參數(shù)1 = Request['參數(shù)1']; 
          // 參數(shù)2 = Request['參數(shù)2']; 
          // 參數(shù)3 = Request['參數(shù)3']; 
          // 參數(shù)N = Request['參數(shù)N']; 
          

          修改 URL 的中某個(gè)參數(shù)值

          薦閱讀:

          阿里架構(gòu)師精選Nginx+Redis+Sping+SpringBoot源碼級(jí)PDF文檔分享

          微服務(wù)+Docker完美教程,被阿里架構(gòu)師匯集到這2份文檔里面了!

          引言

          對(duì)于面試常問的從瀏覽器輸入 URL 到頁面展示過程發(fā)生了什么?,我想大家都或多或少能說出一二。但是,其實(shí)這個(gè)問題很有深度,而你是否回答的有深度,在很大程度上會(huì)影響到面試官對(duì)你的印象。

          并且,網(wǎng)上各種資料都是淺嘗輒止地講解這個(gè)過程,經(jīng)常會(huì)出現(xiàn)今天看到這個(gè)版本,明天看到另一個(gè)版本地情況。所以,這次我們就來深入淺出一下這整個(gè)過程~

          一、Chrome 多進(jìn)程架構(gòu)

          首先,在開始講解整個(gè)過程前,我們需要認(rèn)識(shí)一下 Chrome 多進(jìn)程架構(gòu)。因?yàn)椋瑥臑g覽器輸入 URL 到頁面渲染的整個(gè)過程都是由 Chrome 架構(gòu)中的各個(gè)進(jìn)程之間的配合完成。

          Chrome 的多進(jìn)程架構(gòu):

          • 瀏覽器進(jìn)程,它負(fù)責(zé)用戶界面(地址欄、菜單等等)、子進(jìn)程的管理(例如,進(jìn)程間通信和數(shù)據(jù)傳遞)、存儲(chǔ)等等
          • 渲染進(jìn)程,它負(fù)責(zé)將接收到的 HTML 文檔和 JavaScript 等轉(zhuǎn)化為用戶界面
          • 網(wǎng)絡(luò)進(jìn)程,它負(fù)責(zé)網(wǎng)絡(luò)資源的請(qǐng)求,例如 HTTP請(qǐng)求、WebSocket 模塊
          • GPU(圖形處理器)進(jìn)程,它負(fù)責(zé)對(duì) UI 界面的展示
          • 插件進(jìn)程,它負(fù)責(zé)對(duì)插件的管理

          二、過程詳解

          2.1 解析輸入

          發(fā)生這個(gè)過程的前提,用戶在地址欄中輸入了 URL,而地址欄會(huì)根據(jù)用戶輸入,做出如下判斷:

          • 輸入的是非 URL 結(jié)構(gòu)的字符串,則會(huì)用瀏覽器默認(rèn)的搜索引擎搜索該字符串
          • 輸入的是 URL 結(jié)構(gòu)字符串,則會(huì)構(gòu)建完整的 URL 結(jié)構(gòu),瀏覽器進(jìn)程會(huì)將完整的 URL 通過進(jìn)程間通信,即 IPC,發(fā)送給網(wǎng)絡(luò)進(jìn)程

          2.2 請(qǐng)求過程

          在網(wǎng)絡(luò)進(jìn)程接收到 URL 后,并不是馬上對(duì)指定 URL 進(jìn)行請(qǐng)求。首先,我們需要進(jìn)行 DNS 解析域名得到對(duì)應(yīng)的 IP,然后通過 ARP 解析 IP 得到對(duì)應(yīng)的 MAC(Media Access Control Address)地址。

          域名是我們?nèi)〈洃洀?fù)雜的 IP 的一種解決方案,而 IP 地址才是目標(biāo)在網(wǎng)絡(luò)中所被分配的節(jié)點(diǎn)。MAC 地址是對(duì)應(yīng)目標(biāo)網(wǎng)卡所在的固定地址。

          1. DNS 解析

          而 DNS 解析域名的過程分為以下幾個(gè)步驟:

          • 詢問瀏覽器 DNS 緩存
          • 詢問本地操作系統(tǒng) DNS 緩存(即查找本地 host 文件)
          • 詢問 ISP(Internet Service Provider)互聯(lián)網(wǎng)服務(wù)提供商(例如電信、移動(dòng))的 DNS 服務(wù)器
          • 詢問根服務(wù)器,這個(gè)過程可以進(jìn)行遞歸和迭代兩種查找的方式,兩者都是先詢問頂級(jí)域名服務(wù)器查找

          2. 通信過程

          首先,建立 TCP 連接,即三次握手過程

          • 客戶端發(fā)送標(biāo)有 SYN 的數(shù)據(jù)包,表示我將要發(fā)送請(qǐng)求。
          • 服務(wù)端發(fā)送標(biāo)有 SYN/ACK 的數(shù)據(jù)包,表示我已經(jīng)收到通知,告知客戶端發(fā)送請(qǐng)求。
          • 客戶端發(fā)送標(biāo)有 ACK 的數(shù)據(jù)包,表示我要開始發(fā)送請(qǐng)求,準(zhǔn)備被接受。



          然后,利用 TCP 通道進(jìn)行數(shù)據(jù)傳輸

          • 服務(wù)端接收到數(shù)據(jù)包,并發(fā)送確認(rèn)數(shù)據(jù)包已收到的消息到客戶端,不斷重復(fù)這個(gè)過程
          • 客戶端在發(fā)送一個(gè)數(shù)據(jù)包后,未接收到服務(wù)端的確定消息,則重新發(fā)送該數(shù)據(jù)包,即 TCP 的重發(fā)機(jī)制
          • 當(dāng)接收完所有的數(shù)據(jù)包后,接收端會(huì)按照 TCP 頭中的需要進(jìn)行排序,形成完整的數(shù)據(jù)

          最后,斷開 TCP 連接,即四次握手過程

          • 客戶端發(fā)送請(qǐng)求,申請(qǐng)斷開連接,進(jìn)入等待階段,此時(shí)不會(huì)發(fā)送數(shù)據(jù),但是會(huì)繼續(xù)接收數(shù)據(jù)。
          • 服務(wù)端接收請(qǐng)求后,告知客戶端已明白,此時(shí)服務(wù)端進(jìn)入等待狀態(tài),不會(huì)再接收數(shù)據(jù),但是會(huì)繼續(xù)發(fā)送數(shù)據(jù)。
          • 客戶端收到后,進(jìn)入下一階段等待。
          • 服務(wù)端發(fā)送完剩余的數(shù)據(jù)后,告知客戶端可以斷開連接,此時(shí)服務(wù)端不會(huì)發(fā)送和接收數(shù)據(jù)。
          • 客戶端收到后,告知服務(wù)端我開始斷開連接。
          • 服務(wù)端收到后,開始斷開連接。



          而這整個(gè)過程的客戶端則是網(wǎng)絡(luò)進(jìn)程。并且,在數(shù)據(jù)傳輸?shù)倪^程還可能會(huì)發(fā)生的重定向的情況,即當(dāng)網(wǎng)絡(luò)進(jìn)程接收到狀態(tài)碼為 3xx 的響應(yīng)報(bào)文,則會(huì)根據(jù)響應(yīng)報(bào)文首部字段中的 Location 字段的值進(jìn)行重新向,即會(huì)重新發(fā)起請(qǐng)求

          3. 數(shù)據(jù)處理

          當(dāng)網(wǎng)絡(luò)進(jìn)程接收到的響應(yīng)報(bào)文狀態(tài)碼,進(jìn)行相應(yīng)的操作。例如狀態(tài)碼為 200 OK 時(shí),會(huì)解析響應(yīng)報(bào)文中的 Content-Type 首部字段,例如我們這個(gè)過程 Content-Type 會(huì)出現(xiàn) application/javascript、text/css、text/html,即對(duì)應(yīng) Javascript 文件、CSS 文件、HTML 文件。

          詳細(xì)的 MIME 類型講解可以看 MDN

          2.3 創(chuàng)建渲染進(jìn)程

          當(dāng)前需要渲染 HTML 時(shí),則需要?jiǎng)?chuàng)建渲染進(jìn)程,用于后期渲染 HTML。而對(duì)于渲染進(jìn)程,如果是同一站點(diǎn)是可以共享一個(gè)渲染進(jìn)程,例如 a.abc.com 和 c.abc.com 可以共享一個(gè)渲染渲染進(jìn)程。否則,需要重新創(chuàng)建渲染進(jìn)程

          需要注意的是,同站指的是頂級(jí)域名二級(jí)域名相等

          2.4 開始渲染

          在創(chuàng)建完渲染進(jìn)程后,網(wǎng)絡(luò)進(jìn)程會(huì)將接收到的 HTML、JavaScript 等數(shù)據(jù)傳遞給渲染進(jìn)程。而在渲染進(jìn)程接收完數(shù)據(jù)后,此時(shí)用戶界面上會(huì)發(fā)生這幾件事:

          • 更新地址欄的安全狀態(tài)
          • 更新地址欄的 URL
          • 前進(jìn)后退此時(shí) enable,顯示正在加載狀態(tài)
          • 更新網(wǎng)頁



          2.5 渲染過程

          大家都知道頁面渲染的過程也是面試中單獨(dú)會(huì)考的點(diǎn),并且時(shí)常會(huì)由這個(gè)點(diǎn)延申出另一個(gè)問題,即如何避免回流和重繪。

          渲染過程,是整個(gè)從理器輸入 URL 到頁面渲染過程的最后一步。而頁面渲染的過程可以分為 9 個(gè)步驟:

          • 解析 HTML 生成 DOM 樹
          • 解析 CSS 生成 CSSOM
          • 加載或執(zhí)行 JavaScript
          • 生成渲染樹(Render Tree)
          • 布局
          • 分層
          • 生成繪制列表
          • 光柵化
          • 顯示

          2.5.1 構(gòu)建 DOM 樹

          由于網(wǎng)絡(luò)進(jìn)程傳輸給渲染進(jìn)程的是 HTML 字符串,所以,渲染進(jìn)程需要將 HTML 字符串轉(zhuǎn)化成 DOM 樹。例如:



          需要注意的是這個(gè) DOM 樹不同于 Chrome-devtool 中 Element 選項(xiàng)卡的 DOM 樹,它是存在內(nèi)存中的,用于提供 JavaScript 對(duì) DOM 的操作。

          2.5.2 構(gòu)建 CSSOM

          構(gòu)建 CSSOM 的過程,即通過解析 CSS 文件、style 標(biāo)簽、行內(nèi) style 等,生成 CSSOM。而這個(gè)過程會(huì)做這幾件事:

          • 規(guī)范 CSS,即將 color: blue 轉(zhuǎn)化成 color: rgb() 形式,可以理解成類似 ES6 轉(zhuǎn) ES5 的過程
          • 計(jì)算元素樣式,例如 CSS 樣式會(huì)繼承父級(jí)的樣式,如 font-size、color 之類的。



          CSS Object Model 是一組允許用 JavaScript 操縱 CSS 的 API。詳細(xì) API 講解可以看 MDN

          2.5.3 加載 JavaScript

          通常情況下,在構(gòu)建 DOM 樹或 CSSOM 的同時(shí),如果也要加載 JavaScript,則會(huì)造成前者的構(gòu)建的暫停。當(dāng)然,我們可以通過 defer 或 sync 來實(shí)現(xiàn)異步加載 JavaScript。雖然 defer 和 sync 都可以實(shí)現(xiàn)異步加載 JavaScript,但是前者是在加載后,等待 CSSOM 和 DOM 樹構(gòu)建完后才執(zhí)行 JavaScript,而后者是在異步加載完馬上執(zhí)行,即使用 sync 的方式仍然會(huì)造成阻塞。

          而 JavaScript 執(zhí)行的過程,即編譯和運(yùn)行 JavaScript 的過程。由于 JavaScript 是解釋型的語言。所以這個(gè)過程會(huì)是這樣的:

          • 針對(duì)每句代碼進(jìn)行分行處理,即 Token 化
          • 根據(jù) Token,生成 AST(Abstract Sytanx Tree) 抽象語法樹和創(chuàng)建上下文
          • 解釋器解析和執(zhí)行 AST,生成字節(jié)碼。
          • 編譯器針對(duì)需要反復(fù)執(zhí)行的代碼,生成對(duì)應(yīng)的機(jī)器碼,提高運(yùn)行效率

          2.5.4 生成渲染樹(Render Tree)

          在有了 DOM 樹和 CSSOM 之后,需要將兩者結(jié)合生成渲染樹 Render Tree,并且這個(gè)過程會(huì)去除掉那些 display: node 的節(jié)點(diǎn)。此時(shí),渲染樹就具備元素和元素的樣式信息。

          2.5.5 布局

          根據(jù) Render Tree 渲染樹,對(duì)樹中每個(gè)節(jié)點(diǎn)進(jìn)行計(jì)算,確定每個(gè)節(jié)點(diǎn)在頁面中的寬度、高度和位置。

          需要注意的是,第一次確定節(jié)點(diǎn)的大小和位置的過程稱為布局,而第二次才被稱為回流

          2.5.6 分層

          由于層疊上下文的存在,渲染引擎會(huì)為具備層疊上下文的元素創(chuàng)建對(duì)應(yīng)的圖層,而諸多圖層的疊加就形成了我們看到的一些頁面效果。例如,一些 3D 的效果、動(dòng)畫就是基于圖層而形成的。

          值得一提的是,對(duì)于內(nèi)容溢出存在滾輪的情況也會(huì)進(jìn)行分層

          2.5.7 生成繪制列表

          對(duì)于存在圖層的頁面部分,需要進(jìn)行有序的繪制,而對(duì)于這個(gè)過程,渲染引擎會(huì)將一個(gè)個(gè)圖層的繪制拆分成繪制指令,并按照?qǐng)D層繪制順序形成一個(gè)繪制列表。

          2.5.8 光柵化

          有了繪制列表后,渲染引擎中的合成線程會(huì)根據(jù)當(dāng)前視口的大小將圖層進(jìn)行分塊處理,然后合成線程會(huì)對(duì)視口附近的圖塊生成位圖,即光柵化。而渲染進(jìn)程也維護(hù)了一個(gè)柵格化的線程池,專門用于將圖塊轉(zhuǎn)為位圖。

          柵格化的過程通常會(huì)使用 GPU 加速,例如使用 wil-change、opacity,就會(huì)通過 GPU 加速顯示

          2.5.9 顯示

          當(dāng)所有的圖塊都經(jīng)過柵格化處理后,渲染引擎中的合成線程會(huì)生成繪制圖塊的指令,提交給瀏覽器進(jìn)程。然后瀏覽器進(jìn)程將頁面繪制到內(nèi)存中。最后將內(nèi)存繪制結(jié)果顯示在用戶界面上。

          而這個(gè)整個(gè)從生成繪制列表、光柵化、顯示的過程,就是我們常說的重繪的過程

          結(jié)語

          整個(gè)瀏覽器輸入 URL 到頁面渲染的過程涉及到的知識(shí)點(diǎn)非常廣,如 Chrome 多進(jìn)程的架構(gòu)、HTTP 通信過程、瀏覽器解析 JavaScript 過程、瀏覽器繪制頁面過程以及一些計(jì)算機(jī)的基礎(chǔ)知識(shí)等等,并且,這整個(gè)過程的分析其實(shí)和 Chrome-devtools 密切相關(guān),所以很好的使用 Chrome-devtools 是非常重要的,后續(xù)應(yīng)該會(huì)出一篇關(guān)于使用 Chrome-devtools 的指南。當(dāng)然,本篇文章仍然存在諸多不足,歡迎提 issue ~


          作者:五柳
          鏈接:https://juejin.im/post/5e871ee56fb9a03c832b0013

          我們向?yàn)g覽器的地址欄輸入U(xiǎn)RL的時(shí)候,網(wǎng)絡(luò)會(huì)進(jìn)行一系列的操作,最終獲取到我們所需要的文件,如何交給瀏覽器進(jìn)行渲染

          我們所關(guān)注的問題也就是:

          • 如何獲取到我們所需要的文件
          • 瀏覽器是如何渲染的

          大致的執(zhí)行順序

          • URL解析
          • DNS 解析:緩存判斷 + 查詢IP地址
          • TCP 連接:TCP 三次握手
          • SSL/TLS四次握手(只有https才有這一步)
          • 瀏覽器發(fā)送請(qǐng)求
          • 服務(wù)器響應(yīng)請(qǐng)求并返回?cái)?shù)據(jù)
          • 瀏覽器解析渲染頁面
          • 斷開連接:TCP 四次揮手

          URL解析

          瀏覽器先會(huì)判斷輸入的字符是不是一個(gè)合法的URL結(jié)構(gòu),如果不是,瀏覽器會(huì)使用搜索引擎對(duì)這個(gè)字符串進(jìn)行搜索

          URL結(jié)構(gòu)組成

          https://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#anchor

          • 協(xié)議:https:// 互聯(lián)網(wǎng)支持多種協(xié)議,必須指明網(wǎng)址使用哪一種協(xié)議,默認(rèn)是 HTTP 協(xié)議。 也就是說,如果省略協(xié)議,直接在瀏覽器地址欄輸入www.example.com,那么瀏覽器默認(rèn)會(huì)訪問http://www.example.com。 HTTPS 是 HTTP 的加密版本,出于安全考慮,越來越多的網(wǎng)站使用這個(gè)協(xié)議。
          • 主機(jī):www.example.com 主機(jī)(host)是資源所在的網(wǎng)站名或服務(wù)器的名字,又稱為域名。上例的主機(jī)是www.example.com。 有些主機(jī)沒有域名,只有 IP 地址,比如192.168.2.15。
          • 端口:https:// 同一個(gè)域名下面可能同時(shí)包含多個(gè)網(wǎng)站,它們之間通過端口(port)區(qū)分。 “端口”就是一個(gè)整數(shù),可以簡(jiǎn)單理解成,訪問者告訴服務(wù)器,想要訪問哪一個(gè)網(wǎng)站。 默認(rèn)端口是80,如果省略了這個(gè)參數(shù),服務(wù)器就會(huì)返回80端口的網(wǎng)站。 端口緊跟在域名后面,兩者之間使用冒號(hào)分隔,比如www.example.com:80。
          • 路徑:/path/to/myfile.html 路徑(path)是資源在網(wǎng)站的位置。比如,/path/index.html這個(gè)路徑,指向網(wǎng)站的/path子目錄下面的網(wǎng)頁文件index.html 互聯(lián)網(wǎng)的早期,路徑是真實(shí)存在的物理位置。現(xiàn)在由于服務(wù)器可以模擬這些位置,所以路徑只是虛擬位置 路徑可能只包含目錄,不包含文件名,比如/foo/,甚至結(jié)尾的斜杠都可以省略 這時(shí),服務(wù)器通常會(huì)默認(rèn)跳轉(zhuǎn)到該目錄里面的index.html文件(即等同于請(qǐng)求/foo/index.html),但也可能有其他的處理(比如列出目錄里面的所有文件),這取決于服務(wù)器的設(shè)置 一般來說,訪問www.example.com這個(gè)網(wǎng)址,很可能返回的是網(wǎng)頁文件www.example.com/index.html
          • 查詢參數(shù):?key1=value1&key2=value2 查詢參數(shù)(parameter)是提供給服務(wù)器的額外信息。參數(shù)的位置是在路徑后面,兩者之間使用?分隔 查詢參數(shù)可以有一組或多組。每組參數(shù)都是鍵值對(duì)(key-value pair)的形式,同時(shí)具有鍵名(key)和鍵值(value),它們之間使用等號(hào)(=)連接。比如,key1=value就是一個(gè)鍵值對(duì),key1是鍵名,value1是鍵值 多組參數(shù)之間使用&連接,比如key1=value1&key2=value2
          • 錨點(diǎn):#anchor 錨點(diǎn)(anchor)是網(wǎng)頁內(nèi)部的定位點(diǎn),使用#加上錨點(diǎn)名稱,放在網(wǎng)址的最后,比如#anchor 瀏覽器加載頁面以后,會(huì)自動(dòng)滾動(dòng)到錨點(diǎn)所在的位置 錨點(diǎn)名稱通過網(wǎng)頁元素的id屬性命名

          DNS解析

          DNS(Domain Names System),域名系統(tǒng),是互聯(lián)網(wǎng)一項(xiàng)服務(wù),是進(jìn)行域名和與之相對(duì)應(yīng)的 IP 地址進(jìn)行轉(zhuǎn)換的服務(wù)器

          第一步:緩存判斷

          判斷是正確的URL格式之后,DNS會(huì)在我們的緩存中查詢是否有當(dāng)前域名的IP地址

          基本步驟:

          • 瀏覽器緩存:瀏覽器檢查是否在緩存中
          • 操作系統(tǒng)緩存:操作系統(tǒng)DNS緩存,去本地的hosts文件查找
          • 路由器緩存:路由器DNS緩存
          • ISP 緩存: ISP DNS緩存(ISP DNS 就是在客戶端電腦上設(shè)置的首選 DNS 服務(wù)器,又稱本地的DNS服務(wù)器)

          在經(jīng)歷上述緩存查找還沒有找到的話,就進(jìn)行下一步查詢操作

          第二步:查詢IP地址

          瀏覽器會(huì)去根域名服務(wù)器中查找,如果還沒有就去頂級(jí)域名服務(wù)器中查找,最后是權(quán)威域名服務(wù)器。

          找到IP地址后,將它記錄在緩存中,供下次使用。

          TCP連接:三次握手

          簡(jiǎn)單理解

          簡(jiǎn)單的理解就是:

          客戶端:hello,你好,你是server嗎?
          服務(wù)端:hello,你好,我是server,你是client嗎
          客戶端:yes,我是client
          開始數(shù)據(jù)傳輸.....
          ——————————————————————————————————————————
          客戶端(男人):我喜歡你,咱倆處對(duì)象吧
          服務(wù)端(女人):我也喜歡你,我答應(yīng)你
          客戶端(男人):太棒了,我們現(xiàn)在去看電影吧
          開始數(shù)據(jù)傳輸.....
          


          然后雙方就正確建立連接,開始傳輸數(shù)據(jù)

          詳細(xì)分析

          • 第一次握手:客戶端發(fā)送一個(gè)帶 SYN=1,Seq=x 的數(shù)據(jù)包到服務(wù)器端口 第一次握手,由瀏覽器發(fā)起,告訴服務(wù)器我要發(fā)送請(qǐng)求了 SYN(synchronous):請(qǐng)求建立連接 seq(sequence):隨機(jī)序列號(hào) 請(qǐng)注意TCP規(guī)定SYN被設(shè)置為1的報(bào)文段不能攜帶數(shù)據(jù)但要消耗掉一個(gè)序號(hào)。
          • 第二次握手:服務(wù)器發(fā)回一個(gè)帶 SYN=1, ACK=1, seq=y, ack= x+1 的響應(yīng)包以示傳達(dá)確認(rèn)信息 第二次握手,由服務(wù)器發(fā)起,告訴客戶端我準(zhǔn)備接受了,你趕緊發(fā)送吧 ACK(acknowledgement):確認(rèn),是一個(gè)確定字符 ack:ack=上一次的seq+1,作用是接受上一次遠(yuǎn)端主機(jī)傳來的seq,加一然后再傳給客戶端,提示客戶端已經(jīng)成功接收上一次所有數(shù)據(jù) 請(qǐng)注意這個(gè)報(bào)文段不能攜帶數(shù)據(jù),因?yàn)樗荢YN被設(shè)置為一的報(bào)文段但同樣要消耗掉一個(gè)序號(hào)
          • 第三次握手:客戶端再回傳一個(gè)帶 ACK,seq = x + 1, ack = y + 1 的數(shù)據(jù)包,代表“握手結(jié)束” 第三次握手,由瀏覽器發(fā)送,告訴服務(wù)器,我馬上就發(fā)了,準(zhǔn)備接受吧 確認(rèn)報(bào)文段可以攜帶數(shù)據(jù)。但如果不攜帶數(shù)據(jù)則不消耗序號(hào),在這種情況下所發(fā)送的下一個(gè)數(shù)據(jù)報(bào)文段的序號(hào)仍是x + 1

          然后雙方就正確建立連接,開始傳輸數(shù)據(jù)

          SSL/TLS四次握手

          HTTPS 建立連接的過程,先進(jìn)行 TCP 三次握手,再進(jìn)行 TLS 四次握手(僅對(duì)https)

          因?yàn)?HTTPS 都是基于 TCP 傳輸協(xié)議實(shí)現(xiàn)的,得先建立完可靠的 TCP 連接才能做 TLS 握手的事情。

          第一次握手:客戶端發(fā)出請(qǐng)求Client Hello

          • 首先,客戶端先向服務(wù)器發(fā)出加密通信的請(qǐng)求,這被叫做clienthello請(qǐng)求。
          • 在這一步,客戶端主要向服務(wù)器提供以下信息:
            • 支持的協(xié)議版本,比如TLS1.0版本
            • 支持的加密方法,比如RSA公鑰加密
            • 一個(gè)客戶端生成的隨機(jī)數(shù)(client random), 稍后用于生成對(duì)話密鑰(session key)

          第二次握手:服務(wù)器回應(yīng)Server Hello

          • 服務(wù)器收到客戶端請(qǐng)求后,向客戶端發(fā)出回應(yīng),這叫做serverhello
          • 這一步服務(wù)器主要干三件事:
            • 確認(rèn)使用的加密通信協(xié)議版本,比如TLS1.00版本。如果游覽器與服務(wù)器支持的版本不一致,服務(wù)器關(guān)閉加密通信
            • 確認(rèn)使用的加密方法(客戶端所支持),比如RSA公鑰加密
            • 將服務(wù)器證書、非對(duì)稱加密的公鑰,以及一個(gè)隨機(jī)數(shù)(Server random)發(fā)送給客戶端游覽器

          客戶端驗(yàn)證證書

          客戶端收到服務(wù)器回應(yīng)以后,首先驗(yàn)證服務(wù)器證書,驗(yàn)證手段就是執(zhí)行如下三種檢查:

          • 檢查證書是否已過期;
          • 檢查證書中的域名與實(shí)際域名是否一致
          • 檢查證書是否是可信機(jī)構(gòu)頒布的

          如果,上述過程中有任何一個(gè)環(huán)節(jié)發(fā)現(xiàn)問題,那么瀏覽器就會(huì)向訪問者顯示一個(gè)警告,由其選擇是否還要繼續(xù)通信。如果證書受信任,或者是用戶接受了不受信的證書,瀏覽器會(huì)生成一串新的隨機(jī)數(shù)(Premaster secret )

          第三次握手:客戶端回應(yīng)

          此時(shí),瀏覽器會(huì)根據(jù)前三次握手中的三個(gè)隨機(jī)數(shù):

          • Client random
          • Server random
          • Premaster secret

          通過一定的算法來生成 “會(huì)話密鑰” (Session Key),這個(gè)會(huì)話密鑰就是接下來雙方進(jìn)行對(duì)稱加密解密使用的密鑰!

          第四次握手:服務(wù)端回應(yīng)

          服務(wù)端收到客戶端的回復(fù),利用已知的加密解密方式進(jìn)行解密,服務(wù)器收到客戶端的第三個(gè)隨機(jī)數(shù)( Premaster secret) 之后,使用同樣的算法計(jì)算出 “會(huì)話密鑰” (Session Key)。

          至此,整個(gè)握手階段全部結(jié)束。接下來,客戶端與服務(wù)器進(jìn)入加密通信,就完全是使用普通的 HTTP 協(xié)議,只不過用 “會(huì)話密鑰” 加密內(nèi)容。(非對(duì)稱加密解密將不再使用,接下來完全由對(duì)稱加密接手了,因?yàn)槊荑€已經(jīng)安全的傳送給了通信的雙方)

          總結(jié)

          1. 客戶端請(qǐng)求建立SSL鏈接,并向服務(wù)發(fā)送一個(gè)隨機(jī)數(shù)–Client random客戶端支持的加密方法,比如RSA公鑰加密,此時(shí)是明文傳輸
          2. 服務(wù)端回復(fù)一種客戶端支持的加密方法一個(gè)隨機(jī)數(shù)–Server random、授信的服務(wù)器證書非對(duì)稱加密的公鑰
          3. 客戶端收到服務(wù)端的回復(fù)后利用服務(wù)端的公鑰,加上新的隨機(jī)數(shù)–Premaster secret 通過服務(wù)端下發(fā)的公鑰及加密方法進(jìn)行加密,發(fā)送給服務(wù)器
          4. 服務(wù)端收到客戶端的回復(fù),利用已知的加解密方式進(jìn)行解密,同時(shí)利用Client random、Server random和Premaster secret通過一定的算法生成HTTP鏈接數(shù)據(jù)傳輸?shù)膶?duì)稱加密key – session key

          瀏覽器發(fā)送請(qǐng)求

          連接建立成功之后,瀏覽器向服務(wù)器發(fā)送HTTP請(qǐng)求報(bào)文,來獲取自己想要的數(shù)據(jù)

          請(qǐng)求報(bào)文由請(qǐng)求行、請(qǐng)求頭、空行、請(qǐng)求體四部分組成

          • 請(qǐng)求行:有請(qǐng)求方法、請(qǐng)求的url、http協(xié)議及其版本
          • 請(qǐng)求頭:把瀏覽器的一些基礎(chǔ)信息告訴服務(wù)器。比如包含了瀏覽器所使用的操作系統(tǒng)、瀏覽器內(nèi)核等信息,以及當(dāng)前請(qǐng)求的域名信息、瀏覽器端的 Cookie 信息等
          • 空行:最后一個(gè)請(qǐng)求頭之后是一個(gè)空行,發(fā)送回車符和換行符,通知服務(wù)器以下不再有請(qǐng)求頭
          • 請(qǐng)求體(報(bào)文主體/請(qǐng)求中文):當(dāng)使用POST, PUT等方法時(shí),通常需要客戶端向服務(wù)器傳遞數(shù)據(jù)。這些數(shù)據(jù)就儲(chǔ)存在請(qǐng)求正文中。在請(qǐng)求包頭中有一些與請(qǐng)求正文相關(guān)的信息,例如: 現(xiàn)在的Web應(yīng)用通常采用Rest架構(gòu),請(qǐng)求的數(shù)據(jù)格式一般為json。這時(shí)就需要設(shè)置Content-Type: application/json。

          服務(wù)端響應(yīng)請(qǐng)求并返回?cái)?shù)據(jù)

          服務(wù)器對(duì)http請(qǐng)求報(bào)文進(jìn)行解析,并給客戶端發(fā)送HTTP響應(yīng)報(bào)文對(duì)其進(jìn)行響應(yīng)

          HTTP響應(yīng)報(bào)文也是由狀態(tài)行、響應(yīng)頭、空行、響應(yīng)體四部分組成

          • 響應(yīng)行/狀態(tài)行:由 HTTP 版本協(xié)議字段、狀態(tài)碼和狀態(tài)碼的描述文本 3 個(gè)部分組成
          • 響應(yīng)頭:用于指示客戶端如何處理響應(yīng)體,告訴瀏覽器響應(yīng)的類型、字符編碼和字節(jié)大小等信息
          • 空行:最后一個(gè)響應(yīng)頭部之后是一個(gè)空行,發(fā)送回車符和換行符,通知服務(wù)器以下不再有響應(yīng)頭部。
          • 響應(yīng)體:返回客戶端所需數(shù)據(jù)

          這個(gè)時(shí)候?yàn)g覽器拿到我們服務(wù)器返回的HTML文件,可以開始解析渲染頁面

          瀏覽器解析渲染頁面

          當(dāng)我們經(jīng)歷了上述的一系列步驟之后,我們的瀏覽器就拿到了我的HTML文件那么它又是如何解析整個(gè)頁面并且最終呈現(xiàn)出我們的網(wǎng)頁呢?

          渲染流程圖

          簡(jiǎn)圖:從這張圖上我們可以得出一個(gè)重要的結(jié)論:下載CSS文件并不會(huì)阻塞HTML的解析

          詳圖

          詳細(xì)解析步驟

          解析一:HTML解析過程

          默認(rèn)情況下服務(wù)器會(huì)給瀏覽器返回index.html文件,所以解析HTML是所有步驟的開始:解析HTML,會(huì) 構(gòu)建DOM Tree

          當(dāng)遇到我們的script文件的時(shí)候,我們是不能進(jìn)行去構(gòu)建DOM Tree的。它會(huì)停止繼續(xù)構(gòu)建,首先下載JavaScript代碼,并且執(zhí)行JavaScript的腳本,只有等到JavaScript腳本執(zhí)行結(jié)束后,才會(huì)繼續(xù)解析HTML,構(gòu)建DOM樹

          具體的相關(guān)細(xì)節(jié)看下面的script與頁面解析

          解析二:生成CSS規(guī)則

          • 在解析的過程中,如果遇到CSS的link元素,那么會(huì)由瀏覽器負(fù)責(zé)下載對(duì)應(yīng)的CSS文件
            • 注意:下載CSS文件是不會(huì)影響DOM的解析的
          • 瀏覽器下載完CSS文件后,就會(huì)對(duì)CSS文件進(jìn)行解析,解析出對(duì)應(yīng)的規(guī)則樹
            • 我們可以稱之為 CSSOM (CSS Object Model,CSS對(duì)象模型

          解析三:構(gòu)建Render Tree

          當(dāng)有了 DOM Tree 和 CSSOM Tree 后,就可以兩個(gè)結(jié)合來 構(gòu)建 Render Tree

          • 注意一:link元素不會(huì)阻塞DOM Tree的構(gòu)建過程,但是會(huì)阻塞Render Tree的構(gòu)建過程
            • 因?yàn)镽ender Tree在構(gòu)建時(shí),需要對(duì)應(yīng)的CSSOM Tree。當(dāng)我們DOMTree解析完成的時(shí)候,如果CSSOM Tree沒解析完成就會(huì)阻塞。當(dāng)然一般情況下瀏覽器會(huì)進(jìn)行優(yōu)化處理,不會(huì)傻傻的等待
          • 注意二:Render Tree和DOMTree并不是一一對(duì)應(yīng)的關(guān)系
            • 比如對(duì)于display為none的元素,壓根不會(huì)出現(xiàn)在render tree中

          解析四:布局(layout)和繪制(Paint)

          • 第四步是在**渲染樹(Render Tree)**上運(yùn)行 布局(Layout) 以計(jì)算每個(gè)節(jié)點(diǎn)的幾何體。 渲染樹會(huì) 表示 要顯示哪些節(jié)點(diǎn)以及其他樣式,但是 不表示 每個(gè)節(jié)點(diǎn)的尺寸、位置 等信息 布局的主要目的是為了確定呈現(xiàn)樹中所有節(jié)點(diǎn)的寬度、高度和位置信息
          • 第五步是將每個(gè)節(jié)點(diǎn) 繪制(Paint) 到屏幕上 在繪制階段,瀏覽器將布局階段計(jì)算的 每個(gè)frame轉(zhuǎn)為屏幕上實(shí)際的像素點(diǎn) 包括 將元素的可見部分進(jìn)行繪制,比如文本、顏色、邊框、陰影、替換元素(比如img)

          特殊解析:composite合成

          • 繪制的過程,可以將布局后的元素繪制到多個(gè)合成圖層中【這是瀏覽器的一種優(yōu)化手段】
          • 默認(rèn)情況下,標(biāo)準(zhǔn)流中的內(nèi)容都是被繪制在同一個(gè)圖層(Layer)中的
          • 而一些特殊的屬性,會(huì)創(chuàng)建一個(gè)新的合成層(Compositinglayer ),并且新的圖層可以利用GPU來加速繪制
            • 因?yàn)槊總€(gè)合成層都是單獨(dú)渲染的
          • 那么哪些屬性可以形成新的合成層呢?常見的一些屬性:
            • 3D transforms
            • video、canvas、iframe
            • opacity動(dòng)畫轉(zhuǎn)換時(shí)
            • position: fixed
            • will-change:一個(gè)實(shí)驗(yàn)性的屬性,提前告訴瀏覽器元素可能發(fā)生哪些變化
            • animation或 transition設(shè)置了opacity、transform
          • 分層確實(shí)可以提高性能,但是它以內(nèi)存管理為代價(jià),因此不應(yīng)作為web性能優(yōu)化策略的一部分過度使用

          其他相關(guān)概念

          回流

          • 回流reflow(也可以稱之為重排) 第一次確定節(jié)點(diǎn)的大小和位置,稱之為布局(layout) 之后對(duì)節(jié)點(diǎn)的大小、位置修改 重新計(jì)算 稱之為回流
          • 什么情況下引起回流呢? 比如DOM結(jié)構(gòu)發(fā)生改變(添加新的節(jié)點(diǎn)或者移除節(jié)點(diǎn)) 比如改變了布局(修改了width、height、padding、font-size等值) 比如窗口resize(修改了窗口的尺寸等) 比如調(diào)用getComputedStyle方法獲取尺寸、位置信息

          重繪

          • 重繪repaint【字面理解就是對(duì)頁面再做繪制】 第一次渲染內(nèi)容稱之為繪制(paint) 之后重新渲染稱之為重繪
          • 什么情況下會(huì)引起重繪呢? 比如修改背景色、文字顏色、邊框顏色、樣式等

          聯(lián)系

          • 回流一定會(huì)引起重繪,所以回流是一件很消耗性能的事情。
          • 所以在開發(fā)中要盡量避免發(fā)生回流 修改樣式時(shí)盡量一次性修改【比如通過cssText修改,比如通過添加class修改】 盡量 避免頻繁的操作DOM【我們可以在一個(gè)DocumentFragment或者父元素中將要操作的DOM操作完成,再一次性的操作】 盡量 避免通過getComputedStyle獲取尺寸、位置等信息 對(duì)某些元素使用position的absolute或者fixed【并不是不會(huì)引起回流,而是開銷相對(duì)較小,不會(huì)對(duì)其他元素造成影響】

          script元素

          script元素和頁面聯(lián)系

          • 我們現(xiàn)在已經(jīng)知道了頁面的渲染過程,但是JavaScript在哪里呢? 事實(shí)上,瀏覽器在解析HTML的過程中,遇到了 script元素是不能繼續(xù)構(gòu)建DOM樹的 它會(huì) 停止繼續(xù)構(gòu)建,首先下載JavaScript代碼,并且執(zhí)行JavaScript的腳本 只有 等到JavaScript腳本執(zhí)行結(jié)束后,才會(huì)繼續(xù)解析HTML,構(gòu)建DOM樹
          • 為什么要這樣做呢? 這是 因?yàn)镴avaScript的作用之一就是操作DOM,并且可以修改DOM 如果我們等到DOM樹構(gòu)建完成并且渲染再執(zhí)行JavaScript會(huì)造成嚴(yán)重的回流和重繪,影響頁面的性能 所以會(huì)在遇到script元素時(shí),優(yōu)先下載和執(zhí)行JavaScript代碼,再繼續(xù)構(gòu)建DOM樹
          • 但是這個(gè)也往往會(huì)帶來新的問題,特別是現(xiàn)代頁面開發(fā)中: 在目前的開發(fā)模式中(比如Vue、React),腳本往往比HTML頁面更“重”,處理時(shí)間需要更長(zhǎng) 所以會(huì)造成頁面的解析阻塞,在腳本下載、執(zhí)行完成之前,用戶在界面上什么都看不到
          • 為了解決這個(gè)問題,script元素給我們提供了兩個(gè)屬性(attribute) : defer和async

          defer屬性

          • defer屬性告訴瀏覽器 不要等待腳本下載,而繼續(xù)解析HTML,構(gòu)建DOM Tree
            • 腳本會(huì) 由瀏覽器來進(jìn)行下載,但是不會(huì)阻塞DOM Tree的構(gòu)建過程
            • 如果腳本提前下載好了,它會(huì) 等待DOM Tree構(gòu)建完成,在DOMContentLoaded事件之前先執(zhí)行defer中的代碼
          • 所以DOMContentLoaded總是會(huì)等待defer中的代碼先執(zhí)行完成
          <script src="./foo.js" defer></script>
          <script>
          	 window.addEventListener("DOMContentLoaded",()=>{
                   console.log("DOMContentLoaded");
               })
          </script>
          • 多個(gè)帶defer的腳步是可以保持正確的執(zhí)行順序的
          • 從某種角度來說,defer可以提高頁面的性能,并且推薦放到head元素中
          • 注意:defer僅適用于外部腳本,對(duì)于script默認(rèn)內(nèi)容會(huì)被忽略

          async屬性

          • async特性與defer有些類似,它也能夠讓腳本不阻塞頁面
          • async是讓一個(gè)腳本完全獨(dú)立的:
            • 瀏覽器 不會(huì)因async 腳本而阻塞(與defer類似)
            • async腳本不能保證順序,它是獨(dú)立下載、獨(dú)立運(yùn)行,不會(huì)等待其他腳本
            • async不會(huì)能保證在DOMContentLoaded之前或者之后執(zhí)行
          • defer通常用于需要在文檔解析后操作DOM的JavaScript代碼,并且對(duì)多個(gè)script文件有順序要求的
          • async通常用于獨(dú)立的腳本,對(duì)其他腳本,甚至DOM沒有依賴的

          斷開連接:TCP 四次揮手

          在渲染完成后,瀏覽器可能會(huì)繼續(xù)加載頁面中的其他資源,如異步加載的內(nèi)容或者通過JavaScript生成的動(dòng)態(tài)內(nèi)容。

          而在此過程中,如果沒有其他資源需要加載,瀏覽器將與服務(wù)器之間的TCP連接斷開。

          簡(jiǎn)單理解

          復(fù)制代碼主動(dòng)方:我已經(jīng)關(guān)閉了向你那邊的主動(dòng)通道了,這是我最后一次給你發(fā)消息了,之后只能被動(dòng)接收你的信息了
          被動(dòng)方:收到你通道關(guān)閉的信息
          被動(dòng)方:那我也告訴你,我這邊向你的主動(dòng)通道也關(guān)閉了
          主動(dòng)方:最后收到你關(guān)閉的信息,OK結(jié)束
          斷開連接,結(jié)束通訊
          ————————————————————————————————————————————————————————————————————————————
          提出分手的可能是男生(客戶端),也可能是女生(服務(wù)端)
          主動(dòng)方:分手吧,我不喜歡你了!
          被動(dòng)方:行,你等我忙完手上的工作我在收拾你!
          被動(dòng)方:我忙完了,分手就分手!
          主動(dòng)方:好,好聚好散,拜拜!
          斷開連接,結(jié)束通訊
          

          詳細(xì)分析

          由于TCP連接是全雙工的,因此,每個(gè)方向都必須要單獨(dú)進(jìn)行關(guān)閉,這一原則是當(dāng)一方完成數(shù)據(jù)發(fā)送任務(wù)后,發(fā)送一個(gè)FIN來終止這一方向的連接,收到一個(gè)FIN只是意味著這一方向上沒有數(shù)據(jù)流動(dòng)了,即不會(huì)再收到數(shù)據(jù)了,但是在這個(gè)TCP連接上仍然能夠發(fā)送數(shù)據(jù),直到這一方向也發(fā)送了FIN。首先進(jìn)行關(guān)閉的一方將執(zhí)行主動(dòng)關(guān)閉,而另一方則執(zhí)行被動(dòng)關(guān)閉。

          任何一方都可以在數(shù)據(jù)傳送結(jié)束后發(fā)出連接釋放的通知,所有主動(dòng)發(fā)起關(guān)閉請(qǐng)求可以是客戶端,也可以是服務(wù)端

          這里我們假設(shè)是由客戶端先主動(dòng)發(fā)起關(guān)閉請(qǐng)求

          • 第一次揮手:TCP客戶進(jìn)程會(huì)發(fā)送TCP連接釋放報(bào)文段,并進(jìn)入終止等待1(FIN-WAIT-1)狀態(tài)。 FIN:終止位,表示斷開TCP連接 TCP規(guī)定終止位FIN等于1的報(bào)文段即使不攜帶數(shù)據(jù),也要消耗掉一個(gè)序號(hào)
          • 第二次揮手:TCP服務(wù)器進(jìn)程收到TCP連接釋放報(bào)文段后,會(huì)發(fā)送一個(gè)普通的TCP確認(rèn)報(bào)文段并進(jìn)入關(guān)閉等待(CLOSE-WAIT)狀態(tài)。 序號(hào)seq字段的值設(shè)置為v,與之前收到的TCP連接釋放報(bào)文段中的確認(rèn)號(hào)匹配 TCP客戶進(jìn)程收到TCP確認(rèn)報(bào)文段后就進(jìn)入終止等待2(FIN-WAIT-2)狀態(tài),等待TCP服務(wù)器進(jìn)程發(fā)出的TCP連接釋放報(bào)文段 這時(shí)的TCP連接屬于半關(guān)閉狀態(tài),也就是TCP客戶進(jìn)程已經(jīng)沒有數(shù)據(jù)要發(fā)送了。但如果TCP服務(wù)器進(jìn)程還有數(shù)據(jù)要發(fā)送,TCP客戶進(jìn)程仍要接收,也就是說從TCP服務(wù)器進(jìn)程到TCP客戶進(jìn)程這個(gè)方向的連接并未關(guān)閉,這個(gè)狀態(tài)可能要持續(xù)一段時(shí)間。
          • 第三次揮手:TCP服務(wù)器進(jìn)程發(fā)送TCP連接釋放報(bào)文段 假定序號(hào)seq字段的值為w,這是因?yàn)樵诎腙P(guān)閉狀態(tài)下,TCP服務(wù)器進(jìn)程可能又發(fā)送了一些數(shù)據(jù)。 確認(rèn)號(hào)ack字段的值為u+1,這是對(duì)之前收到的TCP連接釋放報(bào)文段的重復(fù)確認(rèn)。
          • 第四次揮手:TCP服務(wù)器進(jìn)程收到確定報(bào)文段后就進(jìn)入關(guān)閉狀態(tài),而TCP客戶進(jìn)程還要經(jīng)過2MSL后才能進(jìn)入關(guān)閉狀態(tài)。

          之后斷開連接,結(jié)束通訊

          總結(jié)

          • 瀏覽器先判斷是否為合法的url格式,不合法則在搜索引擎中搜索
          • 合法后,DNS解析會(huì)先判斷緩存中是否有url的ip地址。
          • 緩存的查詢順序是:瀏覽器緩存 -> 操作系統(tǒng)緩存(本地的hosts文件) -> 路由器緩存 -> 本地的DNS服務(wù)器緩存
          • 在緩存中沒有的情況,則向服務(wù)器發(fā)起請(qǐng)求查詢ip地址。
          • 查詢IP地址的順序是:根域名服務(wù)器 -> 頂級(jí)域名服務(wù)器 -> 權(quán)威域名服務(wù)器。直到查找到返回,并將其存儲(chǔ)在緩存中下次使用
          • TSP建立連接,也就是三次握手
          • 第一次握手,攜帶建立連接請(qǐng)求SYN=1和隨機(jī)序列seq=x
          • 第二次握手,攜帶確定字段ACK=1、連接請(qǐng)求SYN=1、隨機(jī)序列seq=y和ack為上一次握手的seq+1,就是x+1
          • 第三次握手,攜帶確定字段ACK=1、ack=y+1、seq=x+1
          • 如果是https,還有一個(gè)TLS四次握手
          • 第一次握手,客戶端向服務(wù)端發(fā)送 支持的協(xié)議版本 + 支持的加密方法 + 生成的隨機(jī)數(shù)
          • 第二次握手,服務(wù)端向客戶端發(fā)送 證書 + 公鑰 + 隨機(jī)數(shù)
          • 第三次握手前,客戶端會(huì)先驗(yàn)證證書有沒有過期、域名對(duì)不對(duì)、是否可信機(jī)構(gòu)頒發(fā)的。
          • 沒有問題或者用戶接受不受信的證書,瀏覽器會(huì)生成一個(gè)新的隨機(jī)數(shù)
          • 第三次握手,將之前的三個(gè)隨機(jī)數(shù)通過一定的算法生成會(huì)話秘鑰,之后的加密解密都是用這個(gè)秘鑰
          • 第四次握手,服務(wù)端收到回復(fù),是用確定的加密方法進(jìn)行解密,得到第三個(gè)隨機(jī)數(shù),使用同樣的算法計(jì)算出會(huì)話秘鑰
          • 建立連接之后,瀏覽器發(fā)送http請(qǐng)求
          • 請(qǐng)求報(bào)文由請(qǐng)求行、請(qǐng)求頭、空行和請(qǐng)求體組成
          • 服務(wù)器解析請(qǐng)求報(bào)文,返回響應(yīng)報(bào)文
          • 響應(yīng)報(bào)文由響應(yīng)行、響應(yīng)頭、空行和響應(yīng)體組成,我們需要的html文件就在響應(yīng)體中
          • 瀏覽器拿到html文件并開始解析,構(gòu)建dom tree。遇到css文件,下載并構(gòu)建CSSOM tree。等到兩者都構(gòu)建完成之后,一起構(gòu)建Render tree。然后進(jìn)行布局和繪制
          • 其中遇到了script標(biāo)簽,則停止構(gòu)建dom tree,等下載完成之后才會(huì)繼續(xù)構(gòu)建dom tree
          • 當(dāng)資源傳輸完畢之后,TSP關(guān)閉連接,進(jìn)行四次揮手的操作,其中四次揮手的操作客戶端和服務(wù)器都可以發(fā)起
          • 第一次揮手,攜帶斷開連接的FIN=1、確定字段ACK=1、隨機(jī)序列seq=u,ack=v
          • 第二次揮手,攜帶確定字段ACK=1、隨機(jī)序列seq=v,ack=u+1
          • 第三次揮手,攜帶確定字段ACK=1、斷開連接FIN=1、隨機(jī)序列seq=w、ack=u+1
          • 第四次揮手,攜帶確定字段ACK=1,隨機(jī)序列seq=u+1,ack=w+1
          • 等待2MSL后進(jìn)入關(guān)閉狀態(tài)
          • 斷開連接,結(jié)束通訊


          作者:前端實(shí)習(xí)生鯨落
          鏈接:https://juejin.cn/post/7279093851000242234


          主站蜘蛛池模板: 无码人妻精品一区二区三区不卡| 国产成人一区二区在线不卡| 国产一在线精品一区在线观看| 精品一区二区三区四区在线| 亚洲综合一区二区精品导航| 一区二区三区91| 成人免费视频一区| 高清在线一区二区| 亚洲一区综合在线播放| 国产熟女一区二区三区五月婷| 亚洲日韩精品一区二区三区无码| 亚洲一区二区三区在线观看蜜桃| 无码精品蜜桃一区二区三区WW | 日本美女一区二区三区| 国精产品一区二区三区糖心| 性色AV一区二区三区无码| 九九久久99综合一区二区| 国产大秀视频一区二区三区| 无码人妻精品一区二区三区东京热| 福利国产微拍广场一区视频在线| 精品国产一区二区三区久久| 国模大胆一区二区三区| 久久久久99人妻一区二区三区 | 国产一区二区视频在线观看| 亚洲一区免费视频| 国产精品无圣光一区二区 | 一区 二区 三区 中文字幕| 亚洲乱码一区av春药高潮| 精品少妇一区二区三区在线| 国产情侣一区二区| 日韩免费视频一区二区| 一区二区三区免费视频播放器 | 国产精品一区二区电影| 精品一区二区91| 精品人妻少妇一区二区三区在线| 久久精品一区二区免费看| 精品无码一区在线观看| 精品国产一区二区三区www| 久久国产高清一区二区三区| 欧洲精品一区二区三区| 久久伊人精品一区二区三区|