Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端性能监控之 Performance #44

Open
zenglinan opened this issue Feb 13, 2020 · 0 comments
Open

前端性能监控之 Performance #44

zenglinan opened this issue Feb 13, 2020 · 0 comments

Comments

@zenglinan
Copy link
Owner

zenglinan commented Feb 13, 2020

前端除了实现基本功能之外,还需要做好前端监控,他可以跟踪用户对产品的使用情况,定时上报,可以给产品的后续优化提供方向。

一、Performance.timing API 一览

主要用到的 API 是 window.performance.timing,上面完整记录了页面从重定向到文档装载完成过程的时间戳,先来上两张图:
1581561912(1)
1581561934(1)
上面两张图记录了所有属性的含义,关键是要如何利用这些属性。

二、性能监控指标收集

1. 监控起始时间点

可以用 navigationStartfetchStart ,但前者指的是用户按下回车键或刷新的时间点,而后面的浏览器还会经历卸载原先文档和重定向,这部分的时间花费对我们来说意义不大,所以可以从浏览器准备好 HTTP 请求抓取文档开始,需要注意的是 fetchStart 发生在浏览器嗅探本地缓存之前。

起始点:fetchStart

2. DNS 查询耗时

domainLookupEnd - domainLookupStart,如果使用了 DNS 缓存或采用了持久连接,值为 0

3. 建立连接耗时(HTTPS、TCP)

connectEnd - connectStart,如果采用了持久连接,值为 0

4. 收到HTTP响应的首字节

responseStart - fetchStart,这个指标可以反映出网络和后端处理的整体耗时

5. 白屏时间

白屏时间指页面展示出第一个元素的时间,主要通过看到是 DOM 解析完成的时间,domInteractive - fetchStart

6. 首屏时间

首屏时间指第一屏页面完全展示完毕的时间,loadEventStart - fetchStart

三、Performance 的局限性

在 SPA 应用中,如 Vue 等框架下,我们通常会使用路由,通过无刷新跳转页面,但 Performance 获取的时间在无刷新的情况下是不会更新的。

手动获取

如果需要获取子页面的渲染时间,可以在组件的钩子函数埋点:
mounted - beforeCreate

四、监控数据上报

1. 传统方法

为了避免出现跨域问题,我们用 img 标签发送数据给服务端。

要注意在 页面加载完成后发送,不然会增加页面请求的负担。

window.addEventListener('load', uploadData, false);

function logData() {
  let timing = performance.timing,
    time = {}
    let start = timing.fetchStart // 起始时间
    time.dnsTime = 0
    time.connectTime = 0
    time.whitePaintTime = 0
    time.firstPaintTime = 0

  time.dnsTime = timing.domainLookupEnd - timing.domainLookupStart;
  time.connectTime = timing.connectEnd - timing.connectStart;
  time.whitePaintTime = timing.domInteractive - start;
  time.firstPaintTime = timing.loadEventStart - start;

  let params = ''
  Object.keys(time).map(k => {
    return `${k}=${time[k]}`
  })
  params = time.join('&')

  let img = new Image(1, 1);
    let src = `http://server.cn?time=${encodeURIComponent(params)}`;
    img.src = src;
}

2. 新 api 上传

除了可以用 Image 标签外,我们还可以用 navigator.sendBeacon 发送,他可以保证在文档卸载前将数据发送,最大程度减轻数据上报对页面的负担。
所以我们可以进行一个兼容处理

if(navigator.sendBeacon) {
  navigator.sendBeacon(processData('http://server.cn', time))  // 发送的是 POST 请求
} else {
  window.addEventListener('load', uploadData, false);
}

function processTime() {
  let timing = performance.timing,
    time = {}
    let start = timing.fetchStart // 起始时间
    time.dnsTime = 0
    time.connectTime = 0
    time.whitePaintTime = 0
    time.firstPaintTime = 0

  time.dnsTime = timing.domainLookupEnd - timing.domainLookupStart;
  time.connectTime = timing.connectEnd - timing.connectStart;
  time.whitePaintTime = timing.domInteractive - start;
  time.firstPaintTime = timing.loadEventStart - start;
  return time
}

function logData() {
  let time = processData()
  let params = ''
  Object.keys(time).map(k => {
    return `${k}=${time[k]}`
  })
  params = time.join('&')

  let img = new Image(1, 1);
    let src = `http://server.cn?time=${encodeURIComponent(params)}`;
    img.src = src;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant