整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          22《Vue 入門教程》VueRouter 路由嵌套

          22《Vue 入門教程》VueRouter 路由嵌套

          . 前言

          本小節我們介紹如何嵌套使用 VueRouter。嵌套路由在日常的開發中非常常見,如何定義和使用嵌套路由是本節的重點。同學們在學完本節課程之后需要自己多嘗試配置路由。

          2. 配置嵌套路由

          實際項目中的應用界面,通常由多層嵌套的組件組合而成。同樣地,URL 中各段動態路徑也按某種結構對應嵌套的各層組件,例如:

          /article/vue                          /article/react
          +------------------+                  +-----------------+
          | Article          |                  | Article         |
          | +--------------+ |                  | +-------------+ |
          | | Vue          | |  +------------>  | | React       | |
          | |              | |                  | |             | |
          | +--------------+ |                  | +-------------+ |
          +------------------+                  +-----------------+
          代碼塊12345678

          借助 vue-router,使用嵌套路由配置,就可以很簡單地表達這種關系。 在上一小節中我們學習了如何配置一個路由信息:

            {
              path: '路由地址',
              component: '渲染組件'
            }
          

          要配置嵌套路由,我們需要在配置的參數中使用 children 屬性:

            {
              path: '路由地址',
              component: '渲染組件',
              children: [
                {
                  path: '路由地址',
                  component: '渲染組件'
                }
              ]
            }
          

          2.1 基本使用

          接下來我們對上一小節的例子來做一個改造:在文章頁面,我們對文章進行分類,提供兩個鏈接按鈕 vue、react,點擊可以跳轉到對應的文章列表,具體代碼示例如下:

          實例演示

          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>Document</title>
          </head>
          <body>
            <div id="app">
              <div>
                <router-link to="/index">首頁</router-link>
                <router-link to="/article">文章</router-link>
              </div>
              <router-view></router-view>
            </div>
          </body>
          
          <script src="https://unpkg.com/vue/dist/vue.js"></script>
          <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
          <script type="text/javascript">
          
          const Index=Vue.component('index', {
            template: '<div>Hello,歡迎使用慕課網學習 Vue 教程!</div>',
          })
          
          const Article=Vue.component('myArticle', {
            template: `<div>
                <div>
                  <router-link to="/article/vue">vue</router-link>
                  <router-link to="/article/react">react</router-link>
                </div>
                <router-view></router-view>
              </div>`,
          })
          
          const VueArticle=Vue.component('vueArticle', {
            template: `<ul><li>1. Vue 基礎學習</li><li>2. Vue 項目實戰</li></ul>`,
          })
          
          const ReactArticle=Vue.component('reactArticle', {
            template: `<ul><li>1. React 基礎學習</li><li>2. React 項目實戰</li></ul>`,
          })
          
          const routes=[
            { path: '/index', component: Index },
            { 
              path: '/article', 
              component: Article ,
              children: [
                {
                  path: 'vue', 
                  component: VueArticle ,
                },
                {
                  path: 'react', 
                  component: ReactArticle ,
                }
              ]
            }
          ]
          
          const router=new VueRouter({
            routes: routes
          })
          
            var vm=new Vue({
              el: '#app',
              router,
              data() {
                  return {}
              }
            })
          </script>
          </html>
          

          "運行案例" 可查看在線運行效果

          代碼解釋: HTML 代碼第 12-13 行,我們定義了兩個跳轉鏈接。 HTML 代碼第 15 行,我們使用 <router-view></router-view> 組件來渲染匹配組件。 JS 代碼第 5-7 行,我們定義了組件 Index。 JS 代碼第 9-17 行,我們定義了組件 Article,組件內部使用 <router-link></router-link> 定義出來兩個跳轉鏈接,使用 <router-view></router-view> 來渲染匹配組件。 JS 代碼第 19-21 行,我們定義了組件 VueArticle. JS 代碼第 23-25 行,我們定義了組件 ReactArticle。 JS 代碼第 27-43 行,我們定義了路由數組,在 ‘/article’ 中配置來嵌套路由 children JS 代碼第 44-46 行,創建 router 實例,然后傳 routes 配置。 JS 代碼第 49 行,通過 router 配置參數注入路由。

          2.2 定義路由地址

          在上述的例子中,我們通過 ‘/article/vue’ 來訪問嵌套路由,但是有時候你可能不希望使用嵌套路徑,這時候我們可以對上面例子中的配置信息做一點修改:

          const routes=[
            { path: '/index', component: Index },
            { 
              path: '/article', 
              component: Article ,
              children: [
                {
                  path: '/vueArticle', 
                  component: VueArticle ,
                },
                {
                  path: '/reactArticle', 
                  component: ReactArticle ,
                }
              ]
            }
          ]
          

          ‘/’ 開頭的嵌套路徑會被當作根路徑,因此,我們訪問 ‘/vueArticle’ 就相當于訪問之前的 ‘/article/vue’。

          3. 小結

          本節,我們帶大家學習了 VueRouter 嵌套路由的使用方法,主要知識點有以下幾點:

          • 通過路由配置的 children 屬性定義和使用嵌套路由。
          • 通過修改路由配置的 path 屬性定義嵌套路由的跳轉地址。

          前言

            在跨域請求不同服務方或是兼容先前系統的頁面時,你可能想利用AJAX從網頁上下載HTML并粘貼到div中,這將帶來不安全注入的問題。

            此時,通過iframe頁面嵌入可以很好地解決上述問題。

          本文帶您了解iframe內聯框架,幫助您提高頁面集成效率和復用率,一次開發,多次使用。同時,解決在使用iframe跨域訪問時,第三方cookie暫存轉發問題。

            iframe安全嵌入方案

            iframe嵌入是一種快速的表示集成方法,具有以下三方面優點:

            1.iframe是一個全新的獨立的宿主環境,用于隔離或者訪問原始接口及對象,并能夠原封不動地將嵌入的網頁展現出來;

            2.如果有多個網頁引用iframe,只需要修改iframe的內容,就可以實現調用的每一個頁面內容的更改,方便快捷;

            3.重載頁面時不需要重載整個頁面,只需要重載頁面中的一個框架頁。

            例如,以vue工程為例,我們可以很輕松地嵌入iframe頁面,輕松實現頁面集成。

          <template>

          <div style="padding-top:10px;height:auto">

          <iframe id='mainFrame' name='mainFrame' ref='mainFrame' :src="iframeUrl" target="_blank" height='1000px' frameborder="0" width="100%" ></iframe>

          </div>

          </template>

          <script>

          export default {

          name: 'MyTestResource',

          data() {

          return{

          iframeUrl: "",

          }

          },

          created(){

          this.iframeUrl=http://40. + "xxx-ev/ev/xxx/RedirectITA?toUrl=zycx&resouceCode=BBB&token=" + getLocalStorageToken()

          },

            在嵌入第三方服務頁面時,需要處理統一身份認證和保持登錄狀態的問題,下圖所示為iframe安全注入方案示例。

            iframe安全注入服務流程為,用戶在本方服務頁面進行登錄,本方服務向統一身份認證平臺驗證用戶服務權限并得到本方服務令牌。

            當用戶訪問嵌入第三方服務的iframe頁面時,本方服務向統一身份認證平臺驗證用戶服務權限并得到第三方服務令牌,通過URL傳送給第三方服務,第三方服務解析后向統一身份認證平臺驗證該用戶權限令牌信息,若用戶權限令牌信息正確則提供服務。

            其中第三方服務對iframe 嵌入可以通過如下配置方式實現。

            IE瀏覽器配置

            為了控制iframe嵌入的權限,需要在Headers里面加入X-Frame-Options字段,其有三種值可以配置,具體如下表格所示。

            Chrome、Edge瀏覽器配置

            經過測試發現X-Frame-Options 對Chrome及Edge瀏覽器不起作用,需要配置Content-Security-Policy: frame-ancestors 對iframe 頁面嵌入進行安全控制。

            X-Frame-Options及 Content-Security-Policy 可以同時配置多個白名單,具體如下:

            Content-Security-Policy:

            frame-ancestors http://aaa.com http://bbb.com http://ccc.com

            X-Frame-Options:

            ALLOW-FROM http://aaa.com,http://bbb.com,http://ccc.com

            對于Nginx、apache 、IIS、tomcat 等不同的中間件有不同的配置方法,再次不再贅述,讀者可以根據需要的場景進行不同配置。

            瀏覽器訪問iframe問題

            我們在訪問iframe 嵌入頁面的時候,從本系統前端調用系統后臺服務后,sendRedirect到第三方系統后臺服務,對X-Frame-Options頭進行了驗證后,正常應該跳轉到請求頁面并對本系統瀏覽器進行內容響應。

            實際使用過程中卻發生了奇怪的現象,第三方服務驗證X-Frame-Options跨站驗證后,302 跳轉到了請求頁面,又302 調用了登出接口,截止302 調用了登錄接口,最終為用戶響應了登錄頁面,如下圖所示。

            猜測一下,問題可能出在以下幾個方面:

            1.第三方服務調用統一安全認證服務失敗,跳轉登錄頁面,但是并沒有安全認證失敗的響應信息;

            2.第三方服務沒有用戶信息,跳轉登錄頁面;

            3.第三方請求跳轉頁面時,對用戶信息進行了基于瀏覽器信息的再次認證,失敗并跳轉登錄頁面。

            經過驗證更換其他瀏覽器后,頁面可以正常訪問,說明統一安全認證無問題,且用戶信息也驗證正常,因此問題應該是第三種情況。

            瀏覽器訪問iframe解決方案

            經過分析發現,Chrome內核51版本開始,其對于Cookie的控制新增加了一個SameSite屬性,用來防止CSRF攻擊和用戶追蹤。

            由第三方提供iframe嵌入頁面服務的Cookie在Chrome內核被認為是第三方Cookie,瀏覽器默認會禁止第三方Cookie跨域傳送,這里就涉及到瀏覽器Cookie 的SameSite屬性。

            SameSite 可以有下面三種值:

            1.Strict僅允許一方請求攜帶 Cookie,即瀏覽器將只發送相同站點請求的 Cookie,即當前網頁 URL 與請求目標 URL 完全一致;

            2.Lax允許部分第三方請求攜帶 Cookie;

            3.None無論是否跨站都會發送 Cookie。

            通過下面表格,我們可以清晰看到,不同請求類型下,SameSit屬性對第三方Cookie 的響應方式。

            從上表可以看出,對大部分 web 應用而言,Post 表單、iframe、AJAX這三種情況從以前的跨站會發送三方Cookie,變成了不發送。

            故這三種情況跨站給第三方發送Cookie需要在設置的時候將SameSite設置為None。

            通過研究,我們發現可以通過配置Chrome 、Edge瀏覽器SameSite 屬性可以有效解決該問題。

            低于91版本的Chrome瀏覽器

            Chrome中訪問地址chrome://flags/ 搜索samesite 將same-site-by-default-cookies,和SameSite by default cookies這兩項設置為Disabled后重啟瀏覽器再運行項目即可解決。該設置默認情況下會將未指定SameSite屬性的請求看做SameSite=Lax來處理。

            Chrome及Edge瀏覽器

            打開瀏覽器快捷方式的屬性,在目標后添加

            --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure ,點擊應用、確定按鈕,重啟瀏覽器后生效。

            "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"

            通過執行腳本直接生成有效的快捷方式

          chcp 65001

          echo 正在創建桌面快捷方式,請勿關閉本窗口.

          ::設置程序或文件的完整路徑(必選)

          set Program=%cd%\chrome.exe

          ::設置快捷方式名稱(必選)

          set LnkName=SSSChrome

          ::設置程序的工作路徑,一般為程序主目錄,此項若留空,腳本將自行分析路徑

          set WorkDir=%cd%

          ::設置快捷方式顯示的說明(可選)

          set Desc=SSSChrome

          if not defined WorkDir call:GetWorkDir "%Program%"

          (echo Set WshShell=CreateObject("WScript.Shell"^)

          echo strDesKtop=WshShell.SpecialFolders("DesKtop"^)

          echo Set oShellLink=WshShell.CreateShortcut(strDesKtop^&"\%LnkName%.lnk"^)

          echo oShellLink.TargetPath="%Program%"

          echo oShellLink.Arguments="--disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure"

          echo oShellLink.WorkingDirectory="%WorkDir%"

          echo oShellLink.WindowStyle=1

          echo oShellLink.Description="%Desc%"

          echo oShellLink.Save)>makelnk.vbs

          echo 桌面快捷方式創建成功!

          makelnk.vbs

          del /f /q makelnk.vbs

          exit

          goto :eof

          :GetWorkDir

          set WorkDir=%~dp1

          set WorkDir=%WorkDir:~,-1%

          goto :eof

            從下圖我們可以看到,響應請求,由原來的4個302 后跳轉到登錄頁面變為2個302 跳轉后正常進入功能頁面,解決了請求第三方iframe時不發送Cookie 造成第三方服務認證通過跳轉登錄頁面的問題。

            總結

            本文介紹了iframe內聯框架,幫助您安全地嵌入iframe頁面并提高頁面集成效率和復用率,達到代碼的高復用和良好的用戶體驗。

            請求第三方iframe時,能夠對iframe進行請求頭驗證及統一安全驗證,實現iframe頁面安全嵌入。針對瀏覽器訪問第三方iframe時存在的第三方Cookie 不能正常發送問題,提供了可行的解決方案,可以為頁面嵌入使用方面提供有效的借鑒。

          最后:

          1)關注+私信回復:“測試”,可以免費領取一份10G軟件測試工程師面試寶典文檔資料。以及相對應的視頻學習教程免費分享!,其中包括了有基礎知識、Linux必備、Mysql數據庫、抓包工具、接口測試工具、測試進階-Python編程、Web自動化測試、APP自動化測試、接口自動化測試、測試高級持續集成、測試架構開發測試框架、性能測試等。

          2)關注+私信回復:"入群" 就可以邀請你進入軟件測試群學習交流~~

          輸入 URL 到頁面加載完成的過程中都發生了什么?

          簡單路徑線

          1. 鍵盤或觸屏輸入URL并回車確認
          2. URL解析/DNS解析查找域名IP地址
          3. 網絡連接發起HTTP請求
          4. HTTP報文傳輸過程
          5. 服務器接收數據
          6. 服務器響應請求/MVC
          7. 服務器返回數據
          8. 客戶端接收數據
          9. 瀏覽器加載/渲染頁面
          10. 打印繪制輸出

          實際上并沒有這么簡單,下面說說它的詳細路徑線

          1. 鍵盤或觸屏輸入URL并回車確認

          當然故事其實并不是從輸入一個URL或抓著鼠標點擊一個鏈接開始的,事情的開端要追溯到服務器啟動監聽服務的時候,在某個未知的時刻,一臺機房里普普通通的服務器,加上電,啟動了操作系統,隨著操作系統的就緒,服務器啟動了 http 服務進程,這個 http 服務的守護進程(daemon),可能是 Apache、Nginx、IIS、Lighttpd中的一個,不管怎么說,這個 http 服務進程開始定位到服務器上的 www 文件夾(網站根目錄),一般是位于 /var/www ,然后啟動了一些附屬的模塊,例如 php,或者,使用 fastcgi 方式連接到 php 的 fpm 管理進程,然后,向操作系統申請了一個 tcp 連接,然后綁定在了 80 端口,調用了 accept 函數,開始了默默的監聽,監聽著可能來自位于地球任何一個地方的請求,隨時準備做出響應。這個時候,典型的情況下,機房里面應該還有一個數據庫服務器,或許,還有一臺緩存服務器,如果對于流量巨大的網站,那么動態腳本的解釋器可能還有單獨的物理機器來跑,如果是中小的站點,那么,上述的各色服務,甚至都可能在一臺物理機上,這些服務監聽之間的關系,可以通過自己搭建一次 Apache PHP MySQL 環境來了解一下,不管怎么說,他們做好了準備,靜候差遣。

          然后是開始鍵盤或手機觸屏輸入URL,然后通過某種機制傳到CPU(過程略),CPU進行內部處理(過程略),處理完后,再從CPU傳到操作系統內核(過程略),然后再由操作系統GUI傳到瀏覽器,再由瀏覽器到瀏覽器內核。這個過程因為涉及很多底層的知識,自己也只是了解皮毛,過程這里不多講了。

          上面一步操作系統 GUI 會將輸入事件傳遞到瀏覽器中,在這過程中,瀏覽器可能會做一些預處理,甚至已經在智能匹配所有可能的URL了,他會從歷史記錄,書簽等地方,找到已經輸入的字符串可能對應的URL,來預估所輸入字符對應的網站,然后給出智能提示,比如輸入了「ba」,根據之前的歷史發現 90% 的概率會訪問「www.baidu.com 」,因此就會在輸入回車前就馬上開始建立 TCP 鏈接了。對于 Chrome這種變態的瀏覽器,他甚至會直接從緩存中把網頁渲染出來,就是說,你還沒有按下「回車」鍵,頁面就已經出來了,再比如Chrome會在瀏覽器啟動時預先查詢10個你有可能訪問的域名等等,這里面還有很多其它策略,不詳細講了。感興趣的推薦閱讀 High Performance Networking in Chrome。

          2. URL 解析/DNS 查詢

          接著是輸入 URL 「回車」后,這時瀏覽器會對 URL 進行檢查,這里需要對URL有個回顧,請見百科《URL》,完整的URL由幾個部分構成:

          協議、網絡地址、資源路徑、文件名、動態參數

          協議/模式(scheme)是從該計算機獲取資源的方式,一般有Http、Https、Ftp、File、Mailto、Telnet、News等協議,不同協議有不同的通訊內容格式,協議主要作用是告訴瀏覽器如何處理將要打開的文件;

          網絡地址指示該連接網絡上哪一臺計算機(服務器),可以是域名或者IP地址,域名或IP地址后面有時還跟一個冒號和一個端口號;

          端口號如果地址不包含端口號,根據協議的類型會確定一個默認端口號。端口號之于計算機就像窗口號之于銀行,一家銀行有多個窗口,每個窗口都有個號碼,不同窗口可以負責不同的服務。端口只是一個邏輯概念,和計算機硬件沒有關系。一般如果你的端口號就是默認的,那么url是不需要輸入端口號的,但如果你更改了默認端口號,你就必須要在url后輸入新端口號才能正常訪問。例如:http協議默認端口號是80。如果你輸入的url是http://www.zhihu.com:8080/ ,那表示不使用默認的端口號,而使用指定的端口號8080。如果使用的就是默認端口號那么輸入http://www.zhihu.com:80 和http://www.zhihu.com是一樣的。有個特殊情況有所不同,比如本地IP 127.0.0.1 其實走的是 loopback,和網卡設備沒關系。

          資源路徑指示從服務器上獲取哪一項資源的等級結構路徑,以斜線/分隔;

          文件名一般是需要真正訪問的文件,有時候,URL以斜杠“/”結尾,而隱藏了文件名,在這種情況下,URL引用路徑中最后一個目錄中的默認文件(通常對應于主頁),這個文件常被稱為 index.html 或 default.htm。

          動態參數有時候路徑后面會有以問號?開始的參數,這一般都是用來傳送對服務器上的數據庫進行動態詢問時所需要的參數,有時候沒有,很多為了seo優化,都已處理成偽靜態了。要注意區分url和路由的區別。

          URL完整格式為:協議://用戶名:密碼@子域名.域名.頂級域名:端口號/目錄/文件名.文件后綴?參數=值#標志

          例如:https://www.zhihu.com/question/55998388/answer/166987812

          協議部分:https

          網絡地址:www.zhihu.com(依次為 子/三級域名.二級域名.頂/一級域名)

          資源路徑:/question/55998388/answer/166987812

          瀏覽器對 URL 進行檢查時首先判斷協議,如果是 http/https 就按照 Web 來處理,另外還會對 URL 進行安全檢查,然后直接調用瀏覽器內核中的對應方法,接下來是對網絡地址進行處理,如果地址不是一個IP地址而是域名則通過DNS(域名系統)將該地址解析成IP地址。IP地址對應著網絡上一臺計算機,DNS服務器本身也有IP,你的網絡設置包含DNS服務器的IP。 例如:www.zhihu.com域名請求對應獲得的IP是 116.211.167.187。DNS 在解析域名的時候有兩種方式:遞歸查詢和迭代查詢

          遞歸查詢的流程如下:

          一般來說,瀏覽器會首先查詢瀏覽器緩存(DNS 在各個層級都有緩存的,相應的,緩存當然有過期時間,Time to live),如果沒有找到,就會檢查系統緩存,檢查本地硬盤的hosts文件,這個文件保存了一些以前訪問過的網站的域名和IP對應的數據。它就像是一個本地的數據庫。如果找到就可以直接獲取目標主機的IP地址了(注意這個地方存在安全隱患,如果有病毒把一些常用的域名,修改 hosts 文件,指向一些惡意的IP,那么瀏覽器也會不加判斷的去連接,是的,這正是很多病毒的慣用手法)。如果本地hosts也沒有找到的話,則需要再向上層找路由器緩存,路由器有自己的DNS緩存,可能就包括了查詢的內容;如果還是沒有,需要接著往上找,查詢ISP DNS 緩存(本地名稱服務器緩存,就是客戶端電腦TCP/IP參數中設置的首選DNS服務器,此解析具有權威性。一般情況下你在不同的地區或者不同的網絡,如電信、聯通、移動的情況下,轉換后的IP地址很可能是不一樣的,這涉及到負載均衡,通過DNS解析域名時會將你的訪問分配到不同的入口,先找附近的本地 DNS 服務器去請求解析域名,盡可能保證你所訪問的入口是所有入口中較快的一個,這和CDN還不一樣,比如我們經常使用的114.114.114.114或Google的8.8.8.8就是本地名稱服務器)。如果附近的本地DNS服務器還是沒有緩存我們請求的域名記錄的話,這時候會根據本地DNS服務器的設置(是否設置轉發器)進行查詢,如果未用轉發模式,則本地名稱服務器再以DNS客戶端的角色發送與前面一樣的DNS域名查詢請求轉發給上一層。這里可能經過一次或者多次轉發,從本地名稱服務器權威名稱服務器再到頂級名稱服務器最后到根名稱服務器。(順便一提,根服務器是互聯網域名解析系統DNS中最高級別的域名服務器,全球一共13組,每組都只有一個主根名稱服務器采用同一個IP。注意不是13個,前期是個現在已經是集群了,據說已經有上千臺了,好多臺用于負載均衡,備份等,全球有386臺根物理服務器,被編號為A到M共13個標號。中國包括臺港也持有其中5組14臺輔根服務器或叫鏡像也可以,386臺根服務器總共只使用了13個IP,因此可以抵抗針對其所進行的分布式拒絕服務攻擊DDoS。具體情況可以參看維基百科的 根域名服務器 條目)所以,最終請求到了根服務器后,根服務器查詢發現我們這個被請求的域名是由類似A或者B這樣的服務器解析的,但是,根服務器并不會送佛送到西地找A或B之類的直接去解析,因為它沒有保存全部互聯網域名記錄,并不直接用于名稱解析,它只是負責頂級名稱服務器(如.com/.cn/.net等)的相關內容。所以它會把所查詢得到的被請求的DNS域名中頂級域名所對應的頂級名稱服務器IP地址返回給本地名稱服務器。本地名稱服務器拿到地址后再向對應的頂級名稱服務器發送與前面一樣的DNS域名查詢請求。對應的頂級名稱服務器在收到DNS查詢請求后,也是先查詢自己的緩存,如果有則直接把對應的記錄項返回給本地名稱服務器,然后再由本地名稱服務器返回給DNS客戶端,如果沒有則向本地名稱服務器返回所請求的DNS域名中的二級域名所對應的二級名稱服務器(如baidu.com/qq.com/net.cn等)地址。然后本地名稱服務器繼續按照前面介紹的方法一次次地向三級(如www.baidu.com/www.qq.com/bbs.taobao.com等)、四級名稱服務器查詢,直到最終的對應域名所在區域的權威名稱服務器返回最終記錄給本地名稱服務器。同時本地名稱服務器會緩存本次查詢得到的記錄項(每層都應該會緩存)。再層層下傳,最后到了我們的DNS客戶端機子,一次 DNS 解析請求就此完成。如果最終權威名稱服務器都說找不到對應的域名記錄,則會向本地名稱服務器返回一條查詢失敗的DNS應答報文,這條報文最終也會由本地名稱服務器返回給DNS客戶端。當然,如果這個權威名稱服務器上配置了指向其它名稱服務器的轉發器,則權威名稱服務器還會在轉發器指向的名稱服務器上進一步查詢。另外,如果DNS客戶端上配置了多個DNS服務器,則還會繼續向其它DNS服務器查詢的。

          DNS遞歸解析示例圖

          所以,我們看到DNS的域名解析是遞歸的,遞歸的DNS首先會查看自己的DNS緩存,如果緩存能夠命中,那么就從緩存中把IP地址返回給瀏覽器,如果找不到對應的域名的IP地址,那么就依此層層向上轉發請求,從根域名服務器到頂級域名服務器再到極限域名服務器依次搜索對應目標域名的IP,最高達到根節點,找到或者全部找不到為止。然后把找到的這個域名對應的 nameserver 的地址拿到,再向這個 namserver 去請求域名對應的IP,最后把這個IP地址返回給瀏覽器,在這個遞歸查詢的過程中,對于瀏覽器來說是透明的,如果DNS客戶端的本地名稱服務器不能解析的話,則后面的查詢都會以本地名稱服務器為中心,全交由本地名稱服務器代替DNS客戶端進行查詢,DNS客戶端只是發出原始的域名查詢請求報文,然后就一直處于坐等狀態,直到本地名稱服務器最終從權威名稱服務器得到了正確的IP地址查詢結果并返回給它。雖然遞歸查詢是默認的DNS查詢方式,但是如果有以下情況發生的話,則會使用迭代的查詢方式進行。

          情況一:DNS客戶端的請求報文中沒有申請使用遞歸查詢,即在DNS請求報頭部的RD字段沒有置1。

          情況二:DNS客戶端的請求報文中申請使用的是遞歸查詢(也就是RD字段置1了),但在所配置的本地名稱服務器上是禁用遞歸查詢了(即在應答DNS報文頭部的RA字段置0)。迭代查詢的流程如下

          開始也是從瀏覽器緩存到系統緩存到路由緩存,如果還是沒找到則客戶端向本機配置的本地名稱服務器(在此僅以首先DNS服務器為例進行介紹,其它備用DNS服務器的解析流程完全一樣)發出DNS域名查詢請求。本地名稱服務器收到請求后,先查詢本地的緩存,如果有該域名的記錄項,則本地名稱服務器就直接把查詢的結果返回給客戶端;如果本地緩存中沒有該域名的記錄,則向DNS客戶端返回一條DNS應答報文,報文中會給出一些參考信息,如本地名稱服務器上的根名稱服務器地址等。DNS客戶端在收到本地名稱服務器的應答報文后,會根據其中的根名稱服務器地址信息,向對應的根名稱服務器再次發出與前面一樣的DNS查詢請求報文。根名稱服務器在收到DNS查詢請求報文后,通過查詢自己的DNS數據庫得到請求DNS域名中頂級域名所對應的頂級名稱服務器信息,然后以一條DNS應答報文返回給DNS客戶端。DNS客戶端根據來自根名稱服務器應答報文中的對應頂級名稱服務器地址信息,向該頂級名稱服務器發出與前面一樣的DNS查詢請求報文。頂級名稱服務器在收到DNS查詢請求后,先查詢自己的緩存,如果有請求的DNS域名的記錄項,則直接把對應的記錄項返回給DNS客戶端,否則通過查詢后把對應域名中二級域名所對應的二級名稱服務器地址信息以一條DNS應答報文返回給DNS客戶端。然后DNS客戶端繼續按照前面介紹的方法一次次地向三級、四級名稱服務器查詢,直到最終的權威名稱服務器返回到最終的記錄。如果權威名稱服務器也找不到對應的域名記錄,則會向DNS客戶端返回一條查詢失敗的DNS應答報文。當然,如果這個權威名稱服務器上配置了指向其它名稱服務器的轉發器,則權威名稱服務器還會在轉發器指向的名稱服務器上進一步查詢。另外,如果DNS客戶端上配置了多個DNS服務器,則還會繼續向其它DNS服務器查詢。

          DNS迭代解析示意圖

          所以,我們發現在遞歸查詢中后面的查詢工作是由本地名稱服務器替代DNS客戶端進行的(以“本地名稱服務器”為中心),只需要本地名稱服務器向DNS客戶端返回最終的查詢結果即可。而DNS迭代查詢的所有查詢工作則全部是DNS客戶端自己進行(以“DNS客戶端”自己為中心)。DNS遞歸查詢和迭代查詢的區別

          遞歸查詢是以本地名稱服務器為中心的,是DNS客戶端和服務器之間的查詢活動,遞歸查詢的過程中“查詢的遞交者” 一直在更替,其結果是直接告訴DNS客戶端需要查詢的網站目標IP地址。

          迭代查詢則是DNS客戶端自己為中心的,是各個服務器和服務器之間的查詢活動,迭代查詢的過程中“查詢的遞交者”一直沒變化,其結果是間接告訴DNS客戶端另一個DNS服務器的地址。

          遞歸和迭代查詢

          舉個例子來說,一次選修課上你碰到了你的女神,但你只知道她的名字并不知道她的電話,于是在手機通訊錄里找,沒找到,然后你回到寢室把她名字告訴了一個很仗義的哥們,讓他幫你找,這個哥們兒(本地名稱服務器)二話沒說,開始替你查(此處完成了一次遞歸查詢,即問詢的人由你變成了你的哥們)。然后你哥們帶著名字去問了學院大四的學長,學長一看,我擦,這姑娘我認識啊,于是在手機里找到了她的電話告訴了你哥們,你哥們回來告訴了你,你于是知道了她的電話(這里一次遞歸查詢結束)。還有一種可能你哥們跑去問學長,學長也沒她電話,但學長告訴你哥們,這姑娘是xx系的;然后你哥們兒馬不停蹄又問了xx系的辦公室主任助理同學,助理同學說是xx系yy班的,然后很仗義的哥們兒去xx系yy班的班長那里取到了該女孩的電話(此處完成若干次迭代查詢,即問詢的人一直是你哥們不變,但反復更替的是問詢對象)。最后,他把號碼交到了你手里,完成整個查詢過程。擴展閱讀:

          什么是DNS劫持?

          什么是301重定向?與301重定向設置教程

          電腦上不了網將DNS改為114.114.114.114或8.8.8.8可以解決或加快網速的原理是什么?

          局域網 IP 和公網 IP 有何差別?

          根域名服務器的作用是什么?全球 13 組根域名服務器中有 10 組在美國,意味著什么?

          遞歸和迭代的區別?

          3. 應用層客戶端發送HTTP請求

          互聯網內各網絡設備間的通信都遵循TCP/IP協議,利用TCP/IP協議族進行網絡通信時,會通過分層順序與對方進行通信。分層由高到低分別為:應用層、傳輸層、網絡層、數據鏈路層。發送端從應用層往下走,接收端從數據鏈路層網上走。如圖所示:

          從上面的步驟中得到 IP 地址后,瀏覽器會開始構造一個 HTTP 請求,應用層客戶端向服務器端發送的HTTP請求包括:請求報頭和請求主體兩個部分,其中請求報頭(request header)包含了至關重要的信息,包括請求的方法(GET / POST和不常用的PUT / DELETE以及更不常用的HEAD / OPTION / TRACE,一般的瀏覽器只能發起 GET 或者 POST 請求)、目標url、遵循的協議(HTTP / HTTPS / FTP…),返回的信息是否需要緩存,以及客戶端是否發送Cookie等信息。需要注意的是,因為 HTTP 請求是純文本格式的,所以在 TCP 的數據段中可以直接分析 HTTP 文本的。

          4. 傳輸層TCP傳輸報文

          當應用層的 HTTP 請求準備好后,瀏覽器會在傳輸層發起一條到達服務器的 TCP 連接,位于傳輸層的TCP協議為傳輸報文提供可靠的字節流服務。它為了方便傳輸,將大塊的數據分割成以報文段為單位的數據包進行管理,并為它們編號,方便服務器接收時能準確地還原報文信息。TCP協議通過“三次握手”等方法保證傳輸的安全可靠。“三次握手”的過程是,發送端先發送一個帶有SYN(synchronize)標志的數據包給接收端,在一定的延遲時間內等待接收的回復。接收端收到數據包后,傳回一個帶有SYN/ACK標志的數據包以示傳達確認信息。接收方收到后再發送一個帶有ACK標志的數據包給接收端以示握手成功。在這個過程中,如果發送端在規定延遲時間內沒有收到回復則默認接收方沒有收到請求,而再次發送,直到收到回復為止。

          這里需要談一下 TCP 的 Head-of-line blocking 問題:假設客戶端的發送了 3 個 TCP 片段(segments),編號分別是 1、2、3,如果編號為 1 的包傳輸時丟了,即便編號 2 和 3 已經到達也只能等待,因為 TCP 協議需要保證順序,這個問題在 HTTP pipelining 下更嚴重,因為 HTTP pipelining 可以讓多個 HTTP 請求通過一個 TCP 發送,比如發送兩張圖片,可能第二張圖片的數據已經全收到了,但還得等第一張圖片的數據傳到。為了解決 TCP 協議的性能問題,Chrome 團隊提出了 QUIC 協議,它是基于 UDP 實現的可靠傳輸,比起 TCP,它能減少很多來回(round trip)時間,還有前向糾錯碼(Forward Error Correction)等功能。目前 Google Plus、 Gmail、Google Search、blogspot、Youtube 等幾乎大部分 Google 產品都在使用 QUIC,可以通過chrome://net-internals/#spdy 頁面來發現。另外,瀏覽器對同一個域名有連接數限制,大部分是 6,但并非將這個連接數改大后就會提升性能,Chrome 團隊有做過實驗,發現從 6 改成 10 后性能反而下降了,造成這個現象的因素有很多,如建立連接的開銷、擁塞控制等問題,而像 SPDY、HTTP 2.0 協議盡管只使用一個 TCP 連接來傳輸數據,但性能反而更好,而且還能實現請求優先級。

          5. 網絡層IP協議查詢MAC地址

          IP協議的作用是把TCP分割好的各種數據包封裝到IP包里面傳送給接收方。而要保證確實能傳到接收方還需要接收方的MAC地址,也就是物理地址才可以。IP地址和MAC地址是一一對應的關系,一個網絡設備的IP地址可以更換,但是MAC地址一般是固定不變的。ARP協議可以將IP地址解析成對應的MAC地址。當通信的雙方不在同一個局域網時,需要多次中轉才能到達最終的目標,在中轉的過程中需要通過下一個中轉站的MAC地址來搜索下一個中轉目標。

          6. 數據到達數據鏈路層

          在找到對方的MAC地址后,已被封裝好的IP包再被封裝到數據鏈路層的數據幀結構中,將數據發送到數據鏈路層傳輸,再通過物理層的比特流送出去。這時,客戶端發送請求的階段結束。

          這些分層的意義在于分工合作,數據鏈路層通過 CSMA/CD 協議保證了相鄰兩臺主機之間的數據報文傳遞,而網絡層的 IP 數據包通過不同子網之間的路由器的路由算法和路由轉發,保證了互聯網上兩臺遙遠主機之間的點對點的通訊,不過這種傳輸是不可靠,于是可靠性就由傳輸層的 TCP 協議來保證,TCP 通過慢開始,乘法減小等手段來進行流量控制和擁塞避免,同時提供了兩臺遙遠主機上進程到進程的通信,最終保證了 HTTP 的請求頭能夠被遠方的服務器上正在監聽的 HTTP 服務器進程收到,終于,數據包在跳與跳之間被拆了又封裝,在子網與子網之間被轉發了又轉發,最后進入了服務器的操作系統的緩沖區,服務器的操作系統由此給正在被阻塞住的 accept 函數一個返回,將他喚醒。

          7. 服務器接收數據

          接收端的服務器在鏈路層接收到數據包,再層層向上直到應用層。這過程中包括在傳輸層通過TCP協議將分段的數據包重新組成原來的HTTP請求報文。

          8. 服務器響應請求并返回相應文件

          服務接收到客戶端發送的HTTP請求后,服務器上的的 http 監聽進程會得到這個請求,然后一般情況下會啟動一個新的子進程去處理這個請求,同時父進程繼續監聽。http 服務器首先會查看重寫規則,然后如果請求的文件是真實存在,例如一些圖片,或 html、css、js 等靜態文件,則會直接把這個文件返回,如果是一個動態的請求,那么會根據 url 重寫模塊的規則,把這個請求重寫到一個 rest 風格的 url 上,然后根據動態語言的腳本,來決定調用什么類型的動態文件腳本解釋器來處理這個請求。

          我們以 php 語言為例來說的話,請求到達一個 php 的 mvc 框架之后,框架首先應該會初始化一些環境的參數,例如遠端 ip,請求參數等等,然后根據請求的 url 送到一個路由器類里面去匹配路由,路由由上到下逐條匹配,一旦遇到 url 能夠匹配的上,而且請求的方法也能夠命中的話,那么請求就會由這個路由所定義的處理方法去處理。

          請求進入處理函數之后,如果客戶端所請求需要瀏覽的內容是一個動態的內容,那么處理函數會相應的從數據源里面取出數據,這個地方一般會有一個緩存,例如 memcached 來減小 db 的壓力,如果引入了 orm 框架的話,那么處理函數直接向 orm 框架索要數據就可以了,由 orm 框架來決定是使用內存里面的緩存還是從 db 去取數據,一般緩存都會有一個過期的時間,而 orm 框架也會在取到數據回來之后,把數據存一份在內存緩存中的。

          orm 框架負責把面向對象的請求翻譯成標準的 sql 語句,然后送到后端的 db 去執行,db 這里以 mysql 為例的話,那么一條 sql 進來之后,db 本身也是有緩存的,不過 db 的緩存一般是用 sql 語言 hash 來存取的,也就是說,想要緩存能夠命中,除了查詢的字段和方法要一樣以外,查詢的參數也要完全一模一樣才能夠使用 db 本身的查詢緩存,sql 經過查詢緩存器,然后就會到達查詢分析器,在這里,db 會根據被搜索的數據表的索引建立情況,和 sql 語言本身的特點,來決定使用哪一個字段的索引,值得一提的是,即使一個數據表同時在多個字段建立了索引,但是對于一條 sql 語句來說,還是只能使用一個索引,所以這里就需要分析使用哪個索引效率最高了,一般來說,sql 優化在這個點上也是很重要的一個方面。

          sql 由 db 返回結果集后,再由 orm 框架把結果轉換成模型對象,然后由 orm 框架進行一些邏輯處理,把準備好的數據,送到視圖層的渲染引擎去渲染,渲染引擎負責模板的管理,字段的友好顯示,也包括負責一些多國語言之類的任務。對于一條請求在 mvc 中的生命周期,可以參考這里,臨摹了一個 PHP MVC 框架,在視圖層把頁面準備好后,再從動態腳本解釋器送回到 http 服務器,由 http 服務器把這些正文加上一個響應頭,封裝成一個標準的 http 響應包,再通過 tcp ip 協議,送回到客戶機瀏覽器。

          9 瀏覽器開始處理數據信息并渲染頁面

          歷經千辛萬苦,我們請求的響應終于成功到達了客戶端的瀏覽器,響應到達瀏覽器之后,瀏覽器首先會根據返回的響應報文里的一個重要信息——狀態碼,來做個判斷。如果是 200 開頭的就好辦,表示請求成功,直接進入渲染流程,如果是 300 開頭的就要去相應頭里面找 location 域,根據這個 location 的指引,進行跳轉,這里跳轉需要開啟一個跳轉計數器,是為了避免兩個或者多個頁面之間形成的循環的跳轉,當跳轉次數過多之后,瀏覽器會報錯,同時停止。比如:301表示永久重定向,即請求的資源已經永久轉移到新的位置。在返回301狀態碼的同時,響應報文也會附帶重定向的url,客戶端接收到后將http請求的url做相應的改變再重新發送。如果是 400 開頭或者 500 開頭的狀態碼,瀏覽器也會給出一個錯誤頁面。比如:404 not found 就表示客戶端請求的資源找不到。

          當瀏覽得到一個正確的 200 響應之后,接下來面臨的一個問題就是多國語言的編碼解析了,響應頭是一個 ascii 的標準字符集的文本,這個還好辦,但是響應的正文本質上就是一個字節流,對于這一坨字節流,瀏覽器要怎么去處理呢?首先瀏覽器會去看響應頭里面指定的 encoding 域,如果有了這個東西,那么就按照指定的 encoding 去解析字符,如果沒有的話,那么瀏覽器會使用一些比較智能的方式,去猜測和判斷這一坨字節流應該使用什么字符集去解碼。相關的筆記可以看這里,字符集編碼

          接下來就是構建 dom 樹了,在 html 語言嵌套正常而且規范的情況下,這種 xml 標記的語言是比較容易的能夠構建出一棵 dom 樹出來的,當然,對于互聯網上大量的不規范的頁面,不同的瀏覽器應該有自己不同的容錯去處理。構建出來的 dom 本質上還是一棵抽象的邏輯樹,構建 dom 樹的過程中,如果遇到了由 script 標簽包起來的 js 動態腳本代碼,那么會把代碼送到 js 引擎里面去跑,如果遇到了 style 標簽包圍起來的 css 代碼,也會保存下來,用于稍后的渲染。如果遇到了 img 或 css 和 js等引用外部文件的標簽,那么瀏覽器會根據指定的 url 再次發起一個新的 http 請求,去把這個文件拉取回來,值得一提的是,對于同一個域名下的下載過程來說,瀏覽器一般允許的并發請求是有限的,通常控制在兩個左右,所以如果有很多的圖片的話,一般出于優化的目的,都會把這些圖片使用一臺靜態文件的服務器來保存起來,負責響應,從而減少主服務器的壓力。

          dom 樹構造好了之后,就是根據 dom 樹和 css 樣式表來構造 render 樹了,這個才是真正的用于渲染到頁面上的一個一個的矩形框的樹,網頁渲染是瀏覽器最復雜、最核心的功能,對于 render 樹上每一個框,需要確定他的 x y 坐標,尺寸,邊框,字體,形態,等等諸多方面的東西,render 樹一旦構建完成,整個頁面也就準備好了,可以上菜了。需要說明的是,下載頁面,構建 dom 樹,構建 render 樹這三個步驟,實際上并不是嚴格的先后順序的,為了加快速度,提高效率,讓用戶不要等那么久,現在一般都并行的往前推進的,現代的瀏覽器都是一邊下載,下載到了一點數據就開始構建 dom 樹,也一邊開始構建 render 樹,構建了一點就顯示一點出來,這樣用戶看起來就不用等待那么久了。

          10)將渲染好的頁面圖像顯示出來,并開始響應用戶的操作。

          這一步主要涉及顯卡,內存及顯示器原理等知識,不做詳細解說,大概就是從內存到 LCD/LED,再由光線進入人眼的一個過程。

          以上過程簡單講主要是:從輸入 URL 到瀏覽器接收(回車前),從瀏覽器接收到數據如何發送給網卡(回車后),再把接收的數據從本機網卡發送到服務器,服務器接收到數據后做了怎么的處理?服務器返回數據后瀏覽器又做了哪些處理?瀏覽器又是如何將處理好的頁面展現在屏幕上的?的這么一個過程。

          但只是最基本的一些步驟,實際不可能就這么簡單,一些可選的步驟例如網頁緩存、連接池、加載策略、加密解密、代理中轉等等都沒有提及。即使基本步驟本身也有很復雜的子步驟,TCP/IP、DNS、HTTP、HTML等等,還需要考慮很多情況,比如廣播、拆包解包合并包丟包重傳、路由表,NAT、TCP 狀態機、CDN、HTTPS 證書校驗與中間人攻擊檢測、RSA 密鑰協商、AES 加解密、瀏覽器解析 HTTP 的有限自動狀態機、GUI 庫與繪圖、OpenGL 繪圖、GPU 加速(OpenCL 與 CUDA)、JIT(JavaScript 會把 JavaScript 代碼編譯成匯編代碼)、服務器的數據庫 NoSQL 或 SQL 查詢、主從數據庫同步、服務器和瀏覽器的內存管理(WebKit 實現的 fastMalloc(),服務器上可能是 TCMalloc 或者 JeMalloc)、服務器上的語言解釋器(可能也是 JIT)、多媒體:傅里葉變換、H.264 解碼(硬件解碼,硬件解碼的話 GPU 的處理單元又在計算.......或軟件解碼)、音頻解碼、WebGL 繪圖、瀏覽器的 Sandbox、服務器的 SQL 注入檢查、產生的鍵盤中斷信號處理(或者是高級層面的輸入輸出驅動)、網卡驅動、網絡棧的 TCP FastOpen、SYN Cookie 之類眾多技術……每一個都可以展開成龐大的課題,而瀏覽器的基礎——操作系統、編譯器、硬件等更是一個比一個復雜。即便是計算機專業的同學看了也會頭大,但我保證這里面的每一個步驟都經過深思熟慮和時間的考驗的,并不是誰閑的蛋疼非要搞得那么復雜,不復雜也不行啊。你輸入URL即可瀏覽互聯網,而計算機系統在背后做了無數你看不到的工作,計算機各個子領域無數工程師為此付出了你難以想象的努力。有興趣的可以閱讀下

          你剛才在淘寶上買了一件東西

          作者:卐鑫卍

          鏈接:https://www.jianshu.com/p/d616d887953a


          主站蜘蛛池模板: 日韩一区二区超清视频| 中文字幕人妻第一区| 国内偷窥一区二区三区视频| 一区二区亚洲精品精华液| 久久综合一区二区无码| 国产综合视频在线观看一区| 亚洲一区二区三区高清视频| 色狠狠色噜噜Av天堂一区| 色综合视频一区二区三区| 国产视频一区在线播放| 无码一区二区三区亚洲人妻| 亚洲乱码一区二区三区国产精品 | 一区二区三区视频| 久久久综合亚洲色一区二区三区 | 色一情一乱一区二区三区啪啪高| 亚洲国产精品一区二区成人片国内 | 日本免费一区尤物| 色综合视频一区二区三区44| 中文乱码字幕高清一区二区| 精品黑人一区二区三区| 亚洲一区二区三区高清视频| 中文激情在线一区二区| 无码国产精品一区二区免费式直播| 亚洲一区二区三区四区在线观看| 国产成人精品一区二区秒拍| 亚洲欧洲一区二区| 午夜影院一区二区| 国产第一区二区三区在线观看| 日本精品高清一区二区2021| 加勒比精品久久一区二区三区| 精品一区二区三区在线观看l| 国产精品一区二区不卡| 午夜视频一区二区| 国产不卡视频一区二区三区| 激情综合一区二区三区| 色欲精品国产一区二区三区AV| 国产成人久久精品区一区二区| 无码人妻精一区二区三区| 午夜精品一区二区三区在线视| 中文字幕人妻丝袜乱一区三区 | 免费无码一区二区|