likes
comments
collection
share

【面试秘籍】掌握浏览器渲染机制:面试官眼中的加分项

作者站长头像
站长
· 阅读数 48

前言

互联网时代,我们每天都面对各种各样的网页也浏览了各种各样的网页。但是大家有没有想过,当点击一个网页时,要经过哪些过程一个网页才能显示在我们浏览器中。

下面将通过几道面试题来和大家一起聊聊浏览器的渲染过程:

  1. 为什么JS是单线程的?
  2. 为什么JS会阻塞页面的加载?
  3. 聊聊浏览器的回流与重绘

浏览器的渲染进程

以Chrome为例,它由多个进程构成,本文主要聊聊浏览器的渲染过程中作用最大的渲染进程,渲染进程也即浏览器的内核,浏览器的渲染进程在页面的渲染过程中主要由以下两个线程起到关键性的作用:

  1. JS引擎线程
  2. GUI渲染线程

1. JS 引擎线程

  • JavaScript 引擎,如 Google 的 V8,担当着脚本编译与执行的重任。
  • JS 引擎线程负责解析 Javascript 脚本,运行代码。
  • 在每个浏览器标签页的渲染进程中,JavaScript 引擎通过其单一的执行线程来解析并运行脚本,确保代码按序执行,也就是说无论在什么时候都只有一个JS线程在运行JS程序。
  • 值得注意的是,JS 引擎线程与用户界面(UI)的渲染线程之间是互斥的。

2. GUI 渲染线程

  • 解析 HTML,CSS,构建 DOM 树和 CSSOM 树组成render树,实现布局和绘制。
  • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
  • 但是GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个渲染队列中等到 JS 引擎空闲时立即被执行。

浏览器的渲染过程

当我们在页面中输入一个url地址后,浏览器会经历以下几个过程;

  1. 解析数据包得到html,css文件,也就是将得到的二进制数据包解析成字符串的过程
  2. 将解析出来的字符串标记Token生成Node节点,构建DOM树
  3. 解析css文件得到CSSOM树
  4. DOM 树 + CSSOM 树 ==》 render树
  5. 计算布局,也即一个回流的过程
  6. GPU渲染该布局,也即一个重绘的过程

有了以上基础,我们来回答一下文章开头提出的三个问题。

为什么JS是单线程的?

让我们用一种更易于理解的方式来探讨为什么 JavaScript 选择成为一门单线程的语言:

想象一下 JavaScript 就像一个舞台上的独奏演员,而不是一个交响乐团。这是因为 JavaScript 最初的设计目标是在网页上创造生动的用户体验,涉及到对文档对象模型(DOM)的精细控制,以及与用户的即时互动。如果 JavaScript 使用多线程,就像是多个演员同时试图调整舞台布景一样,可能会出现混乱。

具体来说,多线程操作 DOM(网页的主要结构)可能导致不同线程之间的冲突,比如一个线程正在修改一个元素,而另一个线程想要删除它。这就如同两个人同时试图移动同一张桌子,一个想把它推到左边,另一个却想把它拉到右边,结果可能就是谁也动不了桌子,或者桌子被损坏。

虽然可以通过复杂的锁定机制来避免这种冲突,但这会增加编程的复杂度和开销,可能会导致性能问题。为了简化问题,保持一致性和可预测性,JavaScript 从一开始就决定采用单线程执行模式。这样一来,任何时刻只有一个“演员”(即 JavaScript 的执行线程)在舞台上表演,确保了对 DOM 的安全和顺序的访问,从而避免了多线程可能引发的竞态条件和同步问题。

这种设计决策使得 JavaScript 成为了一种适合快速响应用户事件和更新界面的脚本语言,同时也降低了开发者需要处理的潜在并发复杂性。

为什么JS会阻塞页面的加载?

想象一下,你正在参加一场现场直播的魔术表演,而浏览器是你的舞台。在这个舞台上,有两个关键角色:一个是魔术师(JavaScript),负责执行各种令人惊叹的表演(如动态改变页面内容);另一个是舞台管理员(GUI 渲染线程),他的职责是确保观众(用户)看到的每一幕都是完美无瑕的。

在表演过程中,魔术师需要对舞台布景(DOM)进行调整,例如移动道具或变换背景。如果魔术师和舞台管理员同时工作,可能会出现混乱——比如,魔术师正要揭开一个神秘盒子的盖子,而舞台管理员却在同时重新摆放舞台上的家具。这种情况下,观众可能会看到杂乱无章的场景,破坏了整个表演的流畅性和视觉效果。

为了避免这种情况,魔术师和舞台管理员之间有一个默契的规则:当魔术师在舞台上表演时,舞台管理员必须暂停一切后台工作,静静地等待魔术师结束表演。只有当魔术师退场后,舞台管理员才能上台整理舞台,确保下一幕顺利进行。

同样,在浏览器中,当 JavaScript 正在执行某个脚本时,GUI 渲染线程会被暂时停止,所有与界面相关的更新都会被暂时存放在一边。一旦 JavaScript 完成了它的“表演”,GUI 渲染线程就会立即上场,将所有等待的更新一次性应用到屏幕上。

然而,如果魔术师的表演(JavaScript 执行)过于冗长,观众(用户)就会感受到等待的焦虑,就像在观看直播时遇到的延迟。因此,如果 JavaScript 脚本执行时间过长,就会导致页面渲染的延迟,让用户感到页面加载阻塞或响应缓慢。

聊聊浏览器的回流与重绘

回流必将引起重绘,重绘不一定会引起回流。

回流

当 Render 树 中元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。

常见的导致回流的操作:

  1. 页面初次渲染
  2. 增加、删除可见的DOM元素
  3. 改变元素的几何信息
  4. 窗口大小改变
  5. 改变字体大小会回流

重绘

非几何信息被修改时则会发生重绘,当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

性能优化

但是如果每发生一次触发回流与重绘的操作就去重新渲染页面这势必会造成浏览器的性能低下,为了解决这个问题,在新版的浏览器中新增了一个渲染队列。

现代浏览器会对频繁的回流或重绘操作进行优化:浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。

常见的强制渲染队列刷新的一些属性:

  1. offsetTop offsetLeft offsetWidth offsetHeight (读取得到边框)
  2. clientTop clientLeft clientWidth clientHeight (读取不到边框)
  3. scorollTop(距离屏幕顶部的距离) scrollLeft scrollWidth scrollHeight

当js引擎在读取到这些属性时,会强制清空渲染队列,进行一次回流操作,这样一来可大大提高浏览器的工作效率与性能。

本篇文章就到此为止啦,希望通过这篇文章能对你理解浏览器的渲染机制有所帮助,本人水平有限难免会有纰漏,欢迎大家指正。如觉得这篇文章对你有帮助的话,欢迎点赞收藏加关注,感谢支持🌹🌹。

【面试秘籍】掌握浏览器渲染机制:面试官眼中的加分项

转载自:https://juejin.cn/post/7394643241851109403
评论
请登录