執行時的頁面構建過程

MarcusY發表於2018-12-21

前言

本文是閱讀《JavaScipt忍者祕籍》的心得體會

  • 瀏覽器是否會總是根據給定的html來渲染頁面?
  • web 應用一次能處理多少個事件?
  • 為什麼瀏覽器使用事件佇列來處理事件?

一、web應用的生命週期

典型的生命週期當使用者輸入url,瀏覽器向伺服器傳送了請求伺服器接收了請求並且返回一個由html,css,hmtl組成的響應瀏覽器接收了該響應後就開始了。

然後瀏覽器開始根據響應構建頁面建立使用者介面。然後開始做事件處理,進入事件佇列的迴圈中,等待事件的發生,然後呼叫處理器。

應用的生命週期隨這使用者關掉或者離開介面結束

二、如何構建頁面?

  • 解析HTML程式碼並構建文件物件模型(DOM)
  • 執行JavaScript程式碼

在頁面構建過程中這兩個步驟會交替執行多次。

#HTML解析和DOM構建

瀏覽器通過解析接收到的HTML程式碼構建一個個HTML元素來構建DOM。

在這個過程中每一個HTML元素都被當成一個節點,除了根節點外每一個節點都只有一個父節點,可以有任意數量個子節點,同一個元素節點的子節點被稱作兄弟節點。

以HTML為藍本渲染DOM的時候還會自動修復一些錯誤。

<
html>
<
head>
<
div>
<
/div>
<
/head>
<
body>
<
body>
<
/html>
複製程式碼

在這裡我們把內容元素div放到了header中。

image

瀏覽器自動把它調整到了body中

直到我們遇到了一種特殊型別的HTML元素——指令碼元素,指令碼元素中包括JavaScript程式碼,當我們們遇到了這種元素就必須停止DOM的渲染開始執行JavaScript程式碼。

#執行JavaScript程式碼

js程式碼由瀏覽器引擎執行,firefox的Spidemonkey引擎,Chrome和Opera 和V8引擎和IE的Chakra引擎。js程式碼的目的是提供動態頁面,瀏覽器通過全域性物件提供了api使JavaScript引擎可以與之互動並改變其內容。

#JavaScript中的全域性物件

JavaScript物件中存在一個window物件,它代表了整個包含頁面的視窗。

它最重要的屬性是document,代表了當前頁面的dom。

window物件是老大,通過跟老大接觸我們可以獲取其他所有的全域性物件,全域性變數和瀏覽器API的訪問途徑。我們可以在任何程度上改變DOM,修改,刪除,新增節點。

#全域性程式碼和函式程式碼

位置上函式外的是全域性變數,函式內的函式變數。

執行方式上去全域性程式碼會由JavaScript引擎以一種直接的方式自動執行,要想執行函式程式碼就要被其他程式碼呼叫,可以是全域性程式碼,也可以是其他函式,又或者是被瀏覽器呼叫。

在頁面構建階段,如果遇到了指令碼節點,瀏覽器就會停止從HTML到DOM的節點,開始執行JavaScript程式碼。看如下程式碼

<
body>
<
button id="button1" type="button">
<
/button>
<
!--建立一個函式,點選新增元素,給元素新增內容插入到dom節點。-->
<
script>
function createButton(element, content) {
var createMessage = window.document.createElement('div');
createMessage.textContent = content;
element.appendChild(createMessage);

} var button1 = document.getElementById('button1');
createButton(button1, '哈哈哈哈') <
/script>
<
div id="div1">
<
/div>
<
!--建立一個點選事件,呼叫之前建立的函式-->
<
script>
window.document.body.onclick = function () {
var div1 = document.getElementById('div1') createButton(div1, '笑一個吧~')
} <
/script>
<
/body>
複製程式碼

JavaScript幾乎可以新增刪除修改所有的節點,但是它不能去動沒有建立的節點,所以為了避免這個問題,我們要把JavaScript程式碼放到頁面底部。

當JavaScript程式碼執行結束後,瀏覽器就退出了JavaScript的執行模式繼續HTML到DOM的解析過程直到再次遇到指令碼節點。重複以上過程。最後頁面建立階段就結束了。

並且在這個過程中所有建立的全域性變數都能正常被其他指令碼元素中的JavaScript程式碼訪問到。原因在於全域性物件window物件在整個頁面的生存期當中。

三、事件處理

事件處理的重點在於一次只能執行一個程式碼片段,可以想象一下食堂一次只能一個人打飯。

由於一次只能執行一個程式碼片段,瀏覽器把觸發的事件依次推到了一個事件佇列中以這種方式來跟蹤尚未處理的事件。

瀏覽器會檢查事件佇列頭,如果沒有找到就繼續檢查,如果找到了事件就取出該事件執行相應的事件處理器,重複執行。

此外放置事件的佇列是獨立在頁面構建階段和事件處理階段以外的。

#非同步

事件是非同步的,程式碼的提前建立是為了保證之後的執行。

#註冊事件處理器

  • 一個方法是把函式賦給某些特殊屬性
window.onload=function(){
}複製程式碼
  • 使用內建方法 addEventListener
document.body.addEventListener("onclick",function(){
})複製程式碼

使用後者的優勢在於可以註冊儘可能多的事件,而前者只能註冊一個事件處理器

#處理事件的流程

觸發事件後,事件被推到事件佇列中。

image

解決問題

  • 瀏覽器是否會總是根據給定的html來渲染頁面?

是的。但是不僅僅是THML,在頁面構建過程中解析HTML成DOM和JavaScript程式碼的執行是交替進行的(這裡不講css)

  • web 應用一次能處理多少個事件?

一次一個。由於JavaScipt基於一個單執行緒的執行模型。

  • 為什麼瀏覽器使用事件佇列來處理事件?

因為一次只能執行一個,所以為了追蹤觸發的事件就使用了事件佇列,並且佇列是先進先出的頁很公平。

來源:https://juejin.im/post/5c1c33296fb9a049fb43a0be

相關文章