浏览器渲染原理与事件循环机制

102 阅读3分钟

前言

依旧是复习一些高频的问题,结合自己最新的一些理解做一个复盘,这次复习的是浏览器渲染原理事件循环机制

浏览器渲染原理

说到浏览器的渲染原理就必须说到浏览器的线程,因为浏览器的渲染是由渲染主线程决定的。我们都知道浏览器是一个多进程多线程的的应用程序,在浏览器启动时会自动开启多个进程,包括浏览器进程网络进程渲染进程等。当渲染进程启动后会自动开启一个渲染主线程,负责执行HtmlCssJs代码,实现渲染。

所以了解浏览器的渲染原理就必须了解渲染主线程是如何工作的,渲染主线程会做很多事,我们比较熟悉的就是解析Html,生成Dom树、解析Css,生成Cssom树、计算样式、完成布局、处理图层、每秒画页面60次、执行全局Js、执行事件处理函数、执行计时器的回调等等。那么在执行这些任务的时候,由于JS是一门单线程的语言,且运行在渲染主线程中,如果一个个执行上述的任务,即采用同步的方式,就可能会使主线程产生阻塞现象。即渲染主线程可能会白白等待浪费时间,比如说执行setTimeout的回调;或者比如说在进行密集型操作时,会对用户造成卡死的现象(当然这种情况可以使用Worker开辟多线程,但这里只是揭示阻塞产生的原因)。所以在浏览器中采用异步+消息队列的方式最大限度地避免阻塞的产生。

在遇到一些不会立即执行的任务,如:

  • 计时完成后需要执行的任务,如setTimeoutsetInterval
  • 网络通信后需要执行的任务,如XHR,Fetch
  • 用户操作后需要执行的任务,如addEventListener

主线线程将这些任务交给其他线程处理,自身立刻结束任务的执行,继续执行后续代码。当其他线程完成后,将这些主线程传递过来的回调函数包装成一个个任务,加入到消息队列的末尾进行排队等待一个个处理。写一个demo理解一下这个行为。

截屏2024-05-27 20.15.31.png

实际效果是点击按钮后3秒延迟后才改变h1内容,虽然delay函数写在h1.textContent = 'click'后面,但实际上浏览器在执行全局JS代码后,将交互事件交给了交互线程,随后结束自身任务。当用户执行点击事件后,交互线程把监听的点击事件fn加入到消息队列给主线程执行,执行函数中的两条语句。但是在页面上确实是延迟3秒之后才看到文本变化,原因是js语句确实已经执行了,但是要在页面上看到还需要浏览器重新渲染,重新生成Dom树,这一步是在fn执行之后的,因此才会出现这样的效果。(待更新,回流+重绘

事件循环

结合浏览器的渲染原理就可以很好理解事件循环了,简单来说:

  • 在开始的时候渲染主线程会进入无限循环
  • 每次循环的时候都要检查消息队列中是否有任务需要执行,如果有就取出任务执行,任务执行完之后结束循环开启下一个循环,如果没有就进入休眠状态。
  • 其他线程可以随时向消息队列添加任务,新任务会添加到消息队列的末尾,按照先进先出的特性(暂不考虑任务的优先级)。

这就是事件循环(eventloop),因为它与消息队列高度相关,因此有的地方也叫消息循环(messageloop)

事件循环的优先级(待更新)