window.performance(前端性能监控并进行上报)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: window.performance(前端性能监控并进行上报)

Web Performance提供了可以通过的函数(performance 属性提供)测试当前网页或者 web应用的性能,获取更为精确的原始数据,以毫秒为单位。一般放在window.onload事件中读取各种数据,有些值必须在页面完全加载之后才能得出。


图示


API详解


navigationStart

表示从上一个文档卸载结束时的unix时间戳,如果没有上一个文档,这个值将和fetchStart 相等。


  • redirectStart, redirectEnd

如果页面是由redirect而来,则redirectStart和redirectEnd分别代表redirect开始和结束的时间节点。跳转且是同域名内部的重定向才算,否则值为0。

  • fetchStart:浏览器准
  • fetchStart

浏览器发起任何请求之前的时间值,发生在检查本地缓存之前,在fetchStart和domainLookupStart之间,浏览器会检查当前文档的缓存

  • domainLookupStart domainLookupEnd

分别代表DNS查询的开始和结束时间节点。如果浏览器没有进行DNS查询(比如使用cache),则都等于fetchStart

  • connectStart、connectEnd

分别代表TCP建立连接和连接成功的时间节点。如果浏览器没有进行TCP连接(比如持久化连接webscoket),则两者都等于domainLookupEnd

  • secureConnectionStart

可选。如果页面使用HTTPS,它的值是安全连接握手之前的时刻。如果该属性不可用,则返回undefined。如果该属性可用,但没有使用HTTPS,则返回0。该api位于connectStart、connectEnd之间。

  • requestStart

代表浏览器发起请求的时间节点,请求的方式可以是服务器,缓存,本地资源。

  • responseStart, responseEnd

分别代表浏览器收到从服务器端(或缓存、本地资源)响应回的第一个字节和最后一个字节数据的时刻;

  • -domLoading

代表浏览器开始解析html文档的时间节点。我们知道IE浏览器下的document有readyState属性,domLoading的值就等于readyState改变为loading的时间节点

  • domInteractive

代表浏览器解析html文档的状态为interactive时的事件节点,即完成解析DOM树的时间,Document.readyState变为interactive,并将抛出readystatechange相关事件。此时只是DOM树解析完成,这时候并没有开始加载网页内的资源(外链css、js)。

  • domContentLoadEventStart

代表domContentLoaded事件触发的时间节点:页面文档完全加载并解析完成之后,会触发DOMContentLoaded,Html文档不会等待样式文件,图片文件,自框架页面的加载(load事件可用来检测页面是否完全加载完毕)

  • domContentLoadEventEnd

代表domContentLoaded事件完成的时间节点,此时用户可以对页面进行操作

  • domComplete
  • Html文档完全解析完成的时间节点


为什么使用Image对象.gif文件上报


  • 防止跨域

只要能上报数据,无论是请求GIF文件还是请求其他普通文件(JS)或者是请求接口,服务器端其实并不关心具体的上报方式。

但是图片的src属性并不会跨域,并且同样可以发起请求

  • 防止阻塞页面加载,影响用户体验

通常,创建资源节点后只有将对象注入到浏览器DOM树后,浏览器才会实际发送资源请求。反复操作DOM不仅会引发性能问题,而且载入js/css资源还会阻塞页面渲染,影响用户体验。

但是图片请求例外。构造图片打点不仅不用插入DOM,只要在js中new出Image对象就能发起请求,而且还没有阻塞问题,在没有js的浏览器环境中也能通过img标签正常打点,这是其他类型的资源请求所做不到的

  • 相比PNG/JPG,GIF的体积最小

最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节。同样的响应,GIF可以比BMP节约41%的流量,比PNG节约35%的流量


上报数据封装


performance.js

/* eslint-disable no-console */
import { VueRouter } from 'vue-router/types/router';
import { BaseTrack } from './track';
export class Performance {
    // TODO 注意上报的单位 现在是毫秒
    public static readonly timing = window.performance && window.performance.timing;
    //初始化
    public static init() {
        if (!this.timing) {
            console.warn('当前浏览器不支持performance API');
            return;
        }
        //页面资源加载完后,上报数据
        window.addEventListener('load', () => {
            BaseTrack.track(this.getTimings());
        });
    }
    //自定义计算白屏时间
    public static record(router?: VueRouter) {
        const setFPT = () => {
            if (window.performance && window.performance.now) {
                this.customFPT = window.performance.now();
            }
        };
        return {
            created: () => {
                if (router) {
                    //如果是单页面应用,等路由加载完之后,记录时间
                    router.onReady(() => {
                        setFPT();
                    });
                } else {
                    setFPT();
                }
            },
        };
    }
    //封装上报数据
    public static getTimings(): { [key in string]: number } {
        if (!this.timing) {
            console.warn('当前浏览器不支持performance API');
            return {};
        }
        return {
            redirect: this.getRedirectTime(),
            dns: this.getDnsTime(),
            tcp: this.getTcpTime(),
            ttfb: this.getTimeOfFirstByte(),
            req: this.getReqTime(),
            ppdt: this.getParsePureDomTime(),
            dclt: this.getDomContentLoadTime(),
            fpt: this.getFirstPaintTime(),
            load: this.getLoadTime(),
        };
    }
    private static customFPT: number = 0;
    private static getRedirectTime() {
        // 重定向耗时
        return Performance.timing.redirectEnd - Performance.timing.redirectStart;
    }
    private static getDnsTime() {
        // dns查询耗时
        return Performance.timing.domainLookupEnd - Performance.timing.domainLookupStart;
    }
    private static getTcpTime() {
        // tcp连接耗时
        return Performance.timing.connectEnd - Performance.timing.connectStart;
    }
    private static getTimeOfFirstByte() {
        // 读取页面第一个字节耗时
        return Performance.timing.responseStart - Performance.timing.navigationStart;
    }
    private static getReqTime() {
        // request请求耗时
        return Performance.timing.responseEnd - Performance.timing.responseStart;
    }
    private static getParsePureDomTime() {
        // 解析纯DOM树耗时, 不包含js css等资源的加载和执行
        return Performance.timing.domInteractive - Performance.timing.domLoading;
    }
    private static getDomContentLoadTime() {
        // 页面资源加载耗时, 包含vue, js css等资源的加载和执行
        return Performance.timing.domComplete - Performance.timing.domInteractive;
    }
    private static getFirstPaintTime() {
        // first paint time, 首次渲染时间, 即白屏时间
        // getEntriesByName 浏览器兼容性不好,故有时需要自己去定义
        return Math.round(
            (window.performance.getEntriesByName &&
                window.performance.getEntriesByName('first-paint') &&
                window.performance.getEntriesByName('first-paint')[0] &&
                window.performance.getEntriesByName('first-paint')[0].startTime) ||
                this.customFPT,
        );
    }
    private static getLoadTime() {
        // 页面load总耗时
        return Performance.timing.loadEventStart - Performance.timing.navigationStart;
    }
    private static toSeconds(time: number) {}
}

上报


track.ts

/* eslint-disable no-console */
import queryString from 'query-string';
import { v4 as uuid } from 'uuid';
let image: HTMLImageElement | null;
export class BaseTrack {
    public static track(params: { [key: string]: any }) {
        try {
            // if (process.env.NODE_ENV !== 'production') {
            //     // 非生产环境不上报
            //     return;
            // }
            //封装上报数据参数
            const qs = queryString.stringify({
                timestamp: Date.now(),
                traceId: this.getTraceId(),
                url: location.href,
                ...params,
            });
            //上报
            this.reportByImg(qs);
        } catch (e) {
            console.error(e);
        }
    }
    private static serverUrl: string =
        'https://open-demo-qiuku.cn-beijing.log.aliyuncs.com/logstores/qiuku-demo/track_ua.gif?APIVersion=0.6.0&';
    private static reportByImg(qs: string, retryTimes: number = 3) {
        const retry = () => {
            image = null;
            retryTimes && this.reportByImg(qs, retryTimes - 1);
        };
        return new Promise((resolve) => {
            try {
                image = new Image();
                image.src = this.serverUrl + qs;
                //请求失败,再次请求,默认请求失败后请求最多三次
                image.onerror = () => {
                    retry();
                };
            } catch (e) {
                console.error(e);
            }
        });
    }
    //获取追踪Id,标记是哪个用户
    private static getTraceId() {
        try {
            const traceKey = 'qiuku_track_id';
            let traceId = localStorage.getItem(traceKey);
            if (!traceId) {
                traceId = uuid();
                localStorage.setItem(traceKey, traceId!);
            }
            return traceId;
        } catch (e) {
            return '';
        }
    }
}

main.ts

// import '@babel/polyfill';
import Vue from 'vue';
import App from './App.vue';
import './registerServiceWorker';
import router from './router';
import { Performance } from '../src/utils/performance';
Vue.config.productionTip = false;
//初始化
Performance.init();
//自定义分析dom渲染的时间
Vue.directive('analysis', {
    inserted(el, options) {
        console.log(`${options.value} = ${Date.now() - window.performance.timing.navigationStart}`);
    }
});
new Vue({
  router,
  render: (h) => h(App),
  mixins: [Performance.record(router)],
}).$mount('#app');
目录
相关文章
|
3月前
|
XML 监控 前端开发
WebTracing:如何使用一款SDK实现前端全链路监控
WebTracing:如何使用一款SDK实现前端全链路监控
190 1
|
5月前
|
缓存 监控 前端开发
前端性能监控:从Lighthouse到Real User Monitoring
**前端性能监控关乎用户体验。Lighthouse是自动化审计工具,评估网页性能、最佳实践、可访问性等,通过CLI或Chrome DevTools使用。RUM则实时监控用户与网站互动,收集性能数据。两者结合,从开发到生产环境,全面优化前端性能,包括资源加载、代码优化、网络性能和用户体验。使用Lighthouse和RUM数据,结合CI/CD,持续改进并设定性能预算,采用SSR、Service Worker、Code Splitting等高级策略,确保高性能和用户满意度。**
92 2
|
4月前
|
Web App开发 存储 监控
如何使用 Chrome DevTools 进行前端性能监控和调试?
如何使用 Chrome DevTools 进行前端性能监控和调试?
|
6月前
|
存储 安全 前端开发
PHP医院安全不良事件管理系统源码(AEMS)前端vue2+element+后端laravel8不良事件上报与闭环管理
医院不良事件上报与管理系统结合现代医院管理思路,遵照PDCA全面质量循环管理方法而设计,并在多家大型三甲医院成熟运用。系统从事件上报、基于人、机、料、法 、环的RCA分析、事件整改、效果评估实现了结构化、标准化、智能化的管理和分析,满足医院可追溯化、全流程闭环管理要求,满足等级医院评审细则要求,大力提高医院不良事件上报的效率,保障事件分析的准确性,促进医疗安全的提高,避免同类事件再次发生,改善整个医院医疗安全,从而实现医院安全医疗的目标。
79 3
|
5月前
|
监控 前端开发 JavaScript
|
数据采集 监控 前端开发
前端性能和错误监控(一)
前端性能和错误监控
142 0
|
6月前
|
存储 监控 前端开发
JavaScript手册:公司员工电脑监控软件前端交互的代码设计
在当今信息时代,随着公司对员工电脑活动的监控需求不断增加,前端交互的代码设计变得尤为关键。本手册将深入探讨JavaScript编写的公司员工电脑监控软件监控代码,着重介绍如何设计能够在不引起怀疑的情况下,实现对员工电脑活动的细致监控。
261 2
|
6月前
|
存储 监控 前端开发
前端埋点上报的几种方式
前端埋点上报的几种方式
282 0
|
6月前
|
移动开发 监控 前端开发
HTML5与CSS3教学:美化员工行为监控软件前端页面的代码技巧
在当今信息时代,企业对员工行为的监控成为了一项必不可少的工作。而员工行为监控软件作为一种高效的管理工具,其前端页面的设计和美化显得尤为重要。本文将介绍如何利用HTML5和CSS3技术,优雅地美化员工行为监控软件前端页面的代码技巧。
256 0
|
监控 前端开发 JavaScript
使用JavaScript实现实时报警功能的办公电脑上网监控软件:前端代码
在今天的数字化时代,监控软件已成为许多组织和企业必不可少的一部分,用于保护数据和确保系统的正常运行。本文将介绍如何使用JavaScript编写前端监控软件,包括实时报警功能的实现。我们将探讨一些关键的代码示例,以展示如何构建这样的系统。最后,我们还会讨论如何自动将监控到的数据提交到一个网站。
295 4