前端阶段性总结(二):页面渲染机制与性能优化

引言: 转前端一年了,期间工作较忙,也没时间整理一些知识体系,此系列文章是对前端基础的一些回顾与总结。本文主要介绍浏览器工作的原理以及一些优化手段。

一、浏览器渲染过程

1. 浏览器的主要结构:

2. 浏览器的多进程模型:

以chorme为例:

  • Browser进程:浏览器的主进程,负责浏览器界面的显示,各个页面的管理,其他各种进程的管理;
  • Renderer进程:页面的渲染进程,负责页面的渲染工作,Blink的工作主要在这个进程中完成(主要分成render主线程和合成器线程);
  • NPAPI插件进程:每种类型的插件只会有一个进程,每个插件进程可以被多个Render进程共享;
  • GPU进程:最多只有一个,当且仅当GPU硬件加速打开的时候才会被创建,主要用于对3D加速调用的实现;
  • Pepper插件进程:同NPAPI插件进程,不同的是为Pepper插件而创建的进程

需要注意的是,NPAPI是指浏览器对系统或外部的一些程序的调用接口,比如播放视频的 flash 插件,而Pepper其实是基于NPAPI改进的插件架构。

3. 网页请求过程:

4. 浏览器渲染过程

a. 主要流程:

主流的浏览器内核主要有2种,Webkit 和 Geoko ,虽然 chorme 现在的内核更换为 blink ,但其实 blink是基于webkit的,差异不大。其渲染过程分别如下:

  • webkit

  • Geoko

这两个内核的渲染流程大同小异,主要的过程可以总结为下列5个:

  • DomTree: 解析html构建DOM树。
  • CssomTree : 解析CSS生成CSSOM规则树。
  • RenderObjectTree: 将DOM树与CSSOM规则树合并在一起生成渲染对象树。
  • Layout: 遍历渲染树开始布局(layout),计算每个节点的位置大小信息。
  • Painting: 将渲染树每个节点绘制到屏幕。

使用chorme浏览器的开发者工具,我们很容易看到这5个过程的时间线,下面是segmentfault主页的渲染截图:

可以看到上述流程的耗时,甚至可以统计到每一帧的耗时分布,从而对影响渲染性能的代码精确定位。其中黄色为JS,紫色为Style和Layout,绿色为Paint和Composite部分,选中每个部分会显示各自的花费时间等信息,可以看出这个图片中JS运行的时间太长。目前的显示设备一般刷新率是60FPS,所以理想中每帧的时间最好为16毫秒。

需要注意的一点是,这里的步骤执行并没有特定的顺序,为保证渲染的速度,浏览器一开始接收到html时就开始执行解析的过程,并且遇到需要重绘和重排的时候会重复执行这些步骤,下面我们详细介绍一下这5个过程。

b. 具体流程

DOM树的构建:

浏览器在接收到html文件后即开始解析和构建DOM树,在碰到js代码段时,由于js代码可能会改变dom的结构,所以为避免重复操作,浏览器会停止dom树构建,先加载并解析js代码。而对于css,图片,视频等资源,则交由资源加载器去加载,这个过程是异步的,并不会阻碍dom树的生成。这个过程需要注意的点是:

  • display:none的元素、注释存在于dom树中
  • js会阻塞dom树的构建从而阻塞其他资源的并发加载,因此好的做法是将js放在最后加载
  • 对于可异步加载的js片段加上 asyncdefer

CSSOM树的构建:

浏览器在碰到