浏览器的渲染原理

看到这个标题大家一定会想到这篇神文《How Browsers Work》,这篇文章把浏览器的很多细节讲得很细,而且也被翻译成了中文。为什么我还想写一篇呢?因为两个原因,

  1)这篇文章太长了,阅读成本太大,不能一口气读完。

  2)花了大力气读了这篇文章后可以了解很多,但似乎对工作没什么帮助。

  所以,我准备写下这篇文章来解决上述两个问题。希望你能在上班途中,或是坐马桶时就能读完,并能从中学会一些能用在工作上的东西。

  浏览器工作大流程

  废话少说,先来看个图:

  从上面这个图中,我们可以看到那么几个事:

  1)浏览器会解析三个东西:

  • 一个是 HTML/SVG/XHTML,事实上,Webkit 有三个 C++ 的类对应这三类文档。解析这三种文件会产生一个 DOM Tree。
  • CSS,解析 CSS 会产生 CSS 规则树。
  • Javascript,脚本,主要是通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree.

  2)解析完成后,浏览器引擎会通过 DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:

  • Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
  • CSS 的 Rule Tree 主要是为了完成匹配并把 CSS Rule 附加上 Rendering Tree 上的每个 Element。也就是 DOM 结点。也就是所谓的 Frame。
  • 然后,计算每个 Frame(也就是每个 Element)的位置,这又叫 layout 和 reflow 过程。

  3)最后通过调用操作系统 Native GUI 的 API 绘制。

  DOM 解析

  HTML 的 DOM Tree 解析如下:

<html>
<head>
  <title>Web page parsing</title>
</head>
<body>
  <div>
  <h1>Web page parsing</h1>
  <p>This is an example Web page.</p>
  </div>
</body>
</html>

  上面这段 HTML 会解析成这样:

  下面是另一个有 SVG 标签的情况。

  CSS 解析

  CSS 的解析大概是下面这个样子(下面主要说的是 Gecko 也就是 Firefox 的玩法),假设我们有下面的 HTML 文档:

<doc>
<title>A few quotes</title>
<para>
Franklin said that <quote>"A penny saved is a penny earned."</quote>
</para>
<para>
FDR said <quote>"We have nothing to fear but <span>fear itself.</span>"</quote>
</para>
</doc>

  于是 DOM Tree 是这个样子:

  然后我们的 CSS 文档是这样的:

/* rule 1 */ doc { display: block; text-indent: 1em; }
/* rule 2 */ title { display: block; font-size: 3em; }
/* rule 3 */ para { display: block; }
/* rule 4 */ [] { font-style: italic; }

  于是我们的 CSS Rule Tree 会是这个样子:

  注意,图中的第 4 条规则出现了两次,一次是独立的,一次是在规则 3 的子结点。所以,我们可以知道,建立 CSS Rule Tree 是需要比照着 DOM Tree 来的。CSS 匹配 DOM Tree 主要是从右到左解析 CSS 的 Selector,好多人以为这个事会比较快,其实并不一定。关键还看我们的 CSS 的 Selector 怎么写了。

  注意:CSS 匹配 HTML 元素是一个相当复杂和有性能问题的事情。所以,你就会在N多地方看到很多人都告诉你,DOM 树要小,CSS 尽量用 id 和 class,千万不要过渡层叠下去,……

  通过这两个树,我们可以得到一个叫 Style Context Tree,也就是下面这样(把 CSS Rule 结点 Attach 到 DOM Tree 上):

  所以,Firefox 基本上来说是通过 CSS 解析生成 CSS Rule Tree,然后,通过比对 DOM 生成 Style Context Tree,然后 Firefox 通过把 Style Context Tree 和其 Render Tree(Frame Tree)关联上,就完成了。注意:Render Tree 会把一些不可见的结点去除掉。而 Firefox 中所谓的 Frame 就是一个 DOM 结点,不要被其名字所迷惑了

  注:Webkit 不像 Firefox 要用两个树来干这个,Webkit 也有 Style 对象,它直接把这个 Style 对象存在了相应的 DOM 结点上了。

  渲染

  渲染的流程基本上如下(黄色的四个步骤):

  1. 计算 CSS 样式
  2. 构建 Render Tree
  3. Layout – 定位坐标和大小,是否换行,各种 position, overflow, z-index 属性 ……
  4. 正式开画

  注意:上图流程中有很多连接线,这表示了 Javascript 动态修改了 DOM 属性或是 CSS 属性会导致重新 Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的 CSS rule 没有被匹配到,等。

  这里重要要说两个概念,一个是 Reflow,另一个是 Repaint。这两个不是一回事。

  • Repaint——屏幕的一部分要重画,比如某个 CSS 的背景色变了。但是元素的几何尺寸没有变。
  • Reflow——意味着元件的几何尺寸变了,我们需要重新验证并计算 Render Tree。是 Render Tree 的一部分或全部发生了变化。这就是 Reflow,或是 Layout。(HTML 使用的是 flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫 reflow)reflow 会从 <html> 这个 root frame 开始递归往下,依次计算所有的结点几何尺寸和位置,在 reflow 过程中,可能会增加一些 frame,比如一个文本字符串必需被包装起来。

  下面是一个打开 Wikipedia 时的 Layout/reflow 的视频(注:HTML 在初始化的时候也会做一次 reflow,叫 intial reflow),你可以感受一下:

  Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。在一些高性能的电脑上也许还没什么,但是如果 reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。

  所以,下面这些动作有很大可能会是成本比较高的。

  • 当你增加、删除、修改 DOM 结点时,会导致 Reflow 或 Repaint。
  • 当你移动 DOM 的位置,或是搞个动画的时候。
  • 当你修改 CSS 样式的时候。
  • 当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候。
  • 当你修改网页的默认字体时。

  注:display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发现位置变化。

  多说两句关于滚屏的事,通常来说,如果在滚屏的时候,我们的页面上的所有的像素都会跟着滚动,那么性能上没什么问题,因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个 fixed 的背景图,或是有些 Element 不跟着滚动,有些 Elment 是动画,那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成 reflow。

  基本上来说,reflow 有如下的几个原因:

  • Initial。网页初始化的时候。
  • Incremental。一些 Javascript 在操作 DOM Tree 时。
  • Resize。其些元件的尺寸变了。
  • StyleChange。如果 CSS 的属性发生变化了。
  • Dirty。几个 Incremental 的 reflow 发生在同一个 frame 的子树上。

  好了,我们来看一个示例吧:

var bstyle = document.body.style; // cache
bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; //  再一次的 reflow 和 repaint
bstyle.color = "blue"; // repaint
bstyle.backgroundColor = "#fad"; // repaint
bstyle.fontSize = "2em"; // reflow, repaint
// new DOM element - reflow, repaint
document.body.appendChild (document.createTextNode (‘dude!‘));

  当然,我们的浏览器是聪明的,它不会像上面那样,你每改一次样式,它就 reflow 或 repaint 一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次 reflow,这又叫异步 reflow 或增量异步 reflow。但是有些情况浏览器是不会这么做的,比如:resize 窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行 reflow。

  但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些 DOM 值:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. IE 中的 getComputedStyle (), 或 currentStyle

  因为,如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会 flush 出去一些样式的改变,从而造成频繁的 reflow/repaint。

  减少 reflow/repaint

  下面是一些 Best Practices:

  1)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。

// bad
var left = 10,
top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";

// Good
el.className += " theclassname";
// Good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

  2)把 DOM 离线后修改。如:

  • 使用 documentFragment 对象在内存里操作 DOM。
  • 先把 DOM 给 display:none (有一次 repaint),然后你想怎么改就怎么改。比如修改 100 次,然后再把他显示出来。
  • clone 一个 DOM 结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。

  3)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。

  4)尽可能的修改层级比较低的 DOM。当然,改变层级比较底的 DOM 有可能会造成大面积的 reflow,但是也可能影响范围很小。

  5)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。

  6)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。

In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.

Fixed layout, CSS 2.1 Specification

This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.

Automatic layout, CSS 2.1 Specification

  几个工具和几篇文章

  有时候,你会也许会发现在 IE 下,你不知道你修改了什么东西,结果 CPU 一下子就上去了到 100%,然后过了好几秒钟 repaint/reflow 才完成,这种事情以 IE 的年代时经常发生。所以,我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。

  • Chrome 下,Google 的 SpeedTracer 是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实 Safari 和 Chrome 都可以使用开发者工具里的一个 Timeline 的东东。
  • Firefox 下这个基于 Firebug 的叫 Firebug Paint Events 的插件也不错。
  • IE 下你可以用一个叫 dynaTrace 的 IE 扩展。

  最后,别忘了下面这几篇提高浏览器性能的文章:

  参考

时间: 2024-11-11 00:57:56

浏览器的渲染原理的相关文章

【转】浏览器的渲染原理简介

How Browsers Work 这篇文章把浏览器的很多细节讲的很细,也有中文的翻译版本,现在转载的这篇是陈皓写的,目的的为了能在上班途中,或是坐马桶时就能读完,并能从中学会一些能用在工作上的东西. 浏览器工作大流程 先看个图 从图中,可以看到: 1) 浏览器会解析三个东西 * 一个 HTML/SVG/XHTML,事实上,Webkit 有三个C++的类对应这三类文档.解析这三种文件会产生一个DOM Tree * CSS,解析CSS会产生CSS规则树 * JavaScript 脚本,主要是通过

浏览器的渲染原理简介

原文转自:http://kb.cnblogs.com/page/178445/ 看到这个标题大家一定会想到这篇神文<How Browsers Work>,这篇文章把浏览器的很多细节讲得很细,而且也被翻译成了中文.为什么我还想写一篇呢?因为两个原因, 1)这篇文章太长了,阅读成本太大,不能一口气读完. 2)花了大力气读了这篇文章后可以了解很多,但似乎对工作没什么帮助. 所以,我准备写下这篇文章来解决上述两个问题.希望你能在上班途中,或是坐马桶时就能读完,并能从中学会一些能用在工作上的东西. 浏览

ie浏览器的渲染原理

IE下载或者渲染顺序大致如下: IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(但并不是说所有相关联的元素都已经下载完.)在下载过程中,如果遇到某一标签是嵌入文件,并且文件是具有语义解释性的(例如:JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载. 并且在下载后进行解析,解析(JS.CSS中如有重定义,后定义函数将覆盖前定义函数)过程中,停止页面所有往下元素的下载.样式表文件比较特殊,在其下

浏览器渲染原理及流程

我们可能都知道浏览器含有一个渲染引擎,用来渲染窗口所展示的内容.默认情况下,渲染引擎可以显示html.xml文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用PDF阅读器插件,用于显示PDF格式.但是其具体的渲染原理和流程估计也有很多人都不知道或者不清楚吧.这些天研究了一下浏览器的渲染原理,有了些心得,在这里跟大家分享一下,这里只讨论渲染引擎最主要的用途--显示应用了CSS之后的html及图片. 渲染引擎简介 本文所讨论的浏览器--Firefox.Chrome和Safari

浏览器渲染原理解析

作者:贝程学院 浏览器内核分为两部分:渲染引擎(Layout Engine 或者 Rendering Engine)和 JS 引擎.早期渲染引擎和 JS 引擎并没有明显区分,随着 JS 引擎越来越独立,内核逐渐变成了渲染引擎的代名词.渲染引擎包括: HTML 解释器 CSS 解释器 布局 网络 存储 图形 音视频 图片解码器 等等 渲染引擎简介 浏览器——Firefox.Chrome和Safari是基于两种渲染引擎构建的,Firefox使用Geoko——Mozilla自主研发的渲染引擎,Safa

[转载]浏览器的工作原理:新式网络浏览器幕后揭秘

原文地址 序言 这是一篇全面介绍 WebKit 和 Gecko 内部操作的入门文章,是以色列开发人员塔利·加希尔大量研究的成果.在过去的几年中,她查阅了所有公开发布的关于浏览器内部机制的数据(请参见资源),并花了很多时间来研读网络浏览器的源代码.她写道: 在 IE 占据 90% 市场份额的年代,我们除了把浏览器当成一个"黑箱",什么也做不了.但是现在,开放源代码的浏览器拥有了过半的市场份额,因此,是时候来揭开神秘的面纱,一探网络浏览器的内幕了.呃,里面只有数以百万行计的 C++ 代码.

浏览器的渲染顺序

直接上代码 1 <style> 2 div{ 3 width: 500px; 4 height: 400px; 5 border: 1px solid #ff0000; 6 background: url(./images/11.jpg); //css里的图片11.jpg 7 } 8 </style> 9 <script> 10 new Image().src = "./images/22.jpg": //js里的图片22.jpg 11 </s

深入学习页面优化之页面渲染原理

拾人牙慧理解并整理之 直奔主题,要考虑到页面性能优化,必须得理解浏览器的渲染机制才行. 1.原理 渲染引擎在这里就不展开了,可自行搜索解决.下面说说渲染流程,大致是这样的: 浏览器在接收到服务器返回的html页面后, 浏览器开始构建DOM TREE,遇到CSS样式会构建CSS RULER TREE, 遇到javascript会通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree,解析完成后, 浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来

不同内核的浏览器,以及渲染原理

一. "Rendering Engine"渲染引擎,也可以叫做浏览器内核(这个部分要扩展一下,原本的浏览器内核分为渲染引擎和Js引擎,后来Js引擎越来越独立,内核就倾向于只指渲染引擎了),是浏览器最核心的部分,浏览器内核的不同对于网页的语法解释会有不同,所以渲染出来的效果也不同,简单说就是不同的内核决定了浏览器如何显示页面,也是因为这个原因导致了浏览器兼容性问题的出现. 下面是5种浏览器分别采用的不同引擎: - IE内核 Trident- 谷歌内核 WebKit / Blink(由we