国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

從URL到頁面

honhon / 1772人閱讀

一個老生常談的問題,從輸入url到頁面渲染完成之間發(fā)生了什么?

在這個過程中包括以下2大部分:

- 1.http請求響應(yīng)
- 2.渲染

1.http請求響應(yīng)

先來提三個問題:
1.當(dāng)輸入url后,瀏覽器如何包裝發(fā)起請求?
2.在發(fā)出請求--接到響應(yīng)之間發(fā)生了什么?
3.當(dāng)返回請求結(jié)果后,瀏覽器如何解析結(jié)果?

1.1 請求 1.1.1 GET請求包裝

1.為了知道瀏覽器是如何包裝http請求的,使用nodejs搭建服務(wù)器

const http = require("http");

const server = http.createServer((req,res) => {
    if(req.url === "/"){
        res.end("hello")
    }
});

server.listen(8005,() => {
    console.log("server listen on http://localhost:8005")
});

2.服務(wù)器搭建好了,需要知道瀏覽器到底包裝了什么信息,直接看控制臺:

Request URL: http://localhost:8005/
Request Method: GET
Status Code: 200 OK
Remote Address: [::1]:8005
Referrer Policy: no-referrer-when-downgrade
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Connection: keep-alive
Host: localhost:8005
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36
1.1.2 POST請求包裝

這些是瀏覽器自動包裝過后的請求,包括請求行,請求頭和請求主體,瀏覽器默認(rèn)發(fā)送的是GET請求,如果需要指定POST請求,可以寫個表單來驗證一下,大概意思是瀏覽器發(fā)起post請求,服務(wù)端接收到后返回success,瀏覽器端顯示返回的內(nèi)容

//index.html




這樣寫的時候,由于html文件的協(xié)議是file,所以為了解決跨域問題,需要服務(wù)端進(jìn)行設(shè)置

const http = require("http");

const server = http.createServer((req,res) => {
    if(req.url === "/"){
        res.setHeader("Access-Control-Allow-Origin", "*")
        res.setHeader("Access-Control-Allow-methods", "GET, POST, OPTIONS, PUT, DELETE")
        res.setHeader("Access-Control-Allow-Headers","*")
        res.setHeader("Content-type","application/plain")
        res.end("success!!!")
    }
});

server.listen(8005,() => {
    console.log("server listen on http://localhost:8005")
});


這樣一次post請求就成功了,來看看瀏覽器默認(rèn)包裝了什么信息

Request URL: http://localhost:8005/
Request Method: POST
Status Code: 200 OK
Remote Address: [::1]:8005
//自動使用https協(xié)議
Referrer Policy: no-referrer-when-downgrade
Content-type: application/*
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36

這些信息有的是我們自己在后端寫的,有的是瀏覽器自動添加的

1.2 過程 1.2.1 整體流程

前面已經(jīng)知道了瀏覽器在發(fā)起GET或者POST請求的時候會自動的添加的字段,那瀏覽器在發(fā)送請求后到接收到服務(wù)端傳來的數(shù)據(jù)前這段時間發(fā)生了什么?
網(wǎng)上看到大家的回答大部分都是:

1.接收 URL,并拆分成協(xié)議,網(wǎng)絡(luò)地址,資源路徑

2.與緩存進(jìn)行比對,如果請求的對象在緩存中,則直接進(jìn)行第9步

3.檢查域名是否在本地的 host 的文件中,在則直接返回 IP 地址,不在則向 DNS 服務(wù)器請求,直到查詢到 IP 地址

4.瀏覽器向服務(wù)器發(fā)起一個 TCP 連接

5.瀏覽器通過 TCP 連接向服務(wù)器發(fā)起 HTTP 請求,HTTP 三次握手,HTTPS 握手過程則復(fù)雜得多

6.瀏覽器接受 HTTP 響應(yīng),這時候它能關(guān)閉 TCP 連接也能為另一個連接保留。

7.檢查 HTTP header 里的狀態(tài)碼,并做出不同的處理方式。比如:錯誤(4XX、5XX),重定向(3XX),授權(quán)請求(2XX)

8.如果是可以緩存的,這個響應(yīng)則會被存儲起來

9.瀏覽器進(jìn)行解碼響應(yīng),并決定如何處理該響應(yīng)(比如HTML頁面,圖像,聲音等等)

10.瀏覽器渲染響應(yīng),或者為不能識別的類型提供下載的提示框

1.2.2 域名解析流程

這樣的回答確實把相關(guān)的流程說了一遍,但是DNS是如何把域名解析成IP的?這個過程可以被觀察到么?三次握手又是什么意思?
為了看到域名解析的過程,我們可以使用Nslookup,它是由微軟發(fā)布用于對DNS服務(wù)器進(jìn)行檢測和排錯的命令行工具
比如可以看一下,https://www.baidu.com它的IP是什么,nslookup https://www.baidu.com
我在查看的時候一直報延時錯誤,只好從網(wǎng)上引用一張圖來說明一下了

其中server代表本地地址ip,下面那個address是百度的ip
通過這樣的方式就能看到具體域名解析的過程

1.2.3 三次握手流程

接下來是三次握手,當(dāng)域名轉(zhuǎn)化成IP后,瀏覽器沿著ip找到服務(wù)器,進(jìn)行三次握手:

第一次握手:客戶端的應(yīng)用進(jìn)程主動打開,并向客戶端發(fā)出請求報文段。其首部中:SYN=1,seq=x。

第二次握手:服務(wù)器應(yīng)用進(jìn)程被動打開。若同意客戶端的請求,則發(fā)回確認(rèn)報文,其首部中:SYN=1,ACK=1,ack=x+1,seq=y

第三次握手:客戶端收到確認(rèn)報文之后,通知上層應(yīng)用進(jìn)程連接已建立,并向服務(wù)器發(fā)出確認(rèn)報文,其首部:ACK=1,ack=y+1。當(dāng)服務(wù)器收到客戶端的確認(rèn)報文之后,也通知其上層應(yīng)用進(jìn)程連接已建立


看到這里,有個問題,前兩次握手已經(jīng)把客戶端和服務(wù)端聯(lián)系在一起了,那為什么還要第三次握手?

如果是兩次握手,當(dāng)A想要建立連接時發(fā)送一個SYN,然后等待ACK,結(jié)果這個SYN因為網(wǎng)絡(luò)問題沒有及時到達(dá)B,所以A在一段時間內(nèi)沒收到ACK后,在發(fā)送一個SYN,B也成功收到,然后A也收到ACK,這時A發(fā)送的第一個SYN終于到了B,對于B來說這是一個新連接請求,然后B又為這個連接申請資源,返回ACK,然而這個SYN是個無效的請求,A收到這個SYN的ACK后也并不會理會它,而B卻不知道,B會一直為這個連接維持著資源,造成資源的浪費,但如果是三次握手,如果第三次握手遲遲不來,服務(wù)器便會認(rèn)為這個SYN是無效的,釋放相關(guān)資源
1.3 響應(yīng)

成功發(fā)起請求并完整走完了上述流程,瀏覽器能獲得服務(wù)器發(fā)來的數(shù)據(jù),那這些數(shù)據(jù)被放在哪里,它是如何被瀏覽器處理的?
其實這個問題很簡單,在前面成功發(fā)起http請求后,服務(wù)端會有一個響應(yīng),這里面規(guī)定了各種文件格式

Access-Control-Allow-Headers: *
Access-Control-Allow-methods: GET, POST, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 10
Content-type: application/plain
Date: Wed, 08 May 2019 07:12:14 GMT
2.渲染 2.1 整體流程

數(shù)據(jù)請求回來以后,瀏覽器是如何把數(shù)據(jù)轉(zhuǎn)化成頁面的呢?這個過程就涉及到了DOM樹,CSSOM樹,render樹的生成和頁面的繪制,先來貼圖看看整體流程:

在構(gòu)建DOM樹的時候,遇到 js 和 CSS元素,HTML解析器就換將控制權(quán)轉(zhuǎn)讓給JS解析器或者是CSS解析器。開始構(gòu)建CSSOM,在構(gòu)建CSSOM樹的時候,解析是從右向左進(jìn)行的,DOM樹構(gòu)建完之后和CSSOM合成一棵render tree
有了Render Tree,瀏覽器已經(jīng)能知道網(wǎng)頁中有哪些節(jié)點、各個節(jié)點的CSS定義以及他們的從屬關(guān)系。下一步操作稱之為Layout,顧名思義就是計算出每個節(jié)點在屏幕中的位置
Layout后,瀏覽器已經(jīng)知道了哪些節(jié)點要顯示(which nodes are visible)、每個節(jié)點的CSS屬性是什么(their computed styles)、每個節(jié)點在屏幕中的位置是哪里(geometry)。就進(jìn)入了最后一步:Painting,按照算出來的規(guī)則,通過顯卡,把內(nèi)容畫到屏幕上,HTML默認(rèn)是流式布局的,CSS和js會打破這種布局,改變DOM的外觀樣式以及大小和位置,當(dāng)尺寸改變時會reflow,也就是重新繪制,比如table布局整體尺寸改變,頁面就需要重繪,但當(dāng)非尺寸改變時,會進(jìn)行replaint

通過這個分析知道了DOM樹的生成過程中可能會被CSS和JS的加載執(zhí)行阻塞,所以平時寫CSS時,盡量用id和class,千萬不要過渡層疊,盡量減少會造成reflow的操作,把JS代碼放到頁面底部,且JavaScript 應(yīng)盡量少影響 DOM 的構(gòu)建
2.2 底層源碼

這樣說一遍,還是在很表面的層次在說渲染這件事,那有沒有更深層次的理解呢?可以通過看瀏覽器源碼來進(jìn)行分析:
大致分為三個步驟:

1.HTMLDocumentParser負(fù)責(zé)解析html文本為tokens

2.HTMLTreeBuilder對這些tokens分類處理

3.HTMLConstructionSite調(diào)用不同的函數(shù)構(gòu)建DOM樹


接下來使用這個html文檔來說明DOM樹的構(gòu)建過程:




    


    

demo

2.2.1生成tokens

首先是>>>HTMLDocumentParser負(fù)責(zé)解析html文本為tokens

void DocumentLoader::commitData(const char* bytes, size_t length) {
  ensureWriter(m_response.mimeType());
  if (length)
    m_dataReceived = true;
  m_writer->addData(bytes, length);//內(nèi)部調(diào)用HTMLDocumentParser
}

構(gòu)建出來的token是包含頁面元素的信息表:

tagName: html  |type: DOCTYPE   |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
"
tagName: html  |type: startTag  |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
"
tagName: head  |type: startTag  |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
    "
tagName: meta  |type: startTag  |attr:charset=utf-8 |text: "
tagName:       |type: Character |attr:              |text: 
"
tagName: head  |type: EndTag    |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
"
tagName: body  |type: startTag  |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
    "
tagName: div   |type: startTag  |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
        "
tagName: h1    |type: startTag  |attr:class=title   |text: "
tagName:       |type: Character |attr:              |text: demo"
tagName: h1    |type: EndTag    |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
        "
tagName: input |type: startTag  |attr:value=hello   |text: "
tagName:       |type: Character |attr:              |text: 
    "
tagName: div   |type: EndTag    |attr:              |text: "
tagName:       |type: Character |attr:              |text:     
"
tagName: body  |type: EndTag    |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
"
tagName: html  |type: EndTag    |attr:              |text: "
tagName:       |type: Character |attr:              |text: 
"
tagName:       |type: EndOfFile |attr:              |text: "
2.2.2tokens分類

接著是>>>>>HTMLTreeBuilder對這些tokens分類處理

void HTMLTreeBuilder::processToken(AtomicHTMLToken* token) {
  if (token->type() == HTMLToken::Character) {
    processCharacter(token);
    return;
  }
 
  switch (token->type()) {
    case HTMLToken::DOCTYPE:
      processDoctypeToken(token);
      break;
    case HTMLToken::StartTag:
      processStartTag(token);
      break;
    case HTMLToken::EndTag:
      processEndTag(token);
      break;
    //othercode
  }
}
2.2.3 構(gòu)建DOM樹

最后,最關(guān)鍵的就是HTMLConstructionSite調(diào)用不同的函數(shù)構(gòu)建DOM樹,它根據(jù)不同的節(jié)點類型進(jìn)行不同的處理

1.DOCTYPE的處理
 // tagName不是html,那么文檔類型將會是怪異模式
  if (name != "html" ) {
    setCompatibilityMode(Document::QuirksMode);
    return;
  }
 // html4寫法,文檔類型是有限怪異模式
  if (!systemId.isEmpty() &&
       publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//",
                           TextCaseASCIIInsensitive))) {
    setCompatibilityMode(Document::LimitedQuirksMode);
    return;
  }
  // h5的寫法,標(biāo)準(zhǔn)模式
  setCompatibilityMode(Document::NoQuirksMode);

不同的模式會造成什么影響?

  // There are three possible compatibility modes:
  // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in
  // this mode, e.g., unit types can be omitted from numbers.
  // Limited Quirks - This mode is identical to no-quirks mode except for its
  // treatment of line-height in the inline box model.
  // No Quirks - no quirks apply. Web pages will obey the specifications to the
  // letter.
  //怪異模式會模擬IE,同時CSS解析會比較寬松,例如數(shù)字單位可以省略,
  //有限怪異模式和標(biāo)準(zhǔn)模式的唯一區(qū)別在于在于對inline元素的行高處理不一樣
  //標(biāo)準(zhǔn)模式將會讓頁面遵守文檔規(guī)定
2.開標(biāo)簽的處理

首先是標(biāo)簽,處理這個標(biāo)簽的任務(wù)應(yīng)該是實例化一個HTMLHtmlElement元素,然后把它的父元素指向document

HTMLConstructionSite::HTMLConstructionSite(
    Document& document)
    : m_document(&document),
      m_attachmentRoot(document)) {
}
void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken* token) {
  HTMLHtmlElement* element = HTMLHtmlElement::create(*m_document);//創(chuàng)建一個html結(jié)點
  attachLater(m_attachmentRoot, element);//加到一個任務(wù)隊列里面
  m_openElements.pushHTMLHtmlElement(HTMLStackItem::create(element, token));//壓到一個棧里面,這個棧存放了未遇到閉標(biāo)簽的所有開標(biāo)簽
  executeQueuedTasks();//執(zhí)行隊列里面的任務(wù)
}
//建立一個task
void HTMLConstructionSite::attachLater(ContainerNode* parent,Node* child, bool selfClosing) {
  HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
  task.parent = parent;
  task.child = child;
  task.selfClosing = selfClosing;
 
  // Add as a sibling of the parent if we have reached the maximum depth
  // allowed.
  if (m_openElements.stackDepth() > maximumHTMLParserDOMTreeDepth &&
      task.parent->parentNode())
    task.parent = task.parent->parentNode();
 
  queueTask(task);
}
//executeQueuedTasks根據(jù)task的類型執(zhí)行不同的操作
void ContainerNode::parserAppendChild(Node* newChild) {
  if (!checkParserAcceptChild(*newChild))
    return;
    AdoptAndAppendChild()(*this, *newChild, nullptr);
  }
  notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
}
//建立起html結(jié)點的父子兄弟關(guān)系
void ContainerNode::appendChildCommon(Node& child) {
  child.setParentOrShadowHostNode(this);//設(shè)置子元素的父結(jié)點,也就是會把html結(jié)點的父結(jié)點指向document
  if (m_lastChild) {
  //子元素的previousSibling指向老的lastChild,老的lastChild的nexSibling指向它
    child.setPreviousSibling(m_lastChild);
    m_lastChild->setNextSibling(&child);
  } else {
      //如果沒有l(wèi)astChild,會將這個子元素作為firstChild
    setFirstChild(&child);
  }
  //子元素設(shè)置為當(dāng)前ContainerNode(即document)的lastChild
  setLastChild(&child);
}

每當(dāng)遇到一個開標(biāo)簽時,就把它壓起來,下一次再遇到一個開標(biāo)簽時,它的父元素就是上一個開標(biāo)簽,借助一個棧建立起了父子關(guān)系

3.閉標(biāo)簽的處理

第一個閉標(biāo)簽是head標(biāo)簽,它會把開的head標(biāo)簽pop出來,棧里面就剩下html元素了,所以當(dāng)再遇到body時,html元素就是body的父元素了

m_tree.openElements()->popUntilPopped(token->name());

至此,一個url到頁面的過程差不多就完成了,寫這篇參考了很多文章,鏈接貼在下面,大家可以去看看:
1.簡述TCP連接的建立與釋放(三次握手、四次揮手):https://www.cnblogs.com/zhuwq...
2.從輸入 URL 到頁面加載完成發(fā)生了什么事:https://segmentfault.com/a/11...
3.十分鐘讀懂瀏覽器渲染流程:https://segmentfault.com/a/11...
4.從Chrome源碼看瀏覽器如何構(gòu)建DOM樹 :https://zhuanlan.zhihu.com/p/...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/54995.html

相關(guān)文章

  • 輸入URL頁面可交互的過程探究之一:服務(wù)端客戶端

    摘要:大多數(shù)情況,為了安全考慮,瀏覽器會強(qiáng)制使用同源策略,意味著一個源無法訪問另一個源的數(shù)據(jù)。如果想要從加載一個文件,它就需要在實行同源策略的瀏覽中發(fā)起一個跨域資源請求。 原文:https://alistapart.com/articl... 最近發(fā)現(xiàn)國外有一個系列,專門探究從輸入URL到頁面可交互的詳細(xì)過程,是一份干貨十足的好資料。筆者決定分為四篇文章對其進(jìn)行有刪減地翻譯,只希望能對大家...

    Gu_Yan 評論0 收藏0
  • 路由原理出發(fā),深入閱讀理解react-router 4.0的源碼

    摘要:通過前端路由可以實現(xiàn)單頁應(yīng)用本文首先從前端路由的原理出發(fā),詳細(xì)介紹了前端路由原理的變遷。接著從的源碼出發(fā),深入理解是如何實現(xiàn)前端路由的。執(zhí)行上述的賦值后,頁面的發(fā)生改變。 ??react-router等前端路由的原理大致相同,可以實現(xiàn)無刷新的條件下切換顯示不同的頁面。路由的本質(zhì)就是頁面的URL發(fā)生改變時,頁面的顯示結(jié)果可以根據(jù)URL的變化而變化,但是頁面不會刷新。通過前端路由可以實現(xiàn)...

    Miyang 評論0 收藏0
  • 頁面跳轉(zhuǎn)與瀏覽器記錄

    摘要:所以再做頁面跳轉(zhuǎn)的時候如果不想留下記錄,還是用比較保險,如果想留下記錄,應(yīng)該幾百毫秒再跳轉(zhuǎn)。解決辦法先用給瀏覽器添加一條記錄,然后用的方法替換掉添加的記錄,這樣記錄里存的就是和解決方案 空 location.href = url location.reload() location.replace(url) url完全不變的情況下 刷新Docment,不會產(chǎn)生記錄 刷新Doc...

    learn_shifeng 評論0 收藏0
  • 輸入URL頁面展示

    摘要:本地服務(wù)器收到信息后,再去聯(lián)系頂級域名服務(wù)器。頂級域名服務(wù)器收到請求后,如果自己無法解析,再返回下一級域名服務(wù)器的,進(jìn)行這樣一個迭代查詢之后,一直到子域名服務(wù)器。布局完成后,將渲染樹轉(zhuǎn)換成屏幕上的像素,顯示頁面。 當(dāng)我們輸入 URL 并按回車后,瀏覽器會對 URL 進(jìn)行檢查,首先判斷URL格式,比如是ftp http ed2k等等,我們這里假設(shè)這個URL是http://hellocas...

    yearsj 評論0 收藏0
  • 輸入URL頁面展示

    摘要:本地服務(wù)器收到信息后,再去聯(lián)系頂級域名服務(wù)器。頂級域名服務(wù)器收到請求后,如果自己無法解析,再返回下一級域名服務(wù)器的,進(jìn)行這樣一個迭代查詢之后,一直到子域名服務(wù)器。布局完成后,將渲染樹轉(zhuǎn)換成屏幕上的像素,顯示頁面。 當(dāng)我們輸入 URL 并按回車后,瀏覽器會對 URL 進(jìn)行檢查,首先判斷URL格式,比如是ftp http ed2k等等,我們這里假設(shè)這個URL是http://hellocas...

    gplane 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<