# 创建你的 60 FPS 页面

# 课程目标

  • 理解浏览器如何通过 HTML、CSS、Javascript 来渲染像素,学习关键渲染路径
  • 学习 RAIL 生命周期,了解每个生命周期应该限制在多少时间,以及如何完成这个目标
  • 学会使用 chrome 开发者工具分析页面性能,找出性能的瓶径处
  • 学习如何使用 webwork 创建副线程优化页面性能
  • 学习如何分别优化布局,合成和绘制流程

# RAIL 模型

# 阅读

RAIL 是一个以用户为中心的性能模型,包含一下四个性能指标。

加载阶段(Response):您有 1000ms 的时间进行响应 闲置阶段(Load):做优先级不高的事情,确保之后出现的任何互动都能及时响应 50ms 动画阶段(Idle):用户滚动屏幕或者出现动画 你需要 16ms 渲染一帧 响应阶段(Response):人的大脑可以忍受 100ms 的停顿时间,如果再长人们会觉得不流畅

# 自测问题

  • 请解释 RAIL 的含义
  • RAIL 每一个阶段能够接受的时间范围是多少?

# 关键渲染路径

# 阅读

掌握关键渲染路径,以及修改 CSS 属性会触发哪些步骤,从而影响性能,理解这一过程是优化应用性能的关键

# 自测问题

  • 核心 web 指标什么?
  • 目前大多数设备的屏幕刷新率是多少?为了避免抖动现象,你需要保证每一帧的的渲染速率在多少以内?
  • 请说说渲染书和 DOM 树的区别。
  • 下面这个 div 标签会出现在渲染树中吗
<style>
div{
    display:none
}
</style>
<div>this is a div<div>
  • 思考浏览器渲染一个页面的过程?如果改变其中一个元素的大小,会触发哪些过程?如果只是改变它的背景颜色呢?

# 卡顿杀伤性武器

学习如何使开发者工具找出性能的瓶径处

在移动设备上使用开发者工具

# 任务

录制你的第一个时间线,你可以浏览任何一个网站,利用 Performance 录制帧率,然然后浏览时间线,分析每一帧进行的哪些工作

分析能不能达到 60fps?参看不稳定性是哪个函数导致的?

# JavaScript 优化动画

# 阅读

要达到 60fps,浏览器只有 16ms 时间渲染每一帧,然后浏览器还有额外的工作要做,所以我们应该控制在 10ms 内完成渲染工作

所以一帧的 JavaScript 最长处理时间应该在 3-4ms,因为还有样式计算,布局,绘制和图层合并等工作要做,接下来我们学习怎么样使 JavaScript 不会妨碍我们这一目标

requestAnimation

requestAnimationFrame API 会安排 Javascript 尽早在每一帧的开始执行,从而留给浏览器足够的时间运行代码(样式->布局->绘制->合并)

IE9 不支持 requestAnimationFrame,你需要使用 polyrequestAnimationFrame Polyfill (opens new window):它会替代用 setTimeout 执行动画

webworker

内存管理

# 编码

现在,我们来针对 Javascript 优化页面性能。复制我们给出来样例代码webWorkersTask01.zip (opens new window),然后保存到本地。

网页上半部分展示了一个救护车动画,下半部分实现了一个杨辉三角的生成器,但是网页性能存在一定问题:点击开始救援按钮,救护车动画开始执行,但是这个时候在杨辉三角输入框输入30点击生成按钮,浏览器会开始生成 30 行的杨辉三角,可以看到救护车动画出现的卡顿,甚至停止,这样的用户体验非常不好。请你利用 Web Workers 优化页面性能。

任务要求

  1. 这个网页使用 setInterval 实现了简单的一个救护车动画,请你使用 requestAnimationFrame 重新实现这个动画,并且要兼顾兼容性
  2. 查找导致卡顿的函数:按下 Command+Opiton+I(Mac)或者 Control+shift+I (Windows, Linux) 来打开 chorme 开发者工具,进入 Performance 面板,按下录制按钮录制时间线.然后点击开始救援按钮,在杨辉三角输入框输入30点击生成按钮,等到杨辉三角成功生成相互来之后,停止录制。浏览时间线,找出不稳定性是哪个函数导致的。
  3. 通过 Web Workers 优化性能:将上一步骤定位到的生成杨辉三角的函数,从主线程移出到 web worker 中去,另外生成一个子线程执行杨辉三角算法,从而修复主线程动画卡顿问题。

# 自测问题

  • 使用 setTimeout 和 setInteval 制作动画有什么问题?
  • webworker 有什么作用?

# 样式和布局

样式计算过程将根据 DOM 树,确定每个元素的外观属性,从而生成渲染树,但只会包括需要绘制的元素,任何不需要绘制的元素不会出现在渲染树中。 掌握从样式计算过程中找到性能问题,并且解决这些问题。

更改的元素越多,需要付出的性能代价就越高。

选择器的复杂性也会影响性能,使用 BEM 元素编写选择器

强制同步布局

请求浏览器先运行布局,然后重新计算样式,然后在运行布局就会出现强制同步布局错误

当我们访问某些属性就会导致布局流程 ,您可以查看csstriggers (opens new window)详细了解哪些属性会触发布局流程

原因:任何涉及地理位置的更改.例如位置、尺寸都可能导致强制布局

解决方法:在 JS 阶段先读取布局属性,意味这您将使用上一帧布局,然后批量进行所有的样式修改。这意味着重新计算样式将在帧结束时发生,并处在管道的正确位置

触发了布局就会触发绘制,你需要注意自己修改了哪些属性。动画中尽量避免布局和绘制流程,如果不能避免,你应该掌握如何减少影响

# 任务

请你针对强制同步布局错误进行修复从而优化页面性能,下载样例代码 FSL.zip (opens new window) ,然后保存到本地

任务步骤

  • 按下 Command+Opiton+I(Mac)或者 Control+shift+I (Windows, Linux) 来打开 chorme 开发者工具,进入 Performance 面板,按下录制按钮录制时间线。
  • 滑动滑块控制顶部样例div到合适的宽度 -> 点击设置块宽度按钮,就可以设置下面 1000 个 div 块 和样例 div 的宽度一致,当设置完成后,暂停录制按钮生成时间线
  • 不用分析时间线就可以发现设置宽度的过程很缓慢影响用户体验造成卡顿感,查看时间线,您会发现许多 layout事件上面都有红色的小三角,如图所示 timeline.png (opens new window)
  • 随机点击一个layout事件都显示一个警告WarningForced reflow is a likely performance bottleneck.这就是强制同步布局(FSL)错误,它严重影响了页面性能
  • 请你定位卡顿代码位置,修复强制同步布局错误,完成页面性能的优化

# 自测问题

  • 什么是 BEM?
  • 下面哪一个浏览器定位到元素速度最快?
div.box:not()
  • 什么是强制同步布局错误(FSL)?下面哪段代码会导致?

# 合成和绘制

绘制流程通常是最影响帧率的,现在我们来学会如何优化合成和绘制流程

# 任务

下载performance_task.zip (opens new window),保存到本地

打开 index.html,这个页面你在 PC 端运行可能没什么太大的问题,但是在移动端使用切换动画十分卡顿,请你按照下面步骤优化这个页面性能。

模拟移动端 CPU

首先,在分析页面性能之前,请使用 CPU 节流来模拟页面在移动设备上的表现

  • 打开 chrome 开发者工具面板,单击 performance 选项卡。
  • 单击 Capture Settings 。DevTools 会展示一个面板包含捕获性能指标相关的设置
  • 对于 CPU,选择4x slowdown 。DevTools 会限制您的 CPU ,使其比平时慢 4 倍。
  • 确保启用了如下[屏幕截图]红色框选项。

分析页面性能

按下 Command+Opiton+I(Mac)或者 Control+shift+I (Windows, Linux) 来打开 chorme 开发者工具,进入 Performance 面板,按下录制按钮录制时间线。

查看时间线截图 (opens new window),你会发现掉帧率十分严重,随机选取一个task,查看summary面板,查看性能分析截图 (opens new window),您会发现每一个 task 都触发了布局事件和重绘事件,并且这两个时间也消耗了大量的时间,从而导致页面性能下降。

避免布局事件

分析页面功能,我们知道主要动画效果是页面的切换,请你试试看能不能定位导致卡顿的代码位置

分析代码我们发现页面是采用改变页面元素的 left 属性值实现切换效果,然后这个属性必然会导致布局(layout)和重绘(paint)事件事件发生。 请你使用更加高效的 css transform 属性实现这一个功能,使页面能够避免触发布局(layout)事件,从而提升性能

您可以参考csstrigers.com,查询 css 属性的触发机制。

使用 Paint Flashing 实时查看绘画事件

transform 优化的页面性能之后,我们从绘制层面优化性能。

使用 Paint Flashing 可实时查看页面上的所有绘制事件。每当页面的一部分被重新绘制时,DevTools 都会以绿色勾勒出该部分的轮廓。

开启 Paint Flashing

  • 打开 rendering 选项卡。
    • 如果没有 rendering 选项卡,按 Control+ Shift+P 或 Command+ Shift+ P(Mac)打开命令菜单
    • 开始输入 Rendering 并选择 Show Rendering 。DevTools 在 DevTools 窗口的底部显示 Rendering 选项卡,。
  • 启用 Paint flashing 复选框,如下截图所示。

减小绘制区域

现在,点击导航栏切换页面,重新绘制的部分会用绿色显示,请你优化绘制事件,创建给合适的元素创建图层,使得切换页面时不会触发绘制事件,开启 Paint Flashing 不会再显示绿色。

实现效果如下图所示

# 自测问题

  1. 创建图层有什么好处?
  2. 图层是不是越多越好?

# 相关参考