跳至主要內容

文档、资源加载

Yang大约 4 分钟

网页生命周期

DOMContentLoaded

浏览器已完全加载 HTML,并构建了 DOM 树,但像 <img> 和样式表之类的外部资源可能尚未加载完成

  • DOM 已经就绪,因此处理程序可以查找 DOM 节点,并初始化接口

  • <script>...<script><script src="..."><script> 之类的脚本会阻塞 DOMContentLoaded,浏览器将等待它们执行结束

  • 不会阻塞 DOMContentLoaded 的脚本

    • 具有 async 特性(attribute)的脚本不会阻塞 DOMContentLoaded
    • 使用 document.createElement('script') 动态生成并添加到网页的脚本也不会阻塞 DOMContentLoade
document.addEventListener('DOMContentLoaded', function(){
  console.log('DOMContentLoaded')
})

load

浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等

  • 外部资源已加载完成,样式已被应用,图片大小也已知
window.onload = function(){
  console.log('loaded')
}

beforeunload

触发了离开页面的导航(navigation)或试图关闭窗口,beforeunload 处理程序将要求进行更多确认

  • 可以检查用户是否保存了更改,并询问他是否真的要离开
window.onbeforeunload = function() {
  return false;
}

window.addEventListener("beforeunload", (event) => {
  // 起作用,与在 window.onbeforeunload 中 return 值的效果是一样的
  event.returnValue = "有未保存的值。确认要离开吗?";
})

unload

用户几乎已经离开

  • 以启动一些操作,例如发送统计数据(可以使用 navigator.sendBeacon 来发送网络请求)
let analyticsData = { /* 带有收集的数据的对象 */ };

window.addEventListener("unload", function() {
  navigator.sendBeacon("/analytics", JSON.stringify(analyticsData));
})

加载状态

readyState

document.readyState:当前加载状态信息,值情况如下:

  • loading:文档正在被加载
  • interactive:文档已被解析完成,与 DOMContentLoaded 几乎同时发生,但是在 DOMContentLoaded 之前发生
  • complete:文档和资源均已加载完成,与 window.onload 几乎同时发生,但是在 window.onload 之前发生

readystatechange

在加载状态发生改变时触发

  • DOMContentLoaded 之前,document.readyState 会立即变成 interactive,所以在加载状态改变监听中打印不出 loading
document.addEventListener('readystatechange', function() {
  console.log('readyState:' + document.readyState)
})

脚本

  • 当浏览器加载 HTML 时遇到 <script>...</script> 标签,浏览器就不能继续构建 DOM。它必须立刻执行此脚本。对于外部脚本 <script src="..."></script> 也是一样的:浏览器必须等脚本下载完,并执行结束,之后才能继续处理剩余的页面
    • 脚本不能访问到位于它们下面的 DOM 元素,因此,脚本无法给它们添加处理程序等
    • 如果页面顶部有一个笨重的脚本,它会“阻塞页面”。在该脚本下载并执行结束前,用户都不能看到页面内容,可以把脚本放在页面底部。此时,它可以访问到它上面的元素,并且不会阻塞页面显示内容

defer

告诉浏览器不要等待脚本

浏览器将继续处理 HTML,构建 DOM

脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行、

  • 仅适用于外部脚本,如果 <script> 脚本没有 src 属性,则会忽略 defer 属性

  • 页面内容不受阻塞,会立即显示

  • DOMContentLoaded 事件处理程序在具有 defer 特性的脚本下载且执行结束后才会被触发

  • 具有 defer 特性的脚本保持其相对顺序,就像常规脚本一样

    • 多个 defer 脚本时,同时下载,但是按照相对顺序执行

async

defer 有些类似。它也能够让脚本不阻塞页面。但是,在行为上二者有着重要的区别

async 特性意味着脚本是 完全独立

  • 仅适用于外部脚本,如果 <script> 脚本没有 src 属性,则会忽略 defer 属性

  • 浏览器不会因 async 脚本而阻塞(与 defer 类似)

  • 其他脚本不会等待 async 脚本加载完成,同样,async 脚本也不会等待其他脚本

  • DOMContentLoaded 和异步脚本不会彼此等待

动态脚本

使用 JavaScript 动态地创建一个脚本,并将其附加到文档中

  • 当脚本被附加到文档时,脚本就会立即开始加载
  • 默认情况下,动态脚本的行为是 异步
    • 不会等待任何东西,也没有什么东西会等它们
    • 先加载完成的脚本先执行
    • 如果显式地设置了 script.async = false,脚本将按照脚本在文档中的顺序执行,就像 defer 那样
let script = document.createElement('script');
script.src = "test.js";
document.body.append(script);

资源加载

浏览器允许我们跟踪外部资源的加载,如脚本、iframe、图片等,基本上适用于具有外部 src 的任何资源

事件

  • onload:资源加载成功
  • onerror:资源加载出现错误
    • 无法获取更多 HTTP error 的详细信息,不知道 error 原因,只知道是加载失败了
    • 在脚本处理和执行期间可能发生的 error 超出了这些事件跟踪的范围
      • 也就是说:如果脚本成功加载,则即使脚本中有编程 error,也会触发 onload 事件
      • 如果要跟踪脚本 error,可以使用 window.onerror 全局处理程序

注意

  • 大多数资源在被添加到文档中后便开始加载,但是 <img> 要等到获得 src 后才开始加载

  • <iframe> 加载完成时会触发 iframe.onload 事件,无论是成功加载还是出现错误

上次编辑于:
贡献者: sunzhenyang