到目前为止,在咱们以前的“JavaScript工做原理”系列文章中,咱们一直关注JavaScript做为一种语言,其功能,它如何在浏览器中执行,如何优化等等。css
可是,当您构建Web应用程序时,您不仅是编写独立运行的独立JavaScript代码。您编写的JavaScript与环境进行交互。了解这种环境,它是如何工做的以及它的组成是什么,将使您可以构建更好的应用程序,并对应用程序发布后可能出现的潜在问题作好充分准备。html
那么,让咱们看看浏览器的主要组件是什么:node
在这篇文章中,咱们将关注渲染引擎,由于它处理HTML和CSS的解析和可视化,这是大多数JavaScript应用程序不断与之交互的东西。git
渲染引擎的主要职责是在浏览器屏幕上显示请求的页面。github
渲染引擎能够显示HTML和XML文档和图像。若是您使用额外的插件,引擎还能够显示不一样类型的文档,如PDF。web
与JavaScript引擎相似,不一样的浏览器也使用不一样的渲染引擎。这些是一些流行的:后端
渲染引擎从网络层接收所请求文档的内容。浏览器
构建DOM树
渲染引擎的第一步是解析HTML文档并将解析的元素转换为DOM树中的实际DOM节点。网络
想象一下你有如下的文字输入:框架
<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>
这个HTML的DOM树以下所示:
基本上,每一个元素都被表示为全部其子元素的父节点子元素直接包含在它的内部。
CSSOM指的是CSS对象模型。当浏览器构建页面的DOM时,它在引用外部theme.css CSS样式表的head部分遇到link标记。预计它可能须要该资源来呈现页面,它当即发出请求。假设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元素的子元素,则因为正在应用更具体的样式,所以不会显示其内容。
另外请注意,上面的树不是完整的CSSOM树,只显示了咱们决定在样式表中重写的样式。每一个浏览器都提供了一组默认的样式,也称为“user agent styles” - 这是咱们在没有明确提供任何样式时看到的。咱们的样式简单地覆盖这些默认值。
HTML中的可视指令与CSSOM树中的样式数据结合在一块儿用于建立渲染树。
你可能会问什么是渲染树?这是按照它们在屏幕上显示的顺序构建的视觉元素树。它是HTML和相应的CSS的可视化表示。此树的目的是为了以正确的顺序绘制内容。
Webkit中,渲染树中的每一个节点都被称为的渲染器或渲染对象。
这就是上述DOM和CSSOM树的渲染器树的外观:
为了构建渲染树,浏览器大体以下:
你能够在这里看看RenderObject的源代码(在WebKit中):https://github.com/WebKit/web...
咱们来看看这个类的一些核心内容:
class RenderObject : public CachedImageClient { // Repaint the entire object. Called when, e.g., the color of a border changes, or when a border // style changes. Node* node() const { ... } RenderStyle* style; // the computed style const RenderStyle& style() const; ... }
每一个渲染器表明一个矩形区域,一般对应于一个节点的CSS框。它包括几何信息,例如宽度,高度和位置。
当渲染器被建立并添加到树中时,它没有位置和大小。计算这些值称为布局。
HTML使用基于流程的布局模型,这意味着大部分时间内它能够经过一次传递计算几何。坐标系相对于根渲染器。使用顶部和左侧坐标。
布局是一个递归过程 - 它从根呈现器开始,它对应于HTML文档的<html>元素。布局经过部分或整个渲染器层次结构递归地继续递归,为须要它的每一个渲染器计算几何信息。
根渲染器的位置是0,0,而且其尺寸具备浏览器窗口(也称为视口)的可见部分的尺寸。
开始布局过程意味着给每一个节点确切的坐标,它应该出如今屏幕上。
在此阶段中,遍历渲染器树并调用渲染器的paint()方法以在屏幕上显示内容。
绘画能够是全局或增量式(与布局相似):
通常来讲,了解绘画是一个渐进的过程是很重要的。为了更好的用户体验,渲染引擎会尝试尽快在屏幕上显示内容。它不会等到全部的HTML被解析,才开始构建和布置渲染树。内容的部份内容将被解析并显示,而该过程继续保持来自网络的其他内容项目。
当解析器到达<script>标记时,脚本将被当即解析并执行。文档解析暂停,直到脚本执行完毕。这意味着该过程是同步的。
若是脚本是外部的,那么它首先必须从网络中获取(也是同步的)。全部解析都会中止,直到抓取完成。
HTML5添加了一个选项,将脚本标记为异步,以便它能够被其余线程解析和执行。
若是您想优化您的应用,那么您须要关注五个主要方面。这些是您能够控制的区域:
JavaScript常常触发浏览器中的视觉变化。当创建一个SPA时更是如此。
如下是关于JavaScript能够优化哪些部分以改善渲染的一些提示:
经过添加和删除元素,更改属性等来修改DOM将使浏览器从新计算元素样式,而且在不少状况下还会整个页面的布局或至少部分布局。
要优化渲染,请考虑如下几点:
浏览器的布局从新计算可能很是繁重。考虑如下优化:
这一般是全部任务中运行时间最长的,所以尽量避免这种状况很是重要。如下是咱们能够作的事情: