性能优化体系四要素:指标、监控、性能分析、优化方案。javascript
什么是性能?前端业务开发关注的性能,主要有两个:加载速度、渲染效率。两者通常也合称【性能体验】。css
衡量加载速度的传统指标通常是:首字节、DOMContentLoaded、onLoad。传统指标的问题是,彻底站在技术视角衡量,并不能表明用户实际的体验。目前受业界普遍承认的是谷歌提出的一套面向用户的指标 Progressive Web Metrics:html
通常用 FCP 定义【白屏时间】,用 FMP 定义【首屏时间】。FP 和 FCP 可直接经过浏览器 api 得到,但 FMP 显然没法由浏览器来定义。通用实现的一种基本思路是检测 DOM 节点变化最大的时刻,固然最精确的仍是得靠自定义埋点,毕竟这个指标归根结底是用于帮助开发掌握页面在线上运行的实际情况,也只有开发最清楚页面内容的加载过程。TTI 最好也用自定义埋点,靠技术手段检测会比较麻烦。前端
不过真从用户体验角度看,FP 和 FCP 意义也不是很大,而 FMP 又难以标准化定义,考虑到这些问题,谷歌又提出了三个新的指标:java
这三个指标连同其余通用指标,归结成了一个新项目:web vitals。具体介绍可参考这篇文章:The New Generation of Performance Metrics for Better User Experience。react
在实践中,咱们须要在关键路径上的各个节点添加监控埋点,从 url 发出请求,到 DNS 解析,到服务端处理(若是是服务端渲染的话),到 html 开始加载,到阻塞类资源加载,到业务代码开始执行,到接口返回,到内容正式渲染。这样才有利于分析到底是哪一个环节影响了最终性能。git
张克军的这张图描绘了网页通用的加载模型,在实际业务中一般还存在其余一些环节。好比移动端 hybrid 页面或 RN 页面的容器初始化过程,好比前置校验用户登陆态、获取用户定位等等。github
上述指标只是衡量了单个页面的性能,看不到产品总体的性能,所以大厂基本都在搞一个叫【秒开率】的指标。秒开率是指整个样本集中,某指标小于 1s 的占比。秒开率回答了这样一个问题:整个产品有多少页面的性能指标(通常取 90 分位线)小于 1s。web
渲染效率就是指页面流畅度,看交互动效是否有卡顿(掉帧)。衡量渲染效率的指标主要是 FPS。查看 FPS 最简单的办法有两个,一是用 stats.js,二是在 chrome devtools 中经过 command + shift + p 调出命令窗口,输入 fps 便可调出帧率面板。ajax
因为 UI 线程和 js 线程共用同一个线程,js 任务的运行可能致使 UI 卡顿、卡死,所以浏览器还提供了一个用于监控 long task 的 api。
参考:百度APP流畅度全流程质量监控实践(一) 流畅度现状分析。
一般咱们发现某个页面性能已经不好了,因而一顿专项优化,最后各项指标都达到了预设目标,但问题是怎么保证性能长期不会继续劣化呢?为了知道页面在线上运行的实际性能情况,首先就得把监控体系创建起来,能对采集到的指标数据作各类聚合展现以便随时看到页面指标数据变化,能按期自动生成报表,能对指标异常变化(好比突增、骤降)添加报警。
为了采集性能数据,浏览器提供了一系列专门用于监控性能的 API,例如 navigation-timing、resource-timing、user-timing 等。通用指标可封装 sdk 进行采集上报,sdk 应该同时提供上报自定义测速点的能力。
除了线上监控,性能保障还有赖于事前防控。好比在上线流程,应该有一道性能检测环节(checklist),防止一些明显的、常见的、比较极端的性能劣化场景,好比上了一个特别大的资源而不自知(不当心全量引入了 lodash、加了张没压缩的大图、引入了无用的资源等)。最简单的思路,就是上传到 ligthouse,若是检测得分低于阈值,就直接禁止上线。这种检测既能够在上线前执行,也能够按期循环执行。
一般监控已经能提供比较详细的性能情况数据,不过排查问题时一般还须要一些工具辅助以得到更加细节的信息。
浏览器提供了一系列衡量性能的工具,可查看资源加载时序、分析网页性能、分析代码执行耗时、分析渲染流畅度等。
现代框架通常也提供了运行时的性能分析工具。好比 react profiler,可方便的看到组件级乃至方法级的执行耗时(针对【渲染效率】)。
其余分析工具
性能优化的基本思路,是搞清楚整个加载链路出问题的环节,而后针对性的修复。具体的手段,主要有四种类型:
其实还有一种严格说只能算体验优化的手段,就是从交互上让用户感受得快,好比骨架屏,还有图片的渐进式加载(可参考 how medium does progressive image loading)。
一般来讲,加载资源的总量越小,加载性能越好。针对 web 而言,主要是限制请求资源的体积。单从资源体积的角度看,理想状况是彻底的按需加载,即每时每刻仅加载当前须要的资源。
小学奥数里有个主题叫统筹规划,其中有一类问题就是看如何安排各类事情的执行流,以最小化总时间。时序规划也是相似,最基本的两种思路,就是并发和前置,要么一块儿搞,要么提早搞。
距离规划的基本原理是:传输速度有上限,所以距离越近,时间越短。距离最近的是寄存器/内存,最远的是服务器。
一般可以被缓存而且缓存能起到较大做用的,是不常变化、会被反复用到的静态信息。常常变化的信息,或者不多重复使用的信息,会影响缓存的命中率。要利用缓存,在设计上就须要考虑动静分离,好比与用户状态无关的配置信息和与用户状态相关的动态信息分开使用不一样接口。
为了让常常变化的数据也能使用缓存来提升效率,也有一种思路:每次都先从缓存里取数据,而后每次都发送请求更新缓存,以时间换空间,以 1 次延迟的代价,来提升接口请求的性能。这种策略还有个专门的规范叫 stale-while-revalidate,基于 react hook 实现的网络请求方法库 swr 已经内置了该策略。
前三种思路属于通用思路,好比 CPU 的性能优化也会采用这些思路。而定向优化是指针对环境的特征,提供专门的优化方案。就 Web 而言,特定环境主要是指:浏览器的请求、加载和渲染机制;http 协议;js 引擎。
针对浏览器机制
针对 http 1.1
针对 js 引擎
优化手段有不少,但收益并不相同,像针对 js 引擎的优化,基本只有框架会去作。有明显收益的,主要是缓存、按需加载、减少资源体积、请求并发/前置,可参考淘宝天猫首页性能对赌优化回忆录(连接: pan.baidu.com/s/1mgILtDfD… 提取码: impp),能够说是把这些手段用到了极致。
优化渲染效率,提升交互反馈及时性和动画流畅度,也能够运用和加载性能优化同样的思路。提升动画帧率的一个关键点,是处理动画渲染和 js 的关系,避免在动画期间执行 js 长任务,毕竟 60FPS 意味着每一帧只有 16ms,若是这 16ms 全用来执行 js 了,那么动画必然会因掉帧而出现卡顿。