页面生命周期事件:DOMContentLoaded、load、beforeunload、unload

理解 HTML 页面的四个关键生命周期事件及其使用场景

问题

如何理解页面生命周期中的 DOMContentLoaded、load、beforeunload、unload 这四个事件?

解答

四个事件的触发时机

DOMContentLoaded

浏览器已完全加载 HTML 并构建了 DOM 树,但外部资源(如图片、样式表)可能尚未加载完成。此时可以查找 DOM 节点并初始化接口。

document.addEventListener('DOMContentLoaded', () => {
  console.log('DOM 已就绪');
  // 可以安全地操作 DOM 元素
  const element = document.querySelector('#app');
});

load

浏览器不仅加载完成了 HTML,还加载完成了所有外部资源(图片、样式等)。此时样式已被应用,图片大小也已知。

window.addEventListener('load', () => {
  console.log('页面和所有资源已加载完成');
  // 可以获取图片尺寸等信息
  const img = document.querySelector('img');
  console.log(img.offsetWidth);
});

beforeunload

用户正在离开页面时触发。可以检查用户是否保存了更改,并询问是否真的要离开。

window.addEventListener('beforeunload', (event) => {
  // 取消事件会提示用户确认
  event.preventDefault();
  event.returnValue = ''; // Chrome 需要设置 returnValue
});

unload

用户最终离开页面时触发。只能执行不涉及延迟的简单操作,如发送统计数据。

window.addEventListener('unload', () => {
  // 发送分析数据
  navigator.sendBeacon('/analytics', JSON.stringify({
    page: location.href,
    time: Date.now()
  }));
});

DOMContentLoaded 与脚本的关系

普通的 <script> 标签会阻塞 DOMContentLoaded,浏览器必须等待脚本执行完成才会触发该事件。

<script>
  // 这个脚本会阻塞 DOMContentLoaded
  console.log('script hil2s');
</script>

例外情况:

  1. async 属性的脚本不会阻塞 DOMContentLoaded
<script async src="script.js"></script>
  1. 动态创建的脚本不会阻塞 DOMContentLoaded
const script = document.createElement('script');
script.src = 'script.js';
document.body.append(script);

DOMContentLoaded 与样式的关系

外部样式表本身不会阻塞 DOMContentLoaded,但如果样式后面有脚本,脚本必须等待样式加载完成(因为脚本可能需要获取样式相关的属性)。此时 DOMContentLoaded 会等待脚本,间接也在等待样式。

<link rel="stylesheet" href="style.css">
<script>
  // 这个脚本会等待 style.css 加载完成
  const element = document.querySelector('.box');
  console.log(getComputedStyle(element).width);
</script>

使用 sendBeacon 发送数据

在 unload 事件中使用 navigator.sendBeacon() 可以在后台发送数据,不会延迟页面跳转。

window.addEventListener('unload', () => {
  const data = {
    userId: 123,
    action: 'page_leave',
    timestamp: Date.now()
  };
  
  navigator.sendBeacon('/api/analytics', JSON.stringify(data));
});

关键点

  • DOMContentLoaded 在 DOM 树构建完成后触发,此时可以操作 DOM,但外部资源可能未加载完成
  • load 在所有资源(包括图片、样式)加载完成后触发,通常无需等待这么久
  • 普通 script 标签会阻塞 DOMContentLoaded,但 async 脚本和动态创建的脚本不会
  • beforeunload 用于在用户离开前确认,unload 用于发送统计数据(推荐使用 navigator.sendBeacon)
  • 样式表后的脚本会等待样式加载,间接导致 DOMContentLoaded 也等待样式