好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

渲染性能_html/css_WEB-ITnose

该文章于三天前发表在 github,若有问题可提至 github .

这篇文章主要关注的是资源加载之后的性能,因为大多数用户关注的不是应用如何加载而是具体的使用。所以要快速响应用户,尤其是无线端,我们有必要了解浏览器渲染性能。

RAIL 性能模型

首先一个需要思考的问题,怎样的网站是顺畅的?我们可能可以给一个大概的感觉,如:秒级响应等。其实,也可以给出一个很讨巧的答案:用户觉得顺畅的网站它就是顺畅的。因为几乎所有网站都希望将用户留在页面上,当然以用户为中心建立性能模型是必要的。下面是 Google 提出的一个以用户为中心的性能模型,里面的数据不是 Google 首创,有一些论文做类似研究(如:100ms 响应用户是一个很合适的时间等)。

上图是 RAIL 的具体含义,这里有一些关键性的数据指标:

Respond:0 - 100ms,视窗一般需要在这个时间段响应用户,超过这个时间段,用户就会感觉到延时。

Animation:0~16ms,屏幕每秒刷新60次,16ms 代表的是每一帧的时间。用户是非常关注动画的,当动画失帧很容易引起用户察觉。所以动画一般要控制在60FPS。

Idle:最大化主进程的空闲时间,这样可以及时响应用户输入。

Load:内容需要在1000ms 内加载出来,超过1000ms 会觉得加载缓慢。

应用要达到上面的性能模型需要从哪些方面入手呢?如果我们知道浏览器是如何渲染一个页面的,并且去优化渲染过程中的关键步骤,是不是就能事半功倍呢?

关键渲染路径

上图是浏览器渲染的关键路径,首先,让我们从浏览器解析一个页面开始吧。

转化:浏览器从磁盘或网络读取 HTML 的原始字节,浏览器会将这段原始文件按照相应编码规范进行解码(现在一般为 utf-8)。

符号化:根据 W3C 标准转化为对应的符号(一般在尖括号内)。

DOM 构建:HTML 解析器会解析其中的 tag 标签,生成 token ,遇到 CSS 或 JS 会发送相应请求。HTML 解析时阻塞主进程的,CSS 一般也是阻塞主进程的(媒体查询时例外),也就是说它们在解析过程中是无法做出响应的。而 JS 手动添加 async 后达到异步加载,根据 token 生成相应 DOM 树。

CSSDOM 构建,添加 CSS 样式生成 CSSDOM 树。

渲染树构建,从 DOM 树的根节点开始,遍历每个可见的节点,给每个可见节点找到相应匹配的 CSSOM 规则,并应用这些规则,连带其内容及计算的样式。

样式计算,浏览器会将所有的相对位置转换成绝对位置等一系列的样式计算。

布局,浏览器将元素进行定位、布局。

绘制,绘制元素样式,颜色、背景、大小、边框等。

合成,将各层合成到一起、显示在屏幕上。

如果我们是做一个动画,一般会用 JS 更改相应样式,接着浏览器就会经历 JS 运行、样式计算、布局、绘制、合成等多个重要步骤(后面还会讲到这个步骤实际过程中可以更长或者更短)。那么要做的优化就是在这几个步骤中进行优化并且尽量去掉中间的耗时步骤。

优化JavaScript的执行

上图描述的四个场景都是有可能对响应用户输入或者动画造成影响的。函数的输入事件处理、不合时机的 JS 、长时间的 JS 运行以及垃圾回收。

函数的输入事件处理

首先,我们要知道的一个事实就是浏览器是由多个处理进程的:Compositor、Tile Worker、Main。当用户进行输入操作(滚动、点击等),如滚动时,Compositor 进程会接收到这个事件(实际它可以接受任何用户输入事件),如果可以的话,它将不会通知主进程,直接说:滚吧,牛宝宝。于是,页面就滚动了。当然,这其中包含更新层定位以及让 GPU 绘制帧,而主线程处于空闲状态。但是,事情往往并非如此。如果输入事件上绑定了 JS 处理事件的话,Compositor 进程就没办法主动跳过主进程了。

如上图,当 JS 处理事件过长时,输入事件的响应会一直处于阻塞状态,直到 JS 处理完成。当响应超过 100ms 时,用户就会感受到延时。所以当处理用户事件时,我们应该做到:

避免长时间的 JS 执行。

避免在处理中改变样式。因为样式改变会引起后面布局、绘制、合成等操作。

对用户输入进行消抖。

优化处理

其他优化:

使用 requestAnimationFrame,将 setTimeout 换成 requestAnimationFrame,因为 setTimeout 时间控制可能造成在一帧的中间,目前各浏览器对 requestAnimationFrame 的支持已经比较好了。

使用 Web Workers,将复杂计算的 JS 采用 Web Workers 进行处理。

减少垃圾回收,垃圾回收是一个容易被忽略的问题,因为垃圾回收的时间是不受控制的,它可能在一个动画的中途,阻塞动画的执行,更理想的情况是在循环中复用对象。

样式计算

添加或移除一个 DOM 元素、修改元素属性和样式类、应用动画效果等操作,都会引起 DOM 结构的改变,从而导致浏览器需要重新计算每个元素的样式、对页面或其一部分重新布局(多数情况下)。计算样式的第一步是创建一套匹配的样式选择器,浏览器就是靠它们来对一个元素应用样式的。第二步是根据匹配的样式选择器来获取对应的具体样式规则,计算出最终具体有哪些样式是要应用在 DOM 元素上的。所以样式的优化也是这两步:

减小选择器的复杂性

如何减小选择器的复杂性?

.box:nth-last-child(-n+1) .title {  /* styles */}.final-box-title {  /* styles */} 

上面代码都是选择同一个元素,当元素很多时,第二个选择器的性能会明显优于第一个。BEM 规范有做类似事情,按照特性直接由一个选择器选择元素的性能往往会更优。

减少样式的计算量

因为元素的计算量和被改变的元素的数量成正比,所以你只需要注意一点,减少无效元素。

多层无意义的标签

查看更多关于渲染性能_html/css_WEB-ITnose的详细内容...

  阅读:25次