当你构建 Web 应用程序时,你不只是编写单独运行的 JavaScript 代码,你编写的 JavaScript 正在与环境进行交互。了解这种环境,它的工作原理以及它的组,这些有助于你够构建更好的应用程序,并为应用程序发布后可能出现的潜在问题做好充分准备。
浏览器的主要组件包括:
在这篇文章中,将重点讨论渲染引擎,因为它处理 HTML 和 CSS 的解析和可视化,这是大多数 JavaScript 应用程序经常与之交互的东西。
渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。
渲染引擎可以显示 HTML 和 XML 文档和图像。如果使用其他插件,渲染引擎还可以显示不同类型的文档,如 PDF。
与 JavaScript 引擎类似,不同的浏览器也使用不同的渲染引擎。以下是一些最受欢迎的:
Firefox、Chrome 和 Safari 是基于两种渲染引擎构建的,Firefox 使用 Geoko——Mozilla 自主研发的渲染引擎,Safari 和 Chrome 都使用 Webkit。Blink 是 Chrome 基于 WebKit的自主渲染引擎。
渲染引擎从网络层接收所请求文档的内容。
解析 HTML 以构建 Dom 树 -> 构建 Render 树 -> 布局 Render 树 -> 绘制 Render 树
渲染现引擎的第一步是解析 HTML文档,并将解析后的元素转换为 DOM 树 中的实际 DOM 节点。
假如有如下 Html 结构
<html> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="theme.css"> </head> <body> <p> Hello, <span> friend! </span> </p> <div> <img src="smiley.gif" alt="Smiley face" height="42" width="42"> </div> </body> </html>
对应的 DOM树如下:
基本上,每个元素都表示为所有元素的父节点,这些元素直接包含在元素中。
CSSOM 指的是 CSS 对象模型 。 当浏览器构建页面的 DOM 时,它在 head
标签下如遇到了一个 link
标记且引用了外部 theme.css CSS 样式表。 浏览器预计可能需要该资源来呈现页面,它会立即发送请求。 假设 theme.css
文件内容如下:
body { font-size: 16px; } p { font-weight: bold; } span { color: red; } p span { display: none; } img { float: right; }
与 HTML一样,渲染引擎需要将 CSS 转换成浏览器可以使用的东西—— CSSOM。CSSOM 结构如下:
你想知道为什么 CSSOM 是一个树形结构? 在为页面上的任何对象计算最终样式集时,浏览器以适用于该节点的最常规规则开始(例如,如果它是 body
元素的子元素,则应用所有 body
样式),然后递归地细化,通过应用更具体的规则来计算样式。
来看看具体的例子。包含在 body
元素内的 span
标签中的任何文本的字体大小均为 16
像素,并且为红色。这些样式是从 body
元素继承而来的。 如果一个 span
元素是一个 p
元素的子元素,那么它的内容就不会被显示,因为它被应用了更具体的样式(display: none)。
另请注意,上面的树不是完整的 CSSOM 树,只显示我们决定在样式表中覆盖的样式。 每个浏览器都提供一组默认样式,也称为 “user agent stylesheet” 。这是我们在未明确指定任何样式时看到的样式,我们的样式会覆盖这些默认值。
不同浏览器对于相同元素的默认样式并不一致,这也是为什么我们在 CSS 的最开始要写 *{padding:0;marging:0};
,也就是我们要重置CSS默认样式的。
CSSOM 树和 DOM 树连接在一起形成一个 render tree,渲染树用来计算可见元素的布局并且作为将像素渲染到屏幕上的过程的输入。
渲染树中的每个节点在 Webkit 中称为渲染器或渲染对象。
收下是上面 DOM 和 CSSOM 树的渲染器树的样子:
为了构建渲染树,浏览器大致执行以下操作:
display:none
“visibility:hidden” 和 “display:none” 之间的不同, “visibility:hidden” 将元素设置为不可见,但是同样在布局上占领一定空间(例如,它会被渲染成为空盒子),但是 “display:none” 的元素是将节点从整个 render tree
中移除,所以不是布局中的一部分 。
https://github.com/WebKit/web...
我们来看看这个类的一些核心内容:
每个渲染器代表一个矩形区域,通常对应于一个节点的 CSS 盒模型。它包含几何信息,例如宽度、高度和位置。
创建渲染器并将其添加到树中时,它没有位置和大小,计算这些值称为布局。
HTML使用基于流的布局模型,这意味着大多数时间它可以一次性计算几何图形。坐标系统相对于根渲染器,使用左上原点坐标。
布局是一个递归过程 - 它从根渲染器开始,它对应于 HTML 文档的 <html>
元素。 布局以递归方式继续通过部件或整个渲染器层次结构,为每个需要它的渲染器计算几何信息。
根渲染器的位置为 0,0
,其尺寸与浏览器窗口的可见部分(即viewport)的大小相同。开始布局过程意味着给每个节点在屏幕上应该出现的确切坐标。
在此绘制,遍历渲染器树并调用渲染器的 paint()
方法以在屏幕上显示内容。
绘图可以是全局的或增量式的(与布局类似):
paint
事件的区域。 操作系统通过将多个区域合并为一个来智能完成。总的来说,重要的中要理解绘图是一个渐进的过程。为了更好的用户体验,渲染引擎将尽可能快地在屏幕上显示内容。它不会等到解析完所有 HTML 后才开始构建和布局渲染树,而是解析和显示部分内容,同时继续处理来自网络的其余内容项。
当解析器到达 <script>
标记时,将立即解析并执行脚本。文档的解析将暂停,直到执行脚本为止。这意味着这个过程是 同步的 。
如果脚本是外部的,那么首先必须从网络中获取它(也是同步的)。所有解析都停止,直到获取完成。HTML5 新加了async 或 defer 属性,将脚本标记为异步的,以便由不同的线程解析和执行。
如果你想优化自己的应用,则需要关注五个主要方面,这些是你自己可以控制的:
JavaScript 经常触发浏览器中的视觉变化,构建 SPA 时更是如此。
以下是一些优化 JavaScript 渲染技巧:
setTimeout
或 setInterval
进行可视更新。 这些将在帧中的某个点调用 callback
,可能在最后。我们想要做的是在帧开始时触发视觉变化而不是错过它。requestAnimationFrame
, setTimeout
, setInterval
中运行它们。通过添加和删除元素,更改属性等来修改 DOM 将使浏览器重新计算元素样式,并且在许多情况下,重新计算整个页面的布局或至少部分布局。
*减少必须进行样式计算的元素的数量。本质上,直接对一些元素进行样式更改,而不是使整个页面无效。
浏览器的布局重新计算可能非常繁重。 考虑以下优化:
box.offsetHeight
,那就不成问题了。但是,如果你在访问 box
之前更改了它的样式(例如,通过动态地向元素添加一些 CSS 类),浏览器必须先应用样式更改并执行布局过程,这是非常耗时和耗费资源的,所以尽可能避免。这通常是所有任务中运行时间最长的,因此尽可能避免这种情况非常重要。 以下是我们可以做的事情:
领取专属 10元无门槛券
私享最新 技术干货