前端界面性能优化指南

界面渲染基本流程

前景知识,了解页面是如何渲染出来的,才知道从何处下手优化渲染性能。

像素管道

  • JavaScript 。一般来说,我们会使用 JavaScript 来实现一些视觉变化的效果。比如用 jQuery 的 animate 函数做一个动画、对一个数据集进行排序或者往页面里添加一些 DOM 元素等。当然,除了 JavaScript,还有其他一些常用方法也可以实现视觉变化效果,比如: CSS Animations、Transitions 和 Web Animation API。
  • 样式计算 (Style)。此过程是根据匹配选择器(例如 .headline.nav > .nav__item)计算出哪些元素应用哪些 CSS 规则的过程。从中知道规则之后,将应用规则并计算每个元素的最终样式。
  • 布局 (Layout)。在知道对一个元素应用哪些规则之后,浏览器即可开始计算它要占据的空间大小及其在屏幕的位置。网页的布局模式意味着一个元素可能影响其他元素,例如 <body> 元素的宽度一般会影响其子元素的宽度以及树中各处的节点,因此对于浏览器来说,布局过程是经常发生的。
  • 绘制(Paint)。绘制是填充像素的过程。它涉及绘出文本、颜色、图像、边框和阴影,基本上包括元素的每个可视部分。绘制一般是在多个表面(通常称为层)上完成的。
  • 合成(Composite)。由于页面的各部分可能被绘制到多层,由此它们需要按正确顺序绘制到屏幕上,以便正确渲染页面。对于与另一元素重叠的元素来说,这点特别重要,因为一个错误可能使一个元素错误地出现在另一个元素的上层。

上面每一个步骤都可能会产生渲染性能而造成卡顿,所以,确定代码触发了管道哪一部分十分重要。

页面如何从文件到用户看到的画面

  • 下载html文件以及相关的css文件并解析成Dom Tree
  • Dom+CSS生成Render Tree,也就是Compputed style里的内容;那么干了什么呢?1)解析不显示的dom-node,将其从tree里删除,2)解析伪类的content,插入新的dom-node。注意里面去掉了与页面样式无关的<header><script>标签,因为这些在获取样式之后和dom并没有任何关联。
  • 根据Render Tree + CSS解析出Layout,也就是几何内容、盒子关系。当其中一个环节更改,影响相关元素,并且将影响从上(根)至下(叶子)传递而形成布局变化的过程也被为Reflow ,也就是回流
  • Painting ,这时将Render Tree 光栅化绘制,以便填充为图层

    • 光栅化就是将矢量图形 (左)通过光栅器填充为单个像素,形成位图(右)
    • 那么回到刚才的简易代码,光栅器是如何执行的呢?

    • 最后,绘制出一个图层

  1. Composite,将解析出的图层进行增量渲染。同一图层,即便是微小的改动,也会整个渲染,而组合这些图层便是性能优化中的关键。

帧率优化-Composite

那么影响帧率的关键就在于"改动"都影响了哪些步骤并引起了连锁反应。

像素管道阶段触发表

CSS Triggers

让我们在浏览器拉爆网页性能吧

下面是两个例子介绍如何使用性能检查工具(Performance)调试和修改引起性能问题的代码

我们直接深入代码优化,以github的一个仓库为例子[仓库地址](udacity/news-aggregator at gh-pages (github.com))

跑起代码之后,我们看到的是这样一个界面:

减少无意义的几何改变

  1. 打开F12 ,选择Performance 标签,按Ecs 打开控制台,添加Rendering标签
  1. 滚动录制一段时间线(以前叫TimeLine现在叫Performance),可以看到帧率及其糟糕,有大量的红色标记
  1. 展开渲染细节我们发现有大量的强制布局同步引起的回流(Force synchronous layout -> Force Reflow),而调用堆栈显示是colorizeAndScaleStories 引起的
  1. 进入到colorizeAndScaleStories内部,我们发现有js在实时的更改这dom的宽高

而从上面的触发对比表格里我们知道Width,Height是全触发

这个函数的功能是计算每一条新闻的位置,将屏幕下半截的左边计数圆盘改变颜色、大小并且在改变之后保持行内上下居中。从需求角度讲根本没卵用,但是对页面的性能有极大的伤害,那就直接干掉,然后看效果。

对比刚才每帧一个回流操作简直赏心悦目,帧率也明显提升。

动画尽量使用CSS实现

点击一条消息,查看详情(消息详情像menu一样滑入滑出)

我们看到这里有异常多的Layout引起的回流。

展开细节我们可以看到这里是由一个叫做animate方法引起的。

不看不知道,一看吓一跳,里面竟然用js递减确定位置,还用的是setTimeout,即便是如此也应该用requestAnimationFrame是吧。

  • 过一遍完整逻辑,通过点击消息调用showStory 来调用showStory,点击 图标X调用hideStory,两个都调用animate对left进行加减控制滑块位置。
  • onStoryClick时,bing对应的id到showStory和hideStory,并setTimeout执行,在执行期间,创建一个滑块的Dom插入body(但是没有删除,这会浪费性能和内存)。

解决办法就很简单了:

  • 直接创建一个节点以供选择新闻内容后进行内容填充。
  • 动画改用css实现。

最终结果表示,滑入滑出的动画也能60帧运行了,丝滑无比。

滚动陷阱

元素超出边界是一个很严肃的问题,超出时,滚动会因为未知高度(子>父高度,但是父没计算滚动条,滚动条在别的元素上,所以每次滚动都要repatin)强制重绘(虽然可能仅是composite)

当子级高度大于父级时,通过overflow-y: auto使子级滚动条正常显示在父级;或者用魔法,transform: translateZ(0) 将子级置于独立layer渲染

使用 最底部的元素 bottomElement.scrollIntoView({ behavior: "smooth" }) 尽力规范控制变量,用Performance监听渲染,并选取帧率变化的 825ms区间,选取中间位置的Task最低值

不优化前

优化后

因为不需要重新计算子级高度并进入composite,每次task平均时间降低到1.3毫秒(较深的2毫秒左右时gif和video的影响),Paint总耗时大大降低

总结

在编写交互和动画时,很容易就能触发重排、回流和重绘操作,以造成糟糕的性能体验。熟悉渲染的各个阶段,并且知道如何在各阶段避开错误的写法就能写出流畅丝滑的页面。

资料汇总

相关推荐
m0_748240255 分钟前
前端编程艺术(5)---Vue3(从零基础到项目开发)
前端
m0_7482574614 分钟前
使用Element UI实现前端分页,前端搜索,及el-table表格跨页选择数据,切换分页保留分页数据,限制多选数量
前端·ui·状态模式
m0_7482396317 分钟前
【HTML入门】第十六课 - 网页中的按钮们
前端·html
m0_7482451718 分钟前
Chrome浏览器调用ActiveX控件--allWebOffice控件
前端·chrome
web1508541593523 分钟前
【CSS】css 如何实现固定宽高比
前端·css
常常不爱学习29 分钟前
CSS的颜色表示方式
前端·css
Domain-zhuo38 分钟前
React的功能是什么?
前端·javascript·react native·react.js·前端框架·ecmascript
Jiaberrr1 小时前
微信小程序中 crypto-js 加解密全攻略
前端·javascript·vue.js·微信小程序·小程序
此星光明1 小时前
GEE训练教程——ECMWF/ERA5_LAND/DAILY_AGGR数据的地表温度的时序分析
javascript·gee·图表·温度·时序·摄氏度
cwtlw2 小时前
CSS学习记录11
前端·css·笔记·学习·其他