前端性能优化

前端性能优化是一个老生常谈的问题,并且在面试的时候,这个题几乎是面试官非常喜欢的一道题。这篇文章主要是总结自己之前在移动端H5性能优化上的实践和思考。

性能

对于前端页面来说来说,性能优化的点可以分为两大类:加载性能优化和渲染性能优化。加载性能越好,用户就能越快的看到页面,跳失率越低,从商业的角度来看就是收益会越高。渲染性能决定了数据到达用户手机后能够多块的展现,这一块其实还包括了交互性能。

加载性能

静态资源加载

  • 速度。主要就是两点:减小体积、减少请求数。
    • js和css,目前正常的线上应用应该都会压缩代码、合并combo请求。理想是1个css、1个js请求。对于目前大量的SPA,并且基本都会依赖webpack等打包工具,需要注意减少不必要的无用包被打到bundle中,可以利用webpack-bundle-analyzer这个工具进行分析。还有就是充分利用webpack提供的各种分包工具、依赖动态加载、异步路由等等工具实现首屏加载的资源最小化。
    • 图片压缩。tiny.png是一个很好的在线压缩图片的地方。最好是能有一个公共库处理所有图片资源。最简单的比如安卓采用webp。如果cdn支持压缩参数,那必须根据网络环境、设备屏幕大小、图片所占空间的尺寸,来控制图片尺寸、压缩比例。还可以用DataURI、iconfont、svg等方式替换图片,也可以有效减少图片请求
    • 域名收敛。由于目前移动端HTTP/2的支持率已经很高了,所以已经不需要分拆域名了,收敛域名反而能加快资源的加载。期望的效果是:图片一个域名,js和css一个域名、动态请求一个域名。
  • 优先级。
    • 如果是后端渲染的页面,那在dom渲染之前不应该出现任何js,因为渲染DOM的优先级是最高的。如果是SPA等采用前端渲染的应用的话那就另说。
    • css要在head中,但是在移动端要考虑首屏,所以如果css很多的话,需要考虑css分拆,在head中只加载首屏用到的css,非首屏的css可以在后面引入。
    • 减少不必要的DOM。最好的方案应该是首屏直出,后面的DOM用前端渲染。或者使用懒渲染。直出的页面只有占坑的DOM,随着滚动加载对应的DOM。或者对于需要接口数据来渲染的DOM,可以用js来渲染,而不是直接将全部DOM预设在HTML中,影响文档加载时间和DOM渲染时间。
    • 所有图片、视频都必须懒加载。无论是img标签还是背景图片,一律懒加载。因为不懒加载的话,非首屏的图片会在页面进入的时候,严重占用系统资源,影响首屏时间。图片懒加载需要考虑一个问题就是懒加载是由js触发的,所以其执行的时机一定要靠前,因为一开始进入页面的时候,首屏图片由于懒加载时也是没有加载渲染的,所以可以考虑把懒加载的相关逻辑代码直接内嵌在html中,并且在其他的js之前。让首屏的图片尽可能的快加载。如果页面是知道哪些资源是首屏的话,那首屏的图片就不要懒加载了。

网络性能优化

网络层面的优化前端能做的事情不多,主要有以下几个方面:

  • dns-prefetch、preconnect
  • 使用http2
  • native容器代理前端请求。由于容器可以做提前建连等优化,所以如果容器能够代理前端的请求,那请求时间也能减少。

渲染性能

优化CRP(Critical-Rendering-Path)

关键渲染路径:从收到 HTML、CSS 和 JavaScript 字节到对其进行必需的处理,从而将它们转变成渲染的像素这一过程中的一些中间步骤。关于CRP的解释以及优化思路和方法可以参考谷歌官方的博客优化CRP,总的来说就是减少关键资源的数量、关键路径长度、关键字节的数量

优化数据获取速度

需要进行前端渲染的页面都是在前端实现(数据+模板)=> dom 这个过程的。由于生成dom已经够快了,并不是瓶颈,所以优化渲染性能需要解决的就是如何更快的获取到数据

  • 本地硬缓存,这个缓存的粒度可粗可细,可以直接把数据缓存,也可以把渲染后的dom直接缓存了,但这只适用于数据变化不大的页面。
  • 数据预加载。目前预加载需要容器的配合,通过和容器的约定,在请求页面的时候容器同步请求数据,这样在js执行到获取数据的时候,数据其实已经到容器了。

交互体验优化

  • 这个主要是css和dom的渲染速度优化,基本的规则都已经深入人心了,比如利用css3特性提升渲染层、合成层优化、避免reflow|repaint等等
  • 大量动画的时候注意css的性能,避免使用js动画
  • 滚动事件,一般是懒加载或者是需要根据滚动进行不同展现的一些需求,一般滚动都要用throttle处理,不然必然会影响滚动的体验,还有就是绑定事件时使用 passive 参数

性能数据采集

在进行性能优化之前,必须进行性能数据采集这项工作。这个的重要性不言而喻。性能数据采集主要有以下几个问题需要解答:采集什么?如何采集?采集后如何处理?

采集指标

这个问题其实目前并没有完美的解决方案,因为性能是一个感官上的东西,要对他进行量化本来就很难,而且怎么量化的精确、准确就更难了,我所了解的主要有一下几种方案:

  • window.performance.timing
    这个方案最简单、兼容性最好,可以完整的记录整个文档生命周期中的各个时间节点,但是缺陷就是它是描述整个页面的,并不能直接给出比如白屏时间、首屏时间等指标
  • fcp\fmp\fci
    这个是lighthouse的工具使用的一些指标,First Contentful Paint(首次内容渲染)、First Meaningful Paint(首次有效渲染)、First CPU Idle(首次可交互时间)。这些指标是直接面向用户的指标,具体计算方法可以参考谷歌官方解释

采集方法

其实就是如何把采集到的数据上报,有两种方式比较常见:页面关闭时、页面onload后。我的经验是用页面关闭时触发的事件来发送埋点不是非常可靠,比如有的情况是页面不关闭,或者关闭太快js并没有执行,会导致整体的丢包率有点高。所以我采用的方式是onload后触发埋点上报

数据处理

这一块经验不多,推荐博客把前端监控做到极致。目标就是要实现前端性能监控。那就需要数据的实时处理、实时展现。这样才能实现前端性能优化的闭环,发现问题->解决问题->线上监控实时反馈->继续发现问题->…

思考

  • 一直以来web的性能都是一大槽点,也是和native相比最大的劣势。到底web的性能能不能和native接近,甚至超过。之前我一直觉得没可能,因为硬件性能的提升是普惠的,并不是只对web有提升。但是后来看了U4内核研发负责人的分享,感觉还是有那么一点可能的,但是要付出很大的代价,并且肯定是前端、容器、网络、后端一起协同改造优化的结果,只靠前端那点优化手段是不可能的。
  • 优化来优化去,在现在机器性能普遍提升的情况下,除了对动画性能要求高的各种炫酷页面、电商会场、游戏等等,渲染层面对于普通的页面其实已经没有多大的问题了。最大的问题还是在加载,目前虽然普及了4G,但是网络速度还是堪忧,并且网络链路非常复杂,也不好优化。那杀手锏级别的优化手段是什么?强缓存,无论是SW还是其他技术,就是在端(native、浏览器)直接把你的资源缓存在本地,资源加载时间在几十毫秒内,那页面秒开也就好办多了,但是又同时会引来缓存更新、到达率等等的新问题
  • 这篇文章是20180118开始写的,今天才写完。。。

参考资料

前端性能优化清单
chrome文档 分析运行时性能
Web性能优化
chrome文档 使用 Lighthouse 审查网络应用
淘宝FED Composite优化
window.performance attribute
user-centric-performance-metrics
critical-rendering-path