使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包
使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包
前端监控,埋点,数据收集,性能监控
Beacon API
优点,请求发送是非阻塞的 post ,用户体验好;支持多种数据格式;
缺点,IE 不支持,使用 XHR 作为 fallback 方案
"use strict";
/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2020-01-01
* @modified
*
* @description
* @difficulty Easy Medium Hard
* @complexity O(n)
* @augments
* @example
* @link
* @solutions
*
* @best_solutions
*
*/
const log = console.log;
let startTime = performance.now();
window.addEventListener(`load`, (e) => {
log(`window load`);
log(`page is fully loaded`);
});
window.addEventListener(`DOMContentLoaded`, (e) => {
log(`window DOMContentLoaded`);
// log(`DOM fully loaded and parsed`);
});
const logVisit = (url = ``) => {
// Test that we have support
if (!navigator.sendBeacon) {
// XHR fallback
return true;
} else {
// URL to send the data to, e.g.
// let url = `/api/log`;
// Data to send
let data = new FormData();
data.append(`start`, startTime);
data.append(`end`, performance.now());
data.append(`url`, document.URL);
// Let`s go!
navigator.sendBeacon(url, data);
}
};
// 将日志记录封装到一个函数中,则可以在页面卸载时调用它。
// good place for sendBeacon ???
window.addEventListener(`beforeunload`, (e) => {
log(`window beforeunload`);
// bad place for sendBeacon
logVisit(`/api/log`);
});
window.addEventListener(`unload`, (e) => {
log(`window unload`);
// bad place for sendBeacon
// logVisit(`/api/log`);
});
// navigator.sendBeacon(`https://www.xgqfrms.xyz/`, `hello `);
W3C & Beacon
W3C Candidate Recommendation 13 April 2017
W3C Editor's Draft 30 January 2020
demos
test
异常关闭浏览器窗口(强制退出 Chrome 进程),验证请求是否会发送成功?
KOA server
React client
WebSocket
load & DOMContentLoaded
https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event
readystate: interactive
DOMContentLoaded
readystate: complete
load
window.addEventListener('load', (event) => {
log.textContent = log.textContent + 'load\n';
});
document.addEventListener('readystatechange', (event) => {
log.textContent = log.textContent + `readystate: ${document.readyState}\n`;
});
document.addEventListener('DOMContentLoaded', (event) => {
log.textContent = log.textContent + `DOMContentLoaded\n`;
});
visibilitychange & pagehide
https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event
"use strict";
/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2020-11-11
* @modified
*
* @description
* @difficulty Easy Medium Hard
* @complexity O(n)
* @augments
* @example
* @link
* @solutions
*
* @best_solutions
*
*/
const log = console.log;
let startTime = performance.now();
window.addEventListener(`load`, (e) => {
log(`window load`);
log(`page is fully loaded`);
});
window.addEventListener(`DOMContentLoaded`, (e) => {
log(`window DOMContentLoaded`);
// log(`DOM fully loaded and parsed`);
});
const logVisit = (url = ``) => {
// Test that we have support
if (!navigator.sendBeacon) {
// XHR fallback
return true;
} else {
// URL to send the data to, e.g.
// let url = `/api/log`;
// Data to send
let data = new FormData();
data.append(`start`, startTime);
data.append(`end`, performance.now());
data.append(`url`, document.URL);
// Let`s go!
navigator.sendBeacon(url, data);
}
};
// 将日志记录封装到一个函数中,则可以在页面卸载时调用它。
window.addEventListener(`pagehide`, (e) => {
log(`window beforeunload`);
// good place for sendBeacon
logVisit(`/api/log`);
if (event.persisted) {
/* the page isn't being discarded, so it can be reused later */
}
}, false);
document.addEventListener(`visibilitychange`, (e) => {
// window.addEventListener(`visibilitychange`, (e) => {
log(`document.visibilityState`, document.visibilityState);
// if (document.visibilityState === `hidden`) {
// if (document.visibilityState === `visible`) {
// backgroundMusic.play();
// } else {
// backgroundMusic.pause();
// }
// log(`window visibilitychange`);
// good place for sendBeacon
logVisit(`/api/log`);
});
window.addEventListener(`beforeunload`, (e) => {
log(`window beforeunload`);
// bad place for sendBeacon
// logVisit(`/api/log`);
});
window.addEventListener(`unload`, (e) => {
log(`window unload`);
// bad place for sendBeacon
// logVisit(`/api/log`);
});
// navigator.sendBeacon(`https://www.xgqfrms.xyz/`, `hello `);
https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
window.addEventListener("pagehide", event => {
if (event.persisted) {
/* the page isn't being discarded, so it can be reused later */
}
}, false);
beforeunload & unload
https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
window.addEventListener('beforeunload', (event) => {
// Cancel the event as stated by the standard.
event.preventDefault();
// Older browsers supported custom message
event.returnValue = '';
});
https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event
refs
MDN
https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API
https://developer.mozilla.org/en-US/docs/Glossary/beacon
https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
xgqfrms 2012-2020
www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!
使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包的更多相关文章
- 使用 js 实现一个简易版的 GIPHY 动图搜索 web 应用程序
使用 js 实现一个简易版的 GIPHY 动图搜索 web 应用程序 具有挑战性的前端面试题 API JAMstack refs https://www.infoq.cn/article/0NUjpx ...
- 使用 js 实现一个简易版的模版引擎
使用 js 实现一个简易版的模版引擎 regex (function test() { this.str = str; })( window.Test = ...; format() { let ar ...
- 使用 js 实现一个简易版的 drag & drop 库
使用 js 实现一个简易版的 drag & drop 库 具有挑战性的前端面试题 H5 DnD js refs https://www.infoq.cn/article/0NUjpxGrqRX ...
- 使用 js 实现一个简易版的动画库
使用 js 实现一个简易版的动画库 具有挑战性的前端面试题 animation css refs https://www.infoq.cn/article/0NUjpxGrqRX6Ss01BLLE x ...
- 使用 js 实现一个简易版的 async 库
使用 js 实现一个简易版的 async 库 具有挑战性的前端面试题 series & parallel 串行,并行 refs https://www.infoq.cn/article/0NU ...
- 使用 js 实现一个简易版的 vue 框架
使用 js 实现一个简易版的 vue 框架 具有挑战性的前端面试题 refs https://www.infoq.cn/article/0NUjpxGrqRX6Ss01BLLE xgqfrms 201 ...
- .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”
FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...
- DI 原理解析 并实现一个简易版 DI 容器
本文基于自身理解进行输出,目的在于交流学习,如有不对,还望各位看官指出. DI DI-Dependency Injection,即"依赖注入":对象之间依赖关系由容器在运行期决定, ...
- 依赖注入[5]: 创建一个简易版的DI框架[下篇]
为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...
随机推荐
- Jmeter如何录制APP客户端脚本
简单五步教大家Jmeter录制APP客户端脚本: Step1 右键单击该测试计划,选择"添加"-"线程组",添加一个线程组. Step2 为了录制客户端的操作, ...
- Qt QMenuBar和QMenu以及QAction巧妙的使用方法
这里简单介绍QMenuBar和QMenu以及QAction是什么,其详细功能本文不做介绍,如果还不了解的朋友可以查阅Qt的帮助手册或浏览其它相关博客.如下图,软件中蓝色条框是QMenuBar用来承载Q ...
- PostgreSQL创建只读权限的用户
1.创建只读角色 CREATE ROLE readaccess; 2.授予对现有表的访问权限 GRANT USAGE ON SCHEMA public TO readaccess; GRANT SEL ...
- P6584 重拳出击
写在前面 来给 zrm 大佬的题写一篇题解. 这题代码实现难度不高,但是比较锻炼思维,而且应该有不少种解法.着实是一道质量很高的题目. 算法思路 首先呢,显然当小 Z 向当前节点的一棵子树走去时,这棵 ...
- Language Guide (proto3) | proto3 语言指南(九)Oneof结构
Oneof - Oneof结构 如果消息包含多个字段,并且最多只能同时设置一个字段,则可以使用oneof功能强制执行此行为并节省内存. oneof字段与常规字段类似,但oneof共享内存中的所有字段除 ...
- SSH密码暴力破解及防御实战
SSH密码暴力破解及防御实战 一.Hydra(海德拉) 1.1 指定用户破解 二.Medusa(美杜莎) 2.1 语法参数 2.2 破解SSH密码 三.Patator 3.1 破解SSH密码 四.Br ...
- Spring Boot RestTemplate文件上传
@ResponseBody @RequestMapping(value = "/upload.do", method = RequestMethod.POST) public St ...
- Java基本类型的内存分配在栈还是堆
我们都知道在Java里面new出来的对象都是在堆上分配空间存储的,但是针对基本类型却有所区别,基本类型可以分配在栈上,也可以分配在堆上,这是为什么? 在这之前,我们先看下Java的基本类型8种分别是: ...
- CyclicBarrier---可重用的循环栅栏
很多文章只是提了下可重用,具体这个栅栏怎么可重用的,很多没有说明,这里会解开你的疑惑. CyclicBarrier是一个同步工具类,它允许一组线程互相等待,直到达到某个公共屏障点.与CountDown ...
- Codeforces Global Round 11 A. Avoiding Zero(前缀和)
题目链接:https://codeforces.com/contest/1427/problem/A 题意 将 \(n\) 个数重新排列使得不存在为 \(0\) 的前缀和. 题解 计算正.负前缀和,如 ...