用户输入 URL,浏览器收到服务器返回的数据后,一个网页应用开始了它的生命周期,这里面分为两个阶段:

  1. 页面构建
  2. 事件处理

页面构建

浏览器用从服务器收到的数据(通常是 HTML,CSS,JavaScript)构建页面 UI,构建分为两步:

  1. 解析 HTML 构建 DOM;
    • 浏览器逐个解析 HTML 代码中的 HTML 元素。
    • 浏览器在处理 HTML 代码时进行的是构建 DOM 的操作,DOM 基于 HTML 代码构建,但两者并不完全一样(可以把 HTML 代码理解成构建 DOM 的蓝图),浏览器会静默地修正 HTML 中存在的错误。
    • 每当遇到 <script> 元素时,浏览器就会暂停 DOM 的构建,转去执行 JavaScript 代码。
  2. 执行 JavaScript 代码。
    • 所有包含在 <script> 中的代码都由浏览器的 JavaScript 引擎执行。
    • 可以将 JavaScript 代码粗略地分为全局代码函数代码
      • 函数内部的代码就是函数代码,函数之外的代码就是全局代码。
      • 全局代码会自动被 JavaScript 引擎逐行执行,简单直接。
      • 函数代码则必须被其它地方调用才会执行。
  • 页面构建的过程中,这两步会反复交替进行。

浏览器通过全局对象为 JavaScript 引擎提供与页面交互的 API,其中最主要的是 window 对象,它表示包含当前页面的窗口,其它所有全局对象和全局变量、浏览器的 API 都通过 window 来访问。

  • document 对象是 window 中最重要的属性之一,它表示当前页面的 DOM;通过 document,JavaScript 可以对 DOM 进行各种操作。
  • JavaScript 不能操作当前不在 DOM 中的元素,这是 <script> 通常放在页面底部的原因之一。
  • JavaScript 代码的全局状态在页面构建阶段的两个步骤的反复切换过程中会被保存下来,因为保存全局变量的 window 对象在页面的整个生命周期都能被访问到

当所有 HTML 代码都处理好后,页面构建阶段完成,转入事件处理阶段。

事件处理

网页应用是一种 GUI 应用,它可以响应各种事件,为此 JavaScript 需要注册事件处理器,即某种事件发生时供浏览器调用去处理该事件的函数。

浏览器使用的是单线程的执行模型,同一时刻只能执行一段代码。

事件按照顺序逐个被处理,当前事件的事件处理器执行完成后,下一个事件才能被处理。

为应对上一个事件没处理完就有新的事件被触发的情况,浏览器用事件队列来记录事件。

一个耗时的事件处理器会导致网页失去响应,使用户体验变差。

浏览器检测事件发生并将事件加入到事件队列的线程独立于事件处理的线程。

主要的事件类别:

  1. 浏览器事件
    • DOMContentLoadedunload
  2. 网络事件
    • 如 Ajax 事件,服务端事件
  3. 用户事件
  4. 定时器事件

事件处理器的注册方法:

  1. 将函数赋给特定的属性(不推荐);

    • 一个事件只能注册一个事件处理器,很容易覆盖掉注册在前的事件处理函数。
  2. 使用内置的 addEventListener 方法。

    • 可以为一个事件注册任意多个事件处理器。
    window.onload = function() {}
    document.body.onclick = function() {}
    
    document.body.addEventListener("mousemove", function() {})
    document.body.addEventListener("click", function() {})