[TOC]
# 官方指引
https://web.dev/
# 监控指标
![](https://img.kancloud.cn/83/00/83009f11785987b241ea09b4782d3bf2_1764x1023.png)
![](https://img.kancloud.cn/3d/82/3d824c8b9eb3f93ff57a3f8d094d21b9_1555x943.png)
<br>
<br>
首先我们需要知道应该监控些什么呢?有哪些具体的指标?
google 开发者提出了一种 RAIL 模型来衡量应用性能,即:Response、Animation、Idle、Load,分别代表着 web 应用生命周期的四个不同方面。并指出最好的性能指标是:100ms 内响应用户输入;动画或者滚动需在 10ms 内产生下一帧;最大化空闲时间;页面加载时长不超过 5 秒。
[![0](https://tva1.sinaimg.cn/large/006tNbRwgy1gah4zxc779j314w0u07bg.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah4zxc779j314w0u07bg.jpg)
我们可转化为三个方面来看:响应速度、页面稳定性、外部服务调用
响应速度:页面初始访问速度 + 交互响应速度
页面稳定性:页面出错率
外部服务调用:网络请求访问速度
## 页面访问速度:白屏、首屏时间、可交互时间
我们来看看 google 开发者针对用户体验,提出的几个性能指标
[![1](https://tva1.sinaimg.cn/large/006tNbRwgy1gah51k3lg8j31bi0gg45d.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah51k3lg8j31bi0gg45d.jpg)
<br>
这几个指标其实都是根据用户体验,提炼出对应的性能指标
[![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah52b9pkrj30v00bwabn.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah52b9pkrj30v00bwabn.jpg)
### first paint (FP) and first contentful paint (FCP)
首次渲染、首次有内容的渲染
这两个指标浏览器已经标准化了,从 performance 的 The Paint Timing API 可以获取到,一般来说两个时间相同,但也有情况下两者不同。
* PerformanceObserver
在页面html代码顶部加入以下代码,订阅性能事件。
~~~
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const metricName = entry.name;
const time = Math.round(entry.startTime + entry.duration);
console.log(entry);
console.log(metricName + ' : ' + time);
}
});
observer.observe({entryTypes: ['paint']});
~~~
上述代码的输出如下:
![](https://img.kancloud.cn/11/8e/118e3da1d4500bd601435dfe34a10db4_732x94.png)
* performance
下面的代码也是可以的:
~~~
window.performance.getEntriesByType('paint');
~~~
![](https://img.kancloud.cn/80/bc/80bce3c87799164a4d7da3211c2a8d5a_732x111.png)
<br>
<br>
### First meaningful paint (FMP)
首次有效绘制。这是一个很主观的指标。根据业务的不同,每一个网站的有效内容都是不相同的,有效内容就是网页中"主角元素"。对于视频网站而言,主角元素就是视频。对于搜索引擎而言,主角元素就是搜索框。
<br>
google lighthouse 定义的 first meaningful paint:
[https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view](https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view)
<br>
<br>
### Time to interactive(TTI)
可交互时间。用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点。应用可能会因为多种原因而无法响应用户输入:
①页面组件运行所需的JavaScript尚未加载完成。
②耗时较长的任务阻塞主线程
<br>
TTI也是一个较为主观的指标。浏览器并没有为这个指标提供api。不过google提供了polyfill。下面是使用方法。
在页面html顶部加入:
~~~
!function(){if('PerformanceLongTaskTiming' in window){var g=window.__tti={e:[]};
g.o=new PerformanceObserver(function(l){g.e=g.e.concat(l.getEntries())});
g.o.observe({entryTypes:['longtask']})}}();
~~~
install这个polyfill,
~~~
npm install tti-polyfill
~~~
在业务代码中引用:
~~~
import ttiPolyfill from 'tti-polyfill';
...
ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
console.log('tti', tti);
});
...
~~~
> `getFirstConsistentlyInteractive()`方法接受可选的`startTime`配置选项,让您可以指定下限值(您知道您的应用在此之前无法进行交互)。 默认情况下,该 polyfill 使用 DOMContentLoaded 作为开始时间,但通常情况下,使用主角元素呈现的时刻或您知道所有事件侦听器都已添加的时间点这类时间会更准确。
<br>
<br>
### 长任务
浏览器是单线程的,如果长任务过多,那必然会影响着用户响应时长。好的应用需要最大化空闲时间,以保证能最快响应用户的输入。
[![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah53416gmj31bg0kgn9i.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah53416gmj31bg0kgn9i.jpg)
### 页面稳定性:页面出错情况
资源加载错误
JS 执行报错
### 外部服务调用
CGI 耗时
CGI 成功率
CDN 资源耗时
## performance API 应用
<br>
[performance API详解](https://www.kancloud.cn/surahe/front-end-notebook/1304833)
<br>
<br>
基于 performance 我们可以测量如下几个方面:
mark、measure、navigation、resource、paint、frame。
<br>
~~~
let p = window.performance.getEntries();
重定向次数:performance.navigation.redirectCount
JS 资源数量:p.filter(ele => ele.initiatorType === "script").length
CSS 资源数量:p.filter(ele => ele.initiatorType === "css").length
AJAX 请求数量:p.filter(ele => ele.initiatorType === "xmlhttprequest").length
IMG 资源数量:p.filter(ele => ele.initiatorType === "img").length
总资源数量: window.performance.getEntriesByType("resource").length
~~~
<br>
**不重复的耗时时段区分:**
~~~
重定向耗时: redirectEnd - redirectStart
DNS 解析耗时: domainLookupEnd - domainLookupStart
TCP 连接耗时: connectEnd - connectStart
SSL 安全连接耗时: connectEnd - secureConnectionStart
网络请求耗时 (TTFB): responseStart - requestStart
HTML 下载耗时:responseEnd - responseStart
DOM 解析耗时: domInteractive - responseEnd
资源加载耗时: loadEventStart - domContentLoadedEventEnd
~~~
<br>
**其他组合分析:**
~~~
白屏时间: domLoading - fetchStart
粗略首屏时间: loadEventEnd - fetchStart 或者 domInteractive - fetchStart
DOM Ready 时间: domContentLoadEventEnd - fetchStart
页面完全加载时间: loadEventStart - fetchStart
~~~
<br>
**JS 总加载耗时:**
~~~
const p = window.performance.getEntries();let cssR = p.filter(ele => ele.initiatorType === "script");Math.max(...cssR.map((ele) => ele.responseEnd)) - Math.min(...cssR.map((ele) => ele.startTime));
~~~
<br>
**CSS 总加载耗时:**
~~~
const p = window.performance.getEntries();let cssR = p.filter(ele => ele.initiatorType === "css");Math.max(...cssR.map((ele) => ele.responseEnd)) - Math.min(...cssR.map((ele) => ele.startTime));
~~~
<br>
# 如何监控?
在了解了 performance 之后,我们来看看,具体是如何监控的?
<br>
[![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jbytugj31mq0b2q50.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jbytugj31mq0b2q50.jpg)
<br>
总体流程:性能指标收集与数据上报—数据存储—数据聚合—分析展示—告警、报表推送
<br>
这里主要讲述如何收集性能数据。
性能指标收集注意项:1)保证数据的准确性 2)尽量不影响应用的性能
<br>
## 基本性能上报
采集数据:将 performance navagation timing 中的所有点都上报,其余的上报内容可参考 performance 分析一节中截取部分上报。例如:白屏时间,JS 和 CSS 总数,以及加载总时长。
其余可参考的上报:是否有缓存?是否启用 gzip 压缩、页面加载方式。
在收集好性能数据后,即可将数据上报。
那选择什么时机上报?
google 开发者推荐的上报方式:
[![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jncmspj31fw0lgtct.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jncmspj31fw0lgtct.jpg)
<br>
## 首屏时间计算
我们知道首屏时间是一项重要指标,但是又很难从 performance 中拿到,来看下首屏时间计算主要有哪些方式?
[https://web.dev/first-meaningful-paint/](https://web.dev/first-meaningful-paint/)
1)用户自定义打点—最准确的方式(只有用户自己最清楚,什么样的时间才算是首屏加载完成)
2)lighthouse 中使用的是 chrome 渲染过程中记录的 trace event
3)可利用[Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)拿到页面布局节点数目。思想是:获取到当页面具有最大布局变化的时间点
4)aegis 的方法:利用 MutationObserver 接口,监听 document 对象的节点变化。
检查这些变化的节点是否显示在首屏中,若这些节点在首屏中,那当前的时间点即为首屏渲染时间。但是还有首屏内图片的加载时间需要考虑,遍历 performance.getEntries() 拿到的所有图片实体对象,根据图片的初始加载时间和加载完成时间去更新首屏渲染时间。
5)利用 MutationObserver 接口提供了监视对 DOM 树所做更改的能力,是 DOM3 Events 规范的一部分。
方法:在首屏内容模块插入一个 div,利用 Mutation Observer API 监听该 div 的 dom 事件,判断该 div 的高度是否大于 0 或者大于指定值,如果大于了,就表示主要内容已经渲染出来,可计算首屏时间。
6)某个专利:在 loading 状态下循环判断当前页面高度是否大于屏幕高度,若大于,则获取到当前页面的屏幕图像,通过逐像素对比来判断页面渲染是否已满屏。[https://patentimages.storage.googleapis.com/bd/83/3d/f65775c31c7120/CN103324521A.pdf](https://patentimages.storage.googleapis.com/bd/83/3d/f65775c31c7120/CN103324521A.pdf)
7) 引入html2canvas,递归截图并计算9个点的颜色红色通道像素值,前后数据相同,可以上报时间。[如何计算首屏加载时间?](https://www.cnblogs.com/caizhenbo/p/7993533.html)
<br>
<br>
[![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jysgr5j31et0u0h1a.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jysgr5j31et0u0h1a.jpg)
<br>
## 异常上报
1)js error
监听 window.onerror 事件
2)promise reject 的异常
监听 unhandledrejection 事件
~~~
window.addEventListener("unhandledrejection", function (event) {
console.warn("WARNING: Unhandled promise rejection. Shame on you! Reason: "
+ event.reason);
});
~~~
3)资源加载失败
window.addEventListener('error')
4)网络请求失败
重写 window.XMLHttpRequest 和 window.fetch 捕获请求错误
5)iframe 异常
window.frames\[0\].onerror
6)window.console.error
## CGI 上报
大致原理:拦截 ajax 请求
数据存储与聚合
一个用户访问,可能会上报几十条数据,每条数据都是多维度的。即:当前访问时间、平台、网络、ip 等。这些一条条的数据都会被存储到数据库中,然后通过数据分析与聚合,提炼出有意义的数据。例如:某日所有用户的平均访问时长、pv 等。
数据统计分析的方法:平均值统计法、百分位数统计法、样本分布统计法。
# 参考资料
[如何进行 web 性能监控?](http://www.alloyteam.com/2020/01/14184/#prettyPhoto)
[当考虑网页首屏加速的时候,我们在考虑什么](https://segmentfault.com/a/1190000018990133?utm_medium=referral&utm_source=tuicool)
[漫谈前端性能优化](https://juejin.im/post/5a4f09eef265da3e3b7a5399)
[研究首屏时间?你先要知道这几点细节](http://www.alloyteam.com/2016/01/points-about-resource-loading/)
- 第一部分 HTML
- meta
- meta标签
- HTML5
- 2.1 语义
- 2.2 通信
- 2.3 离线&存储
- 2.4 多媒体
- 2.5 3D,图像&效果
- 2.6 性能&集成
- 2.7 设备访问
- SEO
- Canvas
- 压缩图片
- 制作圆角矩形
- 全局属性
- 第二部分 CSS
- CSS原理
- 层叠上下文(stacking context)
- 外边距合并
- 块状格式化上下文(BFC)
- 盒模型
- important
- 样式继承
- 层叠
- 属性值处理流程
- 分辨率
- 视口
- CSS API
- grid(未完成)
- flex
- 选择器
- 3D
- Matrix
- AT规则
- line-height 和 vertical-align
- CSS技术
- 居中
- 响应式布局
- 兼容性
- 移动端适配方案
- CSS应用
- CSS Modules(未完成)
- 分层
- 面向对象CSS(未完成)
- 布局
- 三列布局
- 单列等宽,其他多列自适应均匀
- 多列等高
- 圣杯布局
- 双飞翼布局
- 瀑布流
- 1px问题
- 适配iPhoneX
- 横屏适配
- 图片模糊问题
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 内存空间
- 作用域
- 执行上下文栈
- 变量对象
- 作用域链
- this
- 类型转换
- 闭包(未完成)
- 原型、面向对象
- class和extend
- 继承
- new
- DOM
- Event Loop
- 垃圾回收机制
- 内存泄漏
- 数值存储
- 连等赋值
- 基本类型
- 堆栈溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍历对象属性
- 宽度、高度
- performance
- 位运算
- tostring( ) 与 valueOf( )方法
- JavaScript技术
- 错误
- 异常处理
- 存储
- Cookie与Session
- ES6(未完成)
- Babel转码
- let和const命令
- 变量的解构赋值
- 字符串的扩展
- 正则的扩展
- 数值的扩展
- 数组的扩展
- 函数的扩展
- 对象的扩展
- Symbol
- Set 和 Map 数据结构
- proxy
- Reflect
- module
- AJAX
- ES5
- 严格模式
- JSON
- 数组方法
- 对象方法
- 函数方法
- 服务端推送(未完成)
- JavaScript应用
- 复杂判断
- 3D 全景图
- 重载
- 上传(未完成)
- 上传方式
- 文件格式
- 渲染大量数据
- 图片裁剪
- 斐波那契数列
- 编码
- 数组去重
- 浅拷贝、深拷贝
- instanceof
- 模拟 new
- 防抖
- 节流
- 数组扁平化
- sleep函数
- 模拟bind
- 柯里化
- 零碎知识点
- 第四部分 进阶
- 计算机原理
- 数据结构(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 搜索算法
- 动态规划
- 二叉树
- 浏览器
- 浏览器结构
- 浏览器工作原理
- HTML解析
- CSS解析
- 渲染树构建
- 布局(Layout)
- 渲染
- 浏览器输入 URL 后发生了什么
- 跨域
- 缓存机制
- reflow(回流)和repaint(重绘)
- 渲染层合并
- 编译(未完成)
- Babel
- 设计模式(未完成)
- 函数式编程(未完成)
- 正则表达式(未完成)
- 性能
- 性能分析
- 性能指标
- 首屏加载
- 优化
- 浏览器层面
- HTTP层面
- 代码层面
- 构建层面
- 移动端首屏优化
- 服务器层面
- bigpipe
- 构建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack优化
- Webpack原理
- 实现loader
- 实现plugin
- tapable
- Webpack打包后代码
- rollup.js
- parcel
- 模块化
- ESM
- 安全
- XSS
- CSRF
- 点击劫持
- 中间人攻击
- 密码存储
- 测试(未完成)
- 单元测试
- E2E测试
- 框架测试
- 样式回归测试
- 异步测试
- 自动化测试
- PWA
- PWA官网
- web app manifest
- service worker
- app install banners
- 调试PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 饿了么整理
- 样式
- 技巧
- Vue音乐播放器
- Vue源码
- Virtual Dom
- computed原理
- 数组绑定原理
- 双向绑定
- nextTick
- keep-alive
- 导航守卫
- 组件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 动画(未完成)
- 异常监控、收集(未完成)
- 数据采集
- Sentry
- 贝塞尔曲线
- 视频
- 服务端渲染
- 服务端渲染的利与弊
- Vue SSR
- React SSR
- 客户端
- 离线包
- 第五部分 网络
- 五层协议
- TCP
- UDP
- HTTP
- 方法
- 首部
- 状态码
- 持久连接
- TLS
- content-type
- Redirect
- CSP
- 请求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服务端
- Linux
- Linux命令
- 权限
- XAMPP
- Node.js
- 安装
- Node模块化
- 设置环境变量
- Node的event loop
- 进程
- 全局对象
- 异步IO与事件驱动
- 文件系统
- Node错误处理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服务
- 负载均衡
- 获取用户IP
- 解决跨域
- 适配PC与移动环境
- 简单的访问限制
- 页面内容修改
- 图片处理
- 合并请求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自动化(未完成)
- docker
- 创建CLI
- 持续集成
- 持续交付
- 持续部署
- Jenkins
- 部署与发布
- 远程登录服务器
- 增强服务器安全等级
- 搭建 Nodejs 生产环境
- 配置 Nginx 实现反向代理
- 管理域名解析
- 配置 PM2 一键部署
- 发布上线
- 部署HTTPS
- Node 应用
- 爬虫(未完成)
- 例子
- 反爬虫
- 中间件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源码
- app.js
- config.js
- 消息队列
- RPC
- 性能优化
- 第七部分 总结
- Web服务器
- 目录结构
- 依赖
- 功能
- 代码片段
- 整理
- 知识清单、博客
- 项目、组件、库
- Node代码
- 面试必考
- 91算法
- 第八部分 工作代码总结
- 样式代码
- 框架代码
- 组件代码
- 功能代码
- 通用代码