浏览器渲染

搞清楚浏览器渲染的过程对于前端优化是很有用处的!最近看了一些博客和文章稍微做些总结,具体细节进一步参照原文链接和相关文章!

最终决定浏览器表现出来的页面效果的差异是:渲染引擎 Rendering Engine(也叫做排版引擎),也就是我们通常所说的“浏览器内核”(IE--Trident,FF--Gecko,Opera--Presto,Chrome--Webkit),负责解析网页语法(如HTML、JavaScript)并渲染、展示网页。相同的代码在不同的浏览器呈现出来的效果不一样,那么就很有可能是不同的浏览器内核导致的。

对于页面加载一种最简单的描述形式是:

1、用户访问网页,发送一个http请求到网络服务器。
2、网络服务器(应用服务器)解析请求,发送请求给数据库服务器。
3、数据服务器返回数据给网络服务器,网络服务器解析数据,并生成html文件内容放入http response中,返回给浏览器。
4、浏览器解析http response。
5、浏览器创建DOM树。
6、浏览器下载css,并应用在DOM树上,进行渲染。
7、浏览器下载js,并解析执行js。
 
后面三步可以用下图进一步说明:

1、解析HTML以重建DOM树(Parsing HTML to construct the DOM tree ):渲染引擎开始解析HTML文档,转换树中的标签到DOM节点,它被称为“内容树”

2、构建渲染树(Render tree construction):解析CSS(包括外部CSS文件和样式元素),根据CSS选择器计算出节点的样式,创建另一个树 —- 渲染树

3、布局渲染树(Layout of the render tree): 从根节点递归调用,计算每一个元素的大小、位置等,给每个节点所应该出现在屏幕上的精确坐标。

4、绘制渲染树(Painting the render tree) : 遍历渲染树,每个节点将使用UI后端层来绘制。

主要的流程就是:构建一个dom树,页面要显示的各元素都会创建到这个dom树当中,每当一个新元素加入到这个dom树当中,浏览器便会通过css引擎查遍css样式表,找到符合该元素的样式规则应用到这个元素上。

 
在上面的流程中,只要有一个环节出现问题,渲染页面都不能正常进行。比如说:
服务器端:
网络服务器:无法获取到资源文件(404),或者由于并发的原因暂时无法处理你的请求(最常见的500错误),你的浏览器会长时间处于空白状态,直到服务器返回状态,或者进行超时处理。
数据层:如果服务器停止,或忙于处理大数据等等,长时间无法返回数据给网络服务器,那么网络服务器一直处于等待状态中,如果请求量达到最大值,那么后面的请求都被堵塞,从而无法及时返回内容给浏览器。
 
客户端:
JavaScript:如果你的js写在body里,而且这个js执行非常复杂的逻辑,那么整个页面处于等待状态中。
不论js代码是内联还是包含在一个不相干的外部文件中,页面下载和解析过程肯定会停下,等待脚本执行完成这些处理,然后才能继续进行。
大多数浏览器使用单进程处理JavaScript的多个任务,同一时间只能有一个任务执行。
 
CSS:
PS:浏览器可以同时下载多个CSS文件。
如果我们把CSS样式放在页面底部,虽然使页面内容能更快的加载(因为将加载css 文件的时间放在最后,从而使页面内容先显示出来),但这样的内容是没有样式的,在CSS文件加载进来后,浏览器再对DOM使用样式,会出现我们常说的“无样式之闪烁”。
更讨厌的是,上下都放置CSS样式,浏览器会首先按照上面的进行渲染,等到下面的样式上来,再按照下面的样式进行回流(reflow)和重绘(repaint),用户感觉很差。
 
注意两个词“repaint"和"reflow":(具体可以参照http://news.cnblogs.com/n/178402/后半部分更详细介绍)
repaint(重绘):在一个元素的外观被改变,但没有改变布局的情况下发生。——如果只是改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引起浏览器repaint。
reflow(回流):浏览器发现某个部分发生了点变化影响了布局,需要倒回去重新渲染,这个回退的过程就叫回流。
 
改进方案:
服务端:
从语言层面考虑,我们可以使用逐步返回内容的方式,输送数据给浏览器,如我们可以使用php的flush,把整个head部分,半个body加一部分div返回给浏览器,进行渲染,然后把其他部分逐步输送到浏览器。
我们可以在服务端使用多线程或多进程的方式并发去进行数据处理。

JavaScript:

1、把脚本进行压缩(移除不必要的字符,注释以及空行)。

2、对部分js文件进行合并,以减少http的请求个数,以减少服务器端的压力——但是要量力而行,因为如果你的js文件很大,下载很慢的话,很多功能都不能正常进   行,我们可以按照业务进行合并。

3、使用外部js文件。因为现在很多浏览器都有缓存,明显会减少http请求数。

4、将脚本放在页面底部。先让用户看到内容,然后再加载js,这样用户会感觉页面加载速度很快。

CSS:

1、合并多个css文件,以减少http的请求个数,以减少服务器端的压力。

2、使用外部css文件。主要原因是浏览器缓存,以减少http请求。

3、放在页面顶部(head标签处),防止出现“无样式内容的闪烁”。

PS:

<quote>

http://www.smallni.com/css-performance-from-the-browsers-rendering/

css引擎查找样式表,对每条规则都按从右到左的顺序去匹配。 看如下规则:

#nav li{}

看起来很快,实际上很慢,尽管这让人有点费解。我们中的大多数人,尤其是那些从左到右阅读的人,可能猜想浏览器也是执行从左到右匹配规则的,因此会推测这条规则的开销并不高。在脑海中,我们想象浏览器会像这样工作:找到唯一的ID为nav的元素,然后把这个样式应用到直系子元素的li元素上。我们知道有一个ID为nav的元素,并且它只有几个li子元素,所以这个CSS选择符应该相当高效。

在CSS书写过程中,总结出有如下性能提升的方案:

  1. 避免使用通配规则,如 *{} 计算次数惊人!只对需要用到的元素进行选择;
  2. 尽量少的去对标签进行选择,而是用class,如:#nav li{},可以为li加上nav_item的类名,如下选择.nav_item{};
  3. 不要去用标签限定ID或者类选择符,如:ul#nav,应该简化为#nav;
  4. 尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的使用类来关联每一个标签元素;
  5. 考虑继承,了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。

可以看一下CSS selectors Test,这个实验的重点是评估复杂选择符和简单选择符的开销。

但说到底,CSS性能这东西对于小的项目来讲可能真的是微乎其微的东西,提升可能也不是很明显,但对于大型的项目肯定是有帮助的。而且一个好的CSS书写习惯和方式能够帮助我们更加严谨的要求自己。(赞成作者这个说法!)

</quote>

推荐阅读:
 
原文地址:https://www.cnblogs.com/ttcc/p/3817407.html