忍者 Chapter2. 在執行時期產生網頁

2020-05-25
JS新手村

忍者 Chapter2. 在執行時期產生網頁

Web應用程式生命週期

❗️ javascript如何適應這個生命週期

  • 瀏覽器能否用遠正確地根據HTML代碼產生出網頁?
  • 一個Web應用程式可以同時處理多少事件?
  • 為什麼瀏覽器必須使用事件佇列來處理多的事件?

生命週期概述

由頁面建立和事件處理兩的階段組成

  1. 開始於使用者輸入URL連結或點擊URL連結
  2. 瀏覽器發聲請求傳送到伺服器
  3. 伺服器處理請求並產生由HTML, CSS, JavaScript所組成的回應頁面
  4. 瀏覽器接收到這個回應頁面,便是Web應用程式開始有生命的一刻
  5. 事件處理,進入一個等待事件發生的迴圈 (6)
  6. 使用者與頁面元素互動(5)
  7. 使用者關閉頁面,網頁的生命週期結束

頁面建立階段

透過

  1. 解析HTML並建立文件物件模型(DOM)
  2. 執行Javascript程式

瀏覽器根據需要,在這兩個步驟切換許多次

解析HTML並建立DOM

  • 一次處理一個HTML元素
  • 一個節點可以有任何數量的子結點
  • 相同父元素的子節點稱為兄弟元素
  • 遇到特殊類型的HTML元素(如script),瀏覽器會值時停止由HTML代碼來建立DOM,而開始執行JavaScript

DOM由HTML所建構,但兩者不相同。
HTML為建立最初的DOM所遵循的藍圖,瀏覽器會修復在藍圖中發現的問題並建立有效的DOM

HTML最新版本為HTML5
規格
Mozille

DOM為DOM3
規格
Mozille

執行JAVAScript程式碼

script中所有的JavaScript由瀏覽器的JavaScipt引擎執行

瀏覽器JavaScript引擎
FirefoxSpidermonkey
Chrome、OperaV8
Edge、IEChakra

JS程式碼的主要目的為提供頁面的行為,瀏覽器藉由一個全域物件提供一組API,讓JS引擎與頁面互動並修改頁面

JavaScript中的全域物件

瀏覽器給JS引擎的主要全域物件是window物件,代表頁面所屬的瀏覽器視窗。
window是一個特殊的全域物件,可以透過它還存取所有其他的全域物件、全域變數及瀏覽器API。
window最重要的屬性是document,也就是目前頁面的DOM結構
JS可以任意改變頁面上的DOM結構。

1
var first = document.getElementById('first')

使用document全域物件從DOM結構找出ID為first的元素

不同類型的JavaScript

分為全域程式(Global code)函式程式(function code)
兩者主要的區別是它們所在的位置。

1
2
3
4
5
6
7
8
9
10
11
12
<script>
//函式程式
function addMessage(element, message){
var messageElement = document.createElement("li");
messageElement.textContent = message;
element.appendChild(messageElement);
}

//全域程式
var first = document.getElementById("first");
addMessage(first, "Page loading");
</script>

全域程式會被JS引擎自動的逐行直接執行。
函式程式的執行必須由其他程式如全域程式、其他函式或由瀏覽器呼叫。

在頁面建立階段執行JavaScript程式碼

瀏覽器在頁面建立階段遇到script節點時會暫停使用HTML代碼來建立DOM結構,並開始執行JS程式碼。

JS可以任意的修改DOM結構:建立新節點、修改或刪除現有節點,但不能修改尚未建立的元素

人們傾向把script放在頁面底部的原因之一,不必擔心特定的HTML元素有沒有被讀到

當執行完script中的最後一行code,瀏覽器並離開JS執行模式,並繼續處理HTML代碼來建立其他的DOM節點,並反覆的上述行為。

此時已執行過的script元素中,由使用者定義的全域變數能繼續被其他的script中元素的JS所存取ㄝ,因為存放了所有全域變數的window的全域物件,在頁面的整個生命週期內都是有效且可以存取的。

當瀏覽器處理完所有的HTML元素,頁面建立階段完成,並進入生命週期第二部分事件處理

事件處理

在頁面建立階段,JS除了影響全域狀態及修改DOM結構外,還可以註冊事件監聽器(處理器),即在事件發生時要執行的函式。

事件處理概述

瀏覽器執行環境的核心為一次執行一段程式碼,為了避免大量阻塞,瀏覽器需要一種方法來追蹤已發生但尚未處理的事件

所有已發生事件都會按照順序放置在同一個事件佇列中,交由瀏覽器檢測。

  • 瀏覽器檢查事件佇列的頂部
  • 如果沒有事件,瀏覽器會持續檢查
  • 如果有一事件,瀏覽器會存取他,並執行處理程式。執行期間,剩餘待處理事件間耐心等待直到被處理

避免編寫需要大量執行時間的事件處理程式,會導致WEB無法產生回應

將事件放入事件佇列的瀏覽器工作機制,是在頁面建立和事件處理階段之外。判斷事件何時發生,並放入事件佇列的這些動作並不在處理事件的執行緒中

事件是非同步的

無法預測事件的發生的順序,因此事件的處理、對事件處理的函式呼叫是非同步的。

可能會發生的事件

  • 瀏覽器事件,如頁面處於以載入會未載入的狀態
  • 網路事件,如來自伺服器的回應Ajax事件、伺服器端事件)
  • 使用者事件,如點擊滑鼠、移動滑鼠、按下鍵盤
  • 計時器事件,如等候逾時或間隔性的觸發條件

程式碼是在事前完成設置,以便在之後執行。
除了全域程式,在頁面上大多的程式都是某些事件的結果而被執行。

註冊事件處置器

Event-handler registration, 事件處置器是在特定的事件時發生執行的函式。
有兩種註冊事件方式:

  • 將函式指定給特殊屬性
  • 使用內建的addEventListener方法
1
2
3
4
5
window.onload = function(){};
//針對load事件註冊一個事件處理器

document.body.onclick = function(){};
//對document主體的click事件註冊一個事件處理器

不建議使用以上方式,缺點是某一特定事件只能指派一個事件處理器,很容易蓋掉之前的事件處理函式。

一個好的替代方式addEventListener

1
2
3
4
5
6
7
8
9
10
11
<script>
document.body.addEventListener("mousemove", function() {
var second = document.getElementById("second");
addMessage(second, "Event: mousemove");
});

document.body.addEventListener("click", function(){
var second = document.getElementById("second");
addMessage(second, "Event: click");
});
</script>

處理事件

事件處理的主要概念適當事件發生時,瀏覽器呼叫相關的事件處理器。

模擬情境

  1. 當頁面建置完成使用者開始移動滑鼠即點擊滑鼠
  2. 瀏覽器依順序加入將mousemove和click事件放置到事件佇列上
  3. 事件處理階段:佇列頂端有一個mousemove事件,並執行相關事件處理器,此時click事件等待中
  4. mousemove最後一行執行完畢,並且JS引擎退出該事件函式,mousemove事件處理完成
  5. 事件迴圈再次檢查佇列,找到click事件並執行
  6. (重複動作直到使用者關閉Web)

總結

  • 瀏覽器接收HTML代碼作為建立DOM結構的藍圖,而DOM就是客戶端WEB的的結構表現
  • 我們透過Javascript程式動態的修改DOM結構,以便為WEB展現動態行為
  • 客戶端的WEB執行分為兩個階段:
    • 頁面建立:處理HTML以建立DOM結構,遇到<script>執行全域程式,執行時可修改DOM結構,及註冊事件處理器(addEventListener)
    • 事件處理:按照事件產生的順序逐一執行,仰賴事件佇列,事件按照他們的發生的順序被儲存起來。事件回圈會一直檢查事件佇列是否有事件,如果有,呼叫執行函式

名詞解釋

  • DOM(Document Object Model)文件物件模型:
    客戶端Web使用者介面的結構化表示,只少一開始,他是由Web中的HTML代碼所構成
  • 事件:
    絕大多數的JS代碼都是事件驅動的應用程式,意味著程式碼都是為了回應特定事件而執行,事件包含網路事件、計時器、使用者事件
  • 瀏覽器API:
    瀏覽器提供了一組API,允許我們存取設備資訊、在本地端存取資料或與遠端伺服器通訊