浏览器是我们面向世界的窗口,说起本质,它就像我们家里摆放的电视,只不过我们可以从浏览器中去搜索发现我们想知道的东西,恰恰是它这一点的不同,使得它更灵活丰富,那么为了满足我们的这个需求,浏览器在人类的背后悄悄做了些什么呢?
请求的过程见上一篇博客《浅谈http协议》
本文将重点讲解在我们的浏览器请求到一份完整网页资源后是如何将东西渲染到我们肉眼可见的屏幕上的。
渲染流程
- 解析html和css文件
- 先将请求到的html文档中的DOM以树形(数据结构)解析出来,生DOM Tree。
- 将CSS解析及规则匹配成Style Rules Tree。这里需要到CSS解释器。
- 先将请求到的html文档中的DOM以树形(数据结构)解析出来,生DOM Tree。
- 浏览器引擎将DOM Tree及Style Rules Tree合并生成Render Tree。(不包含head元素以及一些display:none的元素)
- 浏览器根据Render Tree,得知这个网页中有哪些节点以及对应的css特性,从而计算每个节点在屏幕重的位置。布局计算是一个递归的过程,这是因为一个节点的大小通常需要先计算它的子节点的位置、大小等信息。
- 按照算出来的规则,通过显卡,把内容画到屏幕上。
- 当页面某个元素的属性发生了变化,例如颜色值发生了变化,会导致浏览器对该元素进行重绘,我们称之为「重绘」;如果是位置的变化、元素大小的变化、或添加一个新的Dom,这都将导致浏览器的重新计算布局,称之为「重排」。
以下从主流浏览器内核对以上行为进行分析
在浏览器中,有一个最重要的模块,它主要作用是将页面转变成可视化的图形结果,这就是浏览器内核。通常,它也被称为渲染引擎。
- Webkit内核 :
- 内核详解
- 操作系统层
- Webkit渲染过程中依赖的很多第三方模块Webkit层,主要包含webCore、渲染引擎层、嵌入式接口层,绑定层
- 一个渲染引擎主要包括HTML解释器、CSS解释器、布局和JavaScript引擎
- HTML解释器:将html文件解析成dom树
- CSS解释器:为DOM中的各个元素对象计算出样式信息,从而为计算最后网页的布局提供基础设施
- 布局Layout:在DOM创建之后,Webkit需要将其中的元素对象同样式信息结合起来,计算它们的大小位置等布局信息,形成一个能够表示这所有信息的内部表示模型
- JavaScript解析引擎 : JavaScript解析引擎就是根据ECMAScript定义的语言标准来动态执行JavaScript字符串。
- 在JavaScript解析过程中,如遇错误就直接跳出当前代码块,直接执行下一个 script 代码段。所以在同一个 script 内的代码段有错误的话就不会执行下去,但是不会影响下一个 script 内的代码段。
- JavaScript解析过程
- 语法检查:
- 词法分析:把字符流转化为记号流
- 语法分析:把从程序中收集的信息存储到数据结构中,每取一个词法记号,就送入语法分析器进行分析。
- 运行阶段:
- 预解析:
- 创建执行上下文。 JavaScript引擎将语法检查正确后生成的语法树复制到当前执行上下文中。
- 属性填充。JavaScript引擎会对语法树当中的变量声明、函数声明以及函数的形参进行属性填充。
- 执行代码:Javascript引擎会一行一行的读取并运行代码
- 预解析:
- 语法检查:
- 内核详解
- 渲染流程图(图取其他网站)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
动画中如何性能优化?
-
减少重排(reflow):
如果你修改一个DOM元素的“Layout”属性,也就是改变了元素的样式(比如
width
、height
或者position
等),那么浏览器会检查哪些元素需要重新布局,然后对页面激发一个reflow(重排)过程完成重新布局。被reflow(重排)的元素,接下来也会激发绘制过程,最后激发渲染层合并过程,生成最后的画面。CSS Triggers- 添加或者删除可见的DOM元素
- 元素位置改变
- 元素尺寸改变
- 元素内容改变(例如:一个文本被另一个不同尺寸的图片替代)
- 页面渲染初始化(这个无法避免)
- 浏览器窗口尺寸改变
不是所有属性动画消耗的性能都一样,其中消耗最低的是transform
和opacity
两个属性,其次是Paint相关属性。所以在制作动画时,建议使用transform
的translate
替代margin
或position
中的top
、right
、bottom
和left
,同时使用transform
中的scaleX
或者scaleY
来替代width
和height
。
-
页面滚动时,避免不必要的渲染:
动画帧率可以作为衡量标准,一般来说画面在 60fps 的帧率下效果比较好,所以为了确保页面的流程,必须保证60fps
内不发生两次渲染树更新。(图取其他网站)- CSS伪类触发。有些CSS伪类在页面滚动时会不小心触发到。比如
:hover
效果有box-shadow
、border-radius
等比较耗时的CSS属性时,建议页面滚动时,先取消:hover
效果,滚动停止后再加上:hover
效果。这个可以通过在外层添加类名进行控制。但添加类名、删除类名也会改变元素时,浏览器就会要重新做一次计算和布局。所以千万要小心这种无意触发重新布局的操作,有的时候可能不是动画,但去付出的代价要比做一个动画更加昂贵。也就是说classname
变化了,就一定会出现一次rendering计算,如果一定需要这么做,那可以使用classlist
的方法。 overflow:scroll
。前面说了,而在滚动也会触发Repaint和Reflow。在调试过程中注意到一个有趣的现象,有时打开了页面并不会导致crash,但快速滑动的时候却会。由于crash是页面本身内存占比过高,只要优化了页面的内存占用,滑动自然也不会是很大的问题。无论你在什么时候滑动页面,页面滚动都是一个不断重新组合重新绘制的过程。所以减少渲染区域在滚动里就显得非常重要。position:fixed;
。fixed
定位在滚动时会不停的进行渲染,特别是页面顶部有一个fixed
,页面底部有个类似返回顶部的fixed
,则在滚动时会对整个页面进行渲染,效率非常低。可以通过transform: translateZ(0)
或者transform: translate3d(0,0,0)
来解决touch
事件的监听
- CSS伪类触发。有些CSS伪类在页面滚动时会不小心触发到。比如
-
避免不必要的长时间渲染:
- 复杂的CSS
- Image Decodes:特别是图片的Image Decodes及Image Resize这两个过程在移动端是非常耗时的
- Large Empty Layers: 大的空图层