-
Notifications
You must be signed in to change notification settings - Fork 0
Description
对浏览器解析和渲染页面的优化,主要通过以下几个方面:
- 优化页面的关键路径
- 减少(或避免)渲染需要的html/css/js
- 优化html/css/js的代码,加快浏览器解析html、css的时间,提高js的执行效率
- 适当的触发浏览器硬件加载
首先需要了解浏览器的解析和渲染的过程
浏览器的解析和渲染过程
主要分以下几个步骤:
- 解析HTML/SVG/XHTML,build DOM树
- 解析CSS,build CSSOM树
- build rendering tree. Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。
- layout/reflow.计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程
- paint. 最后通过调用操作系统Native GUI的API绘制
解析过程优化
根据http响应返回的数据根据页面编码进行解码,而浏览器判断编码主要是依据以下方法:
- HTTP 头中的 Content-Type: text/html; charset= 信息,这一般有最高的优先级;
- 网页本身 meta header 中的 Content-Type 信息的 charset 部分,对于 HTTP 头未指定编码或者本地文件,一般是这么判断;
- 假如前两条都没有找到,浏览器菜单里一般允许用户强制指定编码;
部分浏览器 (比如 Firefox) 可以选择编码自动检测功能,使用基于统计的方法判断未定编码。 - 编码确定后,网页就被解码成了 Unicode 字符流,可以进行HTML 解析.
解析返回的内容,一个字符一个字符解析,边下载边解析
优化点:
- 通过HTTP头信息或meta标签尽早指定正确的字符编码,省去浏览器判断字符编码的时间
解析过程遇到css,js等资源,就会发起请求,下载css,js,..,但是浏览器同一个域名并行下载个数有限制
优化点:
- js/css 多域名存放,增加并行下载的个数,注意与cdn查询成本从和浏览器处理性能之间的平衡
进一步阅读
build dom tree
字节(Bytes) -> 字符(characters) -> 标记(tokens) -> 节点(nodes) -> 对象模型
- 转换(Conversion): 浏览器从磁盘或者网络上读取HTML的原始字节,然后根据指定的编码规则转换成单独的字符(比如按UTF-8编码)。
- 标记分解(Tokenizing):浏览器将字符串按照W3C HTML5标准转换成确定的标记,比如、以及其他带尖括号的字符。每个标记都有特定的意义以及一套规则。
- 词法分析(Lexing):分解出来的标记被转换成能定义其属性和规则的对象。
- DOM 构造: 最终由于HTML标记创建出来的对象被关联到一个树形数据结构。这颗树会反映在原先标签里定义的父子关系,比如HTML对象就是body对象的父对象,body对象又是paragraph对象的父对象等等。
优化点:
- 去除HTML/CSS/js/中的空白字符,省去浏览器解析空格的时间
- 删除无用的html标签,精简html结构,尽量减少不必要的dom的深度
进一步阅读
build CSSOM tree
提高CSS解析的效率,主要通过几个方面:
- 避免通配符选择器
- 删除多余的修饰语
- 避免使用后代选择器,特别是那些指定多余祖先的
- 使用class选择器代替后代选择器
- 在IE中避免对非链接元素应用:hover
- 避免CSS expressions
进一步阅读
build render tree
浏览器只有同时具备DOM(Document Object Mode,文档对象模型)和CSSOM(CSS Object Model,CSS对象模型)才能来构造渲染树。
为了创建渲染树,浏览器大致需要如下的步骤:
- 从DOM树的根节点开始,遍历所有的可视节点
- 有些不可见元素(比如脚本标签,元数据标签之类)会被忽略,因为它们不影响渲染的结果
有些通过CSS隐藏掉的元素也会被忽略,比如上图中的span元素。由于该元素上显式地设置了属性“display:none”,所以不会出现在渲染树上。(CSS中“visibility:hidden”和“display:none”是不同的。前者将元素隐藏起来,但是隐藏元素仍然会在页面最终的布局中占据相应的空间(其实就是一块空白)。然而后者会直接将元素从渲染树中删除,不仅不可见,也不属于最终布局的一部分) - 对于每个可视节点,从CSSOM中寻找对应的样式规则,并付诸节点
- 输出可视的节点,以及每个节点计算出来的样式
谷歌 Web 开发最佳实践手册(4.1.2):渲染树结构、布局和绘制
浏览器即使已经处理好了某些页面内容,只要CSSOM没有构建好,便不会进行渲染,CSS是渲染过程中的阻塞性资源
js前面的css会阻塞js的执行
js执行阻塞页面的解析,domtree的构建
css优化点:
- gzip压缩,提高传输速度,css尽早加载
- 优化css代码,css代码瘦身,压缩,去除冗余
- put css head, js bottom
js优化点:
- js压缩提高传输速度
- put css head, js bottom
- 页面中不要插入js的代码,根据实际情况对js进行defer/asyn,或创建标签异步加载js;
- 代码优化,使用id选择器,使用标准的css选择器,
〉* 对常用变量暂存- 对长时间执行的代码用定时器来分片执行或html5 worker
reflow
我们已经计算了什么节点是可视的以及它们对应的样式是什么,但是我们还没有计算它们在当前设备中准确的位置和尺寸。这正是布局阶段要做的的工作,该阶段在英语中也被称为“回流”(reflow)。布局阶段的输出结果称为 “盒模型”(box model)。盒模型精确表达了窗口中每个元素的位置和大小,而且所有的相对的度量单位都被转化成了屏幕上的绝对像素位置。
paint
最后阶段,已经知道了哪些节点是可视的、它们的样式和它们的几何外观,我们终于能够将这些信息渲染为屏幕上每个真实的像素点了。这个阶段称为“绘制”,或者“格栅化”(rasterizing)。一旦布局阶段完成,浏览器便开始绘制阶段,将渲染树转化为屏幕上的每个像素点
show
通过调用操作系统Native GUI的API绘制每一个像素点,更新显存,给显示器发送信号,显示器根据得到的信号进行显示。
如果浏览器刷新频率为60hz,那么定时器的时间小于1/60s(16.666..ms)就没有意义。
优化点:
- 设置适当的定时时间,小于1/60是没意义
- 使用requestAnimationFrame(rAF)来代替定时器动画
- 利用GPU加速
进一步阅读
Accelerated Rendering in Chrome
Jank Busting for Better Rendering Performance
Leaner, Meaner, Faster Animations with requestAnimationFrame
GPU Accelerated Compositing in Chrome
reflow & repaint
以下这些会引起reflow & repaint:
-
改变文字大小
-
应用新的样式或者修改任何影响元素外观的属性,修改class/style
-
激活伪类,如:hover
-
DOM元素的添加、修改(内容)、删除( Reflow + Repaint),添加新的页面内容,例如输入框输入文字等
-
读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE))
-
当一个元素的外观被改变,但没有改变布局的情况下发生,如改变visibility、outline、前景色,scroll等,会引起repain
-
Resize浏览器窗口、滚动页面,
-
没有指定的图片尺寸,或者如果指定的尺寸不符合图片的实际尺寸,一旦图片下载,浏览器将需要reflows和重新绘制页面
-
在页面中间通过style或外链加载样式,也会导致reflow
-
不指定页面的编码,浏览器开始渲染页面之后发现指定的编码设定与其设定或默认值不同,都会导致重新解析文档并重绘页面。如果编码的变化影响到了外部资源(例如css\js\media),浏览器甚至会重新对资源进行请求;
优化点:
- 尽可能限制reflow的影响范围,避免在document上直接进行频繁的DOM操作,如果确实需要可以采用off-document的方式进行
1. 先将元素从document中删除,完成修改后再把元素放回原来的位置
2. 将元素的display设置为”none”,完成修改后再把display修改为原来的值
3. 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入- 最好通过设置class的方式,避免通过style设置
- 实现元素的动画,它的position属性应当设为fixed或absolute,这样不会影响其它元素的布局
- 不要用table布局,用table的场合,可以设置table-layout为auto或fixed,这样可以让table一行一行的渲染
- 避免使用css expression
- 在HTML的
标签中或在CSS中为所有图片指定宽度和高度,或它的块级父元素指定宽度和高度;指定与图片本身相一致的尺寸
- 把外部样式表和内联样式块放在页面的中,确保样式表首先被下载和解析
- 通过HTTP头信息或meta标签尽早指定正确的字符编码
其他优化点
- 能用css实现的不用img来做,能用css3实现,不用js来做(当然要综合考虑考虑兼容性和成本)
- 不要在线上使用@import 加载css
- 尽可能使用标准的CSS属性
进一步阅读
Rendering: repaint, reflow/relayout, restyle
适当的触发硬件加速
在Webkit内核的浏览器中,硬件加速会把需要渲染的元素放到特定的『Composited Layer』中,表示放到了一个新的『复合层(composited layer)』中渲染。每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中 transform是不会触发 repaint 的,这一点非常类似3D绘图功能,最终这些使用 transform的图层都会由独立的合成器进程进行处理。
层创建标准:
- 3D 或透视变换(perspective transform) CSS 属性
- 使用加速视频解码的 元素
- 拥有 3D (WebGL) 上下文或加速的 2D 上下文的 元素
- 混合插件(如 Flash)
- 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
- 拥有加速 CSS 过滤器的元素
- 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
- 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染), 如果有一个元素,它的兄弟元素在复合层中渲染,而这个兄弟元素的z-index比较小,那么这个元素(不管是不是应用了硬件加速样式)也会被放到复合层中。最可怕的是,浏览器有可能给复合层之后的所有相对或绝对定位的元素都创建一个复合层来渲染,




