一文搞清楚浏览器js事件循环和渲染事件

在浏览器环境中,JavaScript 事件循环(Event Loop)渲染事件 的执行过程是 Web 性能优化和流畅交互的重要基础。以下是详细的执行流程,包括 同步任务、微任务、宏任务、渲染机制 等所有可能的节点。


📌 1. 事件循环(Event Loop)整体执行流程

浏览器的 JavaScript 运行环境基于 事件循环(Event Loop) ,其执行逻辑如下:

  1. 执行同步代码(Script)

    • 代码最先进入 调用栈(Call Stack) ,按照顺序执行 同步任务(Synchronous Tasks)。
  2. 执行微任务(Microtasks)

    • 运行所有 微任务(Microtasks) ,包括:

      • Promise.then/catch/finally
      • MutationObserver
      • queueMicrotask()
  3. 执行宏任务(Macrotasks)

    • 运行 一个 宏任务(Macrotask),包括:

      • setTimeout
      • setInterval
      • setImmediate(仅 Node.js)
      • requestAnimationFrame
      • I/O 操作
      • UI 渲染
      • MessageChannel
  4. 渲染更新(仅浏览器)

    • 如果 JavaScript 执行结束,浏览器会检查是否需要更新 UI,并执行 下一帧的渲染(Frame Rendering)
  5. 重复以上过程

    • 事件循环(Event Loop)继续重复上述流程。

📌 2. 浏览器的执行步骤细节

浏览器的事件循环不仅仅处理 JavaScript 代码,还涉及 渲染、UI 更新,主要执行顺序如下:

🔹 1. 解析 HTML & 构建 DOM

  • 浏览器解析 HTML ,将其转换为 DOM 树(DOM Tree)

🔹 2. 解析 CSS & 构建 CSSOM

  • CSS 解析器 解析 linkstyle 内嵌 CSS,构建 CSSOM(CSS 对象模型)

🔹 3. 运行 JavaScript

  • 执行 JavaScript 代码

    • 运行 同步代码(同步任务) ,如变量赋值、函数调用等。
    • 当遇到 异步任务 (如 setTimeoutPromise),它们会被放入相应的 任务队列(Task Queue) ,等待执行。

🔹 4. 处理微任务(Microtasks)

  • 微任务优先级高于宏任务 ,当同步代码执行完成后,立即执行所有 微任务

    • 例如 Promise.then()MutationObserverqueueMicrotask()

🔹 5. 处理宏任务(Macrotasks)

  • 事件循环每次从宏任务队列取出 一个 任务执行,包括:

    • setTimeout
    • setInterval
    • requestAnimationFrame
    • I/O 任务
    • MessageChannel

🔹 6. 计算布局(Recalculate Style & Layout)

  • 如果 DOM 或 CSS 发生变化 ,浏览器会 重新计算元素的样式和布局

    • Recalculate Style(重新计算样式)
    • Layout(布局计算,确定元素的位置和大小)

🔹 7. 处理合成(Paint & Composite)

  • Paint(绘制) :将像素绘制到屏幕上。
  • Composite(合成) :合成多个图层,显示最终画面。

📌 3. 详细的事件执行顺序(实例解析)

我们来看一个具体的案例:

javascript 复制代码
console.log("同步代码开始");  // 1️⃣

setTimeout(() => {
  console.log("setTimeout 宏任务");  // 6️⃣
}, 0);

Promise.resolve().then(() => {
  console.log("Promise 微任务");  // 3️⃣
});

console.log("同步代码结束");  // 2️⃣

📝 执行顺序解析

步骤 任务类型 代码 说明
1️⃣ 同步任务 console.log("同步代码开始") 直接执行
2️⃣ 同步任务 console.log("同步代码结束") 直接执行
3️⃣ 微任务 Promise.then() 微任务放入微任务队列,立即执行
4️⃣ 检查是否有剩余微任务 微任务队列为空,继续
5️⃣ 宏任务 setTimeout 执行定时器回调
6️⃣ 日志输出 "setTimeout 宏任务"

最终 控制台输出顺序

javascript 复制代码
同步代码开始
同步代码结束
Promise 微任务
setTimeout 宏任务

📌 4. requestAnimationFrame(帧渲染机制)

requestAnimationFrame() 是专门用于优化动画的 宏任务,它的特点是:

  1. 在浏览器下一帧渲染前执行(通常 16.67ms 一次)。
  2. setTimeout(fn, 16) 更精准,且不会阻塞 UI 渲染

示例:

scss 复制代码
function renderFrame() {
  console.log("下一帧渲染");
  requestAnimationFrame(renderFrame);
}

requestAnimationFrame(renderFrame);

执行时机

  • 浏览器刷新时触发(即下一帧到来前)。
  • 如果页面切换到后台,动画会暂停,节省 CPU 资源

📌 5. setTimeout vs requestAnimationFrame

机制 适用场景 运行频率 是否阻塞渲染
setTimeout(fn, 16) 粗略的定时器 约 16ms 一次 可能阻塞渲染
requestAnimationFrame(fn) 平滑动画 下一帧 渲染前 不阻塞,推荐

🚀 建议: 使用 requestAnimationFrame() 来实现动画,而不是 setTimeout(fn, 16),能提供 更流畅的动画效果


📌 6. 总结

🔹 事件循环的完整顺序

  1. 执行 同步任务(script)。
  2. 执行 所有微任务(Promise、MutationObserver)。
  3. 渲染(如果需要)。
  4. 执行 一个 宏任务(setTimeout, setInterval)。
  5. 重复循环

🔹 渲染过程

  1. JS 执行完毕
  2. 计算样式(Recalculate Style)
  3. 计算布局(Layout)
  4. 绘制(Paint)
  5. 合成(Composite)
  6. 显示画面

📢 优化建议

  • 使用 requestAnimationFrame() 优化动画
  • 减少 DOM 操作,避免 Repaint & Reflow
  • 合理使用 setTimeout,避免阻塞渲染

这样,你就完整掌握了浏览器 JS 事件循环渲染机制 的详细流程了!🚀

相关推荐
晓夜残歌2 小时前
安全基线-rm命令防护
运维·服务器·前端·chrome·安全·ubuntu
inxunoffice3 小时前
批量删除 PPT 空白幻灯片页面
前端·powerpoint
Setsuna_F_Seiei5 小时前
前端切图仔的一次不务正业游戏开发之旅
前端·游戏·cocos creator
laimaxgg5 小时前
Qt窗口控件之颜色对话框QColorDialog
开发语言·前端·c++·qt·命令模式·qt6.3
爱编程的鱼5 小时前
Unity—从入门到精通(第一天)
前端·unity·ue5·游戏引擎
默默无闻 静静学习5 小时前
sass介绍
前端·sass
大怪v6 小时前
前端佬们,装起来!给设计模式【祛魅】
前端·javascript·设计模式
vvilkim6 小时前
Vue.js 插槽(Slot)详解:让组件更灵活、更强大
前端·javascript·vue.js
学无止境鸭6 小时前
uniapp报错 Right-hand side of ‘instanceof‘ is not an object
前端·javascript·uni-app
豆豆(设计前端)6 小时前
一键秒连WiFi智能设备,uni-app全栈式物联开发指南。
前端