前端埋点实现

2023-11-09

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

介绍

这段时间博主一直在投入组件库的开发工作,最初其实就是想提供一套组件库来使用并且开源,和大家一起学习,最近突然有一个思路,可以从组件库文档页拉取一下用户的数据,来对组件库更好的维护。

组件库的链接在这里

主要思路还是采用的代码埋点,在每个组件的页面挂载的时候注入埋点,进行数据收集、最后提交给后端来进行长保存。

1. 实现自定义hook,监测组件

代码如下:

import { useEffect, useRef } from 'react';
import getUserIp from '../track/getUserIp';
import getNativeBrowserInfo from '../track/getNativeBrowserInfo';
import sendData from '../track/sendData';

const usePageListener = (componentName: string) => {
  //监测单组件文档页面停留时间,进行埋点
  const leaveTime = useRef<number>(0);
  const timer = useRef<any>();

  useEffect(() => {
    //计算页面停留时间
    timer.current = setInterval(() => {
      leaveTime.current = leaveTime.current + 1;
    }, 1000);
    return () => {
      clearInterval(timer.current);
    };
  }, [leaveTime]);

  useEffect((): any => {
    return async () => {
      //组件销毁,如果停留时间大于十秒,发送单组件埋点记录
      console.log('销毁', componentName, leaveTime.current);
      let trackInfo = {
        componentName,
        leaveTime: leaveTime.current,
      };
      const userDeviceInfo = (await getUserIp()) as object; //用户个人相关信息
      const nativeBrowserInfo = (await getNativeBrowserInfo()) as object; //浏览器原生的信息
      trackInfo = { ...trackInfo, ...userDeviceInfo, ...nativeBrowserInfo };
      //将收集到的数据发送给后端
      const result = await sendData(trackInfo);
      return result;
    };
  }, []);
};

export default usePageListener;

usePageListener hook主要有两个阶段:

  1. 组件挂载阶段,进行页面时间监听,计时用户在该页面停留了多少秒。
  2. 组件销毁阶段,停止计时,并开始收集数据,最后发送给后端。

2. 收集数据

我这里主要收集了两类数据,个人相关信息的收集函数getUserIp如下:

const getUserIp = () => {
  return new Promise((resolve, reject) => {
    const scriptElement = document.createElement('script');
    scriptElement.src = `http://pv.sohu.com/cityjson?ie=utf-8`;
    document.body.appendChild(scriptElement);
    scriptElement.onload = () => {
      try {
        document.body.removeChild(scriptElement);
        resolve(returnCitySN);
      } catch (e) {
        reject(e);
      }
    };
  });
};

export default getUserIp;

这里是借助了搜狐的第三方接口来获取用户的IP地址和所在城市。

浏览器原生的数据方法getNativeBrowserInfo如下:

import { getNowTime } from '../getNowTime';

type nativeBrowserInfoType = {
  domain?: string;
  url?: string;
  title?: string;
  referrer?: string;
  screenHeight?: number | string;
  screenWidth?: number | string;
  color?: number;
  lang?: string;
  ua?: string;
  watchTime?: string;
  memory?: string;
  connectTime?: string;
  responseTime?: string;
  renderTime?: string;
};
const formatMemory = (val: number) => {
  //格式化内存数据
  return Math.floor(val / 1024 / 1024) + 'mb';
};
const formatTimeToSecord = (val: number) => {
  //转换为秒
  return val / 1000 + 's';
};
const getNativeBrowserInfo = () => {
  //获取浏览器原生数据
  return new Promise((resolve) => {
    const params: nativeBrowserInfoType = {};
    if (document) {
      params.domain = document.domain || ''; //获取域名
      // params.url = String(document.URL) || ''; //当前Url地址
      params.title = document.title || '';
      // params.referrer = String(document.referrer) || ''; //上一跳路径
    }
    //Window对象数据
    if (window && window.screen) {
      params.screenHeight = window.screen.height || 0; //获取显示屏信息
      params.screenWidth = window.screen.width || 0;
      params.color = window.screen.colorDepth || 0;
    }
    //navigator对象数据
    if (navigator) {
      params.lang = navigator.language || ''; //获取所用语言种类
      params.ua = navigator.userAgent.toLowerCase(); //运行环境
    }
    //获取性能相关参数
    if (window && window.performance) {
      params.memory = formatMemory(window.performance.memory.usedJSHeapSize);
      params.connectTime = formatTimeToSecord(
        window.performance.timing.connectEnd - window.performance.timing.connectStart,
      );
      params.responseTime = formatTimeToSecord(
        window.performance.timing.responseEnd - window.performance.timing.responseStart,
      );
      params.renderTime = formatTimeToSecord(
        window.performance.timing.domComplete - window.performance.timing.domLoading,
      );
    }
    params.watchTime = getNowTime();
    resolve(params);
  });
};

export default getNativeBrowserInfo;

这里是收集了设备、运行环境以及页面加载内存性能相关的数据,也是可以知道h5在每个设备中是否有不一样的加载表现,从而进行针对性优化。

最终收集到的数据样例是这样的:
在这里插入图片描述

3.前端错误捕捉

通过window.onerror方法,捕捉到前端出现的错误信息,并上报给后端,主要是实现代码如下:

import { getNowTime } from '../getNowTime';

type errorList<T> = {
  errorMessage?: T;
  scriptURI?: T;
  errorObj?: T;
  happenURI?: T;
  happenTime?: T;
};
const getErrorInfo = () => {
  const errorList: errorList<string | Event | number | undefined> = {};
  window.onerror = (errorMessage, scriptURI, errorObj) => {
    errorList.errorMessage = errorMessage;
    errorList.scriptURI = scriptURI;
    errorList.happenURI = window.location.href.replace('#', '');
    errorList.errorObj = errorObj;
    errorList.happenTime = getNowTime();
    const sendScript = document.createElement('script');
    const requestAddress = `http://react-view-ui.com:9999/saveErrorMessage?info=${JSON.stringify(
      errorList,
    )}`;
    sendScript.src = requestAddress;
    document.body.appendChild(sendScript);
  };
};

export { getErrorInfo };

这里主要收集了报错信息、报错的页面地址、报错时间等参数,这里我们就可以自己去mock出错误。

4. 发送后端保存数据

数据收集完毕,准备发送给后端,这里是加入了一个script脚本,请求后端,并把收集到的数据传给后端,代码如下:

const sendData = (params: object) => {
  const sendScript = document.createElement('script');
  const requestAddress =
    process.env.NODE_ENV === 'development'
      ? `http://localhost:9999/saveComponentLog?info=${JSON.stringify(params)}`
      : `http://react-view-ui.com:9999/saveComponentLog?info=${JSON.stringify(params)}`;
  sendScript.src = requestAddress;
  sendScript.async = true;
  document.body.appendChild(sendScript);
  return new Promise((resolve, reject) => {
    sendScript.onload = () => {
      try {
        document.body.removeChild(sendScript);
        resolve('');
      } catch (e) {
        reject(e);
      }
    };
  });
};
export default sendData;

至此…埋点就做完了,线上服务器也可以实时获取到用户的数据,保存在数据库中,提供给我进行参考了~哈哈哈

在这里插入图片描述

5.收集数据展示

这里一共有两张表,用户信息表和错误表。

用户信息表:

在这里插入图片描述

错误表:

在这里插入图片描述

总结

最后…留一下React-View-UI的链接~~

Concis组件库线上链接:http://react-view-ui.com:92
github:https://github.com/fengxinhhh/Concis
npm:https://www.npmjs.com/package/concis

开源不易,欢迎学习和体验,喜欢请多多支持,有问题请留言。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

前端埋点实现 的相关文章

  • Sequelize.js 中的自定义或覆盖连接

    我需要使用创建自定义连接条件Sequelize js http sequelizejs com使用 MSSQL 具体来说 我需要加入TableB基于一个COALESCE中的列的值TableA and TableB并最终得到这样的连接条件 L
  • 正则表达式中的“g”标志是什么意思?

    的含义是什么g正则表达式中的标志 之间有什么区别 g and g用于全局搜索 这意味着它将匹配所有出现的情况 通常你还会看到i这意味着忽略大小写 参考 全局 JavaScript MDN https developer mozilla or
  • 在 Javascript 中本地化字符串

    我目前正在使用 resx文件来管理我的 NET 服务器端资源 我正在处理的应用程序还允许开发人员将 JavaScript 插入各种事件处理程序中以进行客户端验证等 对我来说本地化 JavaScript 消息和字符串的最佳方法是什么 理想情况
  • 拖放区缩略图宽度图像大小

    如何更改上传图像的缩略图大小 我在我的javascript中尝试过thumbnailWidth 350 但是这不会增加缩略图大小 而缩略图只是看起来放大了 如何操作图像缩略图大小 HTML section section
  • 获取请求的客户端 IP 地址而不是 Cloudflare 的 IP 地址

    Cloudflare 会更改传入请求的 IP 地址 因为 Cloudflare 是我的网站和互联网之间的中间件 代理 我该怎么办获取请求的初始IP地址 而不是 Cloudflare 的 IP 地址 我听说过mod cloudflare但是这
  • 在 JavaScript 中定位提示弹出窗口

    我有一个如下所示的 JavaScript 提示 我想将提示放在屏幕中心 如何使用 javascript 做到这一点 function showUpdate var x var name prompt Please enter your na
  • AngularJS 输入字段未从控制器内的 setTimeout 更新

    我正在使用 AngularJS 支持的页面 并且我需要在只读输入文本字段内显示正在运行的时钟 与data ng model 为了模拟运行的时钟 我使用了 JavaScript 调度程序setTimeout每 1000 毫秒调用一个函数 该函
  • 从网站存储数据的最简单方法(在服务器端)

    我有一个非常简单的网站 实际上是单页 有一个输入字段和一个按钮 我需要将用户提交的数据存储在服务器端的某个位置 完美的方法可能是简单的文本文件 并在每次单击按钮后附加新行 日志文件也可以 据我了解 JavaScript 本身是不可能的 我在
  • 获得一次性绑定以适用于 ng-if

    这个问题已经被之前问过 https stackoverflow com questions 23969926 angular lazy one time binding for expressions 但我无法让该解决方案发挥作用 所以我想
  • 删除 Laravel Mix 中的临时文件

    我想在 laravel mix 构建期间或之后删除临时构建文件 这是我目前拥有的一些代码 但是del不工作 const mix require laravel mix const del require del compile sass i
  • 如何将当前元素传递给 Knockout.js 绑定中的 Javascript 函数?

    因此 我尝试根据是否选中子复选框 使用 Knockout js 将类添加到元素 为此 我试图通过this作为我的函数的参数 目前 我的精简 DOM 结构如下 tr td td tr
  • 由于固定导航,增加了 FancyBox v2 的顶部和底部边距

    我目前正在开发一个网站 该网站将来将具有响应能力 该网站主要由图像组成 单击这些图像会加载到 FancyBox 中 FancyBox v2 现在具有响应能力 因此可以在屏幕尺寸发生变化时重新调整图像等的大小 作为我设计的一部分 我有两个固定
  • ReferenceError 和全局对象

    在浏览器中的 JavaScript 中window是全局对象 这意味着在全局范围内定义的每个变量都是window 那么为什么我会得到这个结果 console log window foo No error logs undefined co
  • 仅在首页加载时使用 cookie 加载 colorbox

    我尝试了来自网络的参考代码 仅在页面加载时加载特定元素一次 这是示例代码
  • PhantomJS 网页内存消耗?

    是否有一种编程方式 因为我想在运行时自动执行 方式来查看网页在通过 PhantomJs 运行时使用了多少内存 我也在使用 casperjs 如果这有帮助的话 我已经搜索了很多但没有找到任何方法 PhantomJs 使用 QtWebKit 因
  • 显示班级图片 10 秒

    我有下面给出的代码显示9 boxes 其值如下digital time 还有一个班级box002显示digits相当于随机选择的九个盒子的值 box002 can be dragged to digital time starting wi
  • 如何使用 Jest 测试对象键和值的相等性?

    我有一个mapModule我在其中导入组件并导出它们 import ComponentName from components ComponentName export default name ComponentName 我该如何测试ma
  • 数字和小数的输入掩码

    在测试我的程序后 我发现了以下错误 我在 sqlserver 中的表包含 价格数字 6 2 我的程序的用户输入价格 555 00 就很好了 但是当他输入 555555 时 这是错误的 所以我需要指定掩码 其中尾数是可选的 0 到 999 小
  • 在 JSP 或 Javascript 中清除浏览器缓存中的图像

    我正在为一所大学开发JSP Web 应用程序 用户页面中显示人员图片 用户注销后如何从网络浏览器缓存中清除这张图片 使用 JSP 或 JavaScript 那是不可能的 最好的选择是完全禁用相关资源的缓存 创建一个filter https
  • NodeJS:如何获取服务器的端口?

    您经常会看到 Node 的示例 hello world 代码 它创建一个 Http Server 开始侦听端口 然后执行以下操作 console log Server is listening on port 8000 但理想情况下你会想要

随机推荐

  • 电脑主板跳线_电脑哥教你如何接电脑主板跳线

    主板跳线接法详解 图 作为一名新手 要真正从头组装好自己的电脑并不容易 也许你知道CPU应该插哪儿 内存应该插哪儿 但遇到一排排复杂跳线的时候 很多新手都不知道如何下手 钥匙开机其实并不神秘 还记不记得你第一次见到装电脑的时候 JS将CPU
  • stm32f407 usb cdc设备无法启动问题

    最新要做一个项目 要求基于STM32F407实现USB CDC设备 首先想到的就是直接用STM32CUBEMX工具来生成 OK 话不多说 直接上过程 RCC配置 Sys配置 USB OTG FS配置 USB DEVICE配置 时钟配置 然后
  • Windows 下安装并配置Maven

    前言 Maven 翻译为 专家 内行 是Apache下的一个纯Java开发的开源项目 Maven 是一个项目管理工具 可以对Java项目进行构建 依赖管理 Maven是基于项目对象模型 POM project object model 可以
  • 输入阻抗与偏置电流

    对于高阻信号要选用FET运放 高阻信号R2和运放上的高阻并联会影响实际R2电阻值 CMRR 共模抑制比 放大电路对差模信号的电压增益与对共模信号的电压增益之比的绝对值 因为我们要抑制零漂所以共模电压增益越小越好 而差模电压增益越大越好 所以
  • pycharm上已存在某些库,但无法调用的问题解决

    如果出现pycharm上已存在openpyxl 但无法调用的时候 就是pycharm安装openpyxl库时 安装的位置与pycharm中引用python解释器的位置不一致 导致不能调用opentyxl 所以再安装openpyxl时 把项目
  • Python 绝对简明手册

    原文 简述 1 阅读须知 文中使用 gt gt gt 作为会命令行中的输出信息的前缀 对于不清楚用用途的函数可以在解释器下面输入 help 函数名 来获取相关信息 另外 自带的文档和google也是不可少的 2 基本语法 2 1 if el
  • 简历制作讲解

    简历制作讲解 前期假想 简历如同一本书 书大体分为文本结构和文本内容 一 简历文本结构 一 个人信息 必要 二 教育背景 必要 三 自我介绍 可选 四 工作经历 五 项目经历 六 技能评价 二 简历文本内容 一 个人信息 必要 必要信息 姓
  • WebTestClient使用

    介绍 WebTestClient用于测试WebFlux服务器端点的主要入口点 它具有与WebClient非常相似的API 内部大部分调用WebClient实例 主要提供测试上下文 绑定到一个服务 WebTestClient testClie
  • 解决:参考的对象类型不支持尝试的操作。 [已退出进程,代码为 4294967295]

    问题描述 win10系统下运行wsl时候显示错误 参考的对象类型不支持尝试的操作 已退出进程 代码为 4294967295 经过个人测试解决方式为关闭网易UU当前的加速 过一会就恢复正常 不需要重启
  • MySQL架构的Server层的执行过程

    1 连接器 主要负责跟客户端建立连接 获取权限 维持和管理连接 2 查询缓存 优先在缓存中进行查询 如果查到了则直接返回 如果缓存中查询不到 在去数据库中查询 3 解析器 分析器 分析器的工作主要是对要执行的SQL语句进行词法解析 语法解析
  • 基于SpringBoot+Async注解整合多线程

    提示 本文没有使用原生的创建线程方式 默认已掌握创建线程的四种方式 全文基于SpringBoot框架 要求读者掌握SpringBoot操作 本人能力有限 如有遗漏或错误 敬请指正 谢谢 文章目录 其他文章 前言 一 为什么要使用多线程 二
  • 计算机 服装生产管理的变化,服装生产管理概述.doc

    PAGE PAGE 182 目 录 TOC o n h z HYPERLINK l To 第一章 服装生产管理概述 HYPERLINK l To 第一节 服装生产概述 HYPERLINK l To 一 服装生产企业的特点 HYPERLINK
  • Yii 2.0集成七牛云

    背景知识 七牛云就是我们常说的图床 什么是图床 可以简单理解为是一种存储图片资源的服务器 本文基于Yii2简单介绍七牛云的使用 1 首先在七牛云平台创建账户 传送门 2 登陆账户之后 点击头部菜单管理控制台 进入之后 点击左侧菜单存储对象
  • 技术岗-网上测评智力题

    A 逻辑推理 1 你让工人为你工作7天 给工人的回报是一根金条 金条平分成相连的7段 你必须在每天结束时给他们一段金条 如果只许你两次把金条弄断 你如何给你 的工人付费 2 请把一盒蛋糕切成8份 分给8个人 但蛋糕盒里还必须留有一份 3 小
  • Qt Plugin

    问题 创建 Qt 插件 方法 1 QML 插件 1 qmldir plugin dll plugin qml 位于同一目录 目录名和模块名相同 2 错误列表如下 no dir no qmldir module module is not i
  • CUDA C编程3 - 并行性衡量指标

    系列文章目录 文章目录 系列文章目录 前言 一 CUDA C并行性衡量指标介绍 二 案例介绍 1 案例说明 2 案例实现 3 结果分析 总结 参考资料 前言 CUDA编程 就是利用GPU设备的并行计算能力实现程序的高速执行 CUDA内核函数
  • 相关系数R-判定系数R方的matlab实现

    相关系数 判定系数 相关系数是最早由统计学家卡尔 皮尔逊设计的统计指标 是研究变量之间线性相关程度的量 一般用字母 r 表示 由于研究对象的不同 相关系数有多种定义方式 较为常用的是皮尔逊相关系数 相关表和相关图可反映两个变量之间的相互关系
  • Table表格(antd-design组件库)简单使用

    1 Table表格 展示行列数据 2 何时使用 当有大量结构化的数据需要展现时 当需要对数据进行排序 搜索 分页 自定义操作等复杂行为时 组件代码来自 表格 Table Ant Design 3 本地验证前的准备 参考文章 react项目
  • java 正则表达式 pattern_Java—正则表达式(Pattern类和Matcher类)

    正则表达式介绍 正则表达式可以用于对字符串的处理 相当于是一个匹配字符串的模板 主要包含查找 替换 分割 提取等操作 Java中通过Pattern和Matcher类提供对正则的支持 字符处理 特殊字符处理 对于特殊字符 前面都要加上 进行转
  • 前端埋点实现

    您好 如果喜欢我的文章 可以关注我的公众号 量子前端 将不定期关注推送前端好文 前端埋点实践 介绍 1 实现自定义hook 监测组件 2 收集数据 3 前端错误捕捉 4 发送后端保存数据 5 收集数据展示 总结 介绍 这段时间博主一直在投入