旗下品牌:
石家庄网站开发 石家庄网站开发公司

资讯动态

察而思、思而行、行而后语、知行合一

【前端技术】前端实现监控 SDK 的全面解析(二)

发布时间:2024-11-19 热度:

  三、功能拆分

  (一)初始化

  初始化阶段需要获取用户传递过来的相关参数,接着调用初始化函数。在这个初始化函数里,能够注入一些监听事件,以此来达成数据统计的功能。以下是具体的代码实现示例及解析:

  javascript

  // 初始化配置

  function init(options) {

  // ------- 加载配置 ----------

  loadConfig(options);

  }

  // 加载配置

  export function loadConfig(options) {

  const {

  appId, // 系统id

  userId, // 用户id

  reportUrl, // 后端url

  autoTracker, // 自动埋点

  delay, // 延迟和合并上报的功能

  hashPage, // 是否hash录有

  errorReport // 是否开启错误监控

  } = options;

  // --------- appId ----------------

  if (appId) {

  window['_monitor_app_id_'] = appId;

  }

  // --------- userId ----------------

  if (userId) {

  window['_monitor_user_id_'] = userId;

  }

  // --------- 服务端地址 ----------------

  if (reportUrl) {

  window['_monitor_report_url_'] = reportUrl;

  }

  // -------- 合并上报的间隔 ------------

  if (delay) {

  window['_monitor_delay_'] = delay;

  }

  // --------- 是否开启错误监控 ------------

  if (errorReport) {

  errorTrackerReport();

  }

  // --------- 是否开启无痕埋点 ----------

  if (autoTracker) {

  autoTrackerReport();

  }

  // ----------- 路由监听 --------------

  if (hashPage) {

  hashPageTrackerReport(); // hash路由上报

  } else {

  historyPageTrackerReport(); // history路由上报

  }

  }

  (二)错误监控

  前端错误类型多样,不同类型的错误需要采用不同的捕获方式来实现全面监控。

  1. 语法错误

  这类错误通常在开发阶段就能被发现,例如拼写错误、符号使用错误等情况。语法错误没办法通过 try{}catch{} 进行捕获,因为正常在开发流程中就会被排查出来,基本不会发布到线上环境。

  2. 同步错误

  在 JavaScript 同步执行过程中产生的错误属于同步错误,像变量未定义这类情况,此类错误是可以被 try-catch 语句捕获到的。

  3. 异步错误

  在诸如 setTimeout 等函数执行中出现的错误就是异步错误,它无法被 try-catch 捕获,但可以利用 Window.onerror 来进行捕获处理,相较而言这种方式要比 try-catch 方便许多。

  4. Promise 错误

  在 Promise 中,如果使用 catch 语句是可以捕获到异步错误的,然而要是没写 catch 的话,在 Window.onerror 里是没办法捕获到这类错误的。对此,可以在全局添加 unhandledrejection 监听来捕获那些没被捕获到的 Promise 错误。

  5. 资源加载错误

  指的是一些资源文件获取失败的情况,一般通过 Window.addEventListener 来进行捕获操作。

  综合来看,SDK 监控错误主要围绕以上这些类型来实现,try-catch 用于在可预见的情形下监控特定错误,Window.onerror 主要负责捕获那些预料之外的错误(比如异步错误),但对于 Promise 错误和网络错误无法单纯依靠它们捕获,所以需要借助 Window.unhandledrejection 监听捕获 Promise 错误,通过 error 监听捕获资源加载错误,以此达成对各类型错误的全面覆盖。

2023082517472366945.jpg

  (三)用户埋点统计

  埋点是用于监控用户在应用上的各种动作表现的一种手段。

  1. 手动埋点

  需要手动在代码中添加相关的埋点代码,比如当用户点击某个按钮或者提交一个表单时,就在按钮点击事件以及提交事件中添加对应的埋点代码。其优点在于可控性较强,能够根据需求自定义上报具体的数据内容;但缺点也很明显,就是对业务代码的侵入性较大,如果需要在很多地方进行埋点操作,那就得逐个去添加代码了。

  2. 自动埋点

  自动埋点很好地解决了手动埋点的缺点,实现了无需侵入业务代码就能在应用里添加埋点监控的功能。不过它也存在不足,只能上报基本的行为交互信息,没办法上报自定义的数据,而且只要页面中有点击操作,就会向服务器上报,这可能导致上报次数过多,给服务器带来较大压力。同时需要注意,如果在 click 事件中阻止了冒泡行为,自动埋点是无法捕获到的,这种情况下就需要进行手动埋点上报,以确保上报的全面覆盖。

  (四)PV 统计

  PV 即页面浏览量,表示页面被访问的次数。对于非 SPA 页面,只需通过监听 onload 事件就能统计页面的 PV 了。但在 SPA 页面中,路由的切换主要依靠前端来实现,而且单页面切换又分为 hash 路由和 history 路由,这两种路由的实现原理不一样,所以要分别针对它们实现不同的数据采集方式。

  1. history 路由

  history 路由是依赖全局对象 history 来实现的,它包含诸如 history.back()(返回上一页,对应浏览器回退操作)、history.forward()(前进一页,即浏览器前进操作)、history.go()(跳转历史中某一页)、history.pushState()(添加新记录)、history.replaceState()(修改当前记录)等方法。其中 pushState 和 replaceState 这两个方法不能被 popstate 监听到,因此需要对这两个方法进行重写,并添加自定义事件监听来实现数据采集,以下是具体的代码实现示例及解析:

  javascript

  import { lazyReport } from './report';

  // history路由监听

  export function historyPageTrackerReport() {

  let beforeTime = Date.now(); // 进入页面的时间

  let beforePage = ''; // 上一个页面

  // 获取在某个页面的停留时间

  function getStayTime() {

  let curTime = Date.now();

  let stayTime = curTime - beforeTime;

  beforeTime = curTime;

  return stayTime;

  }

  // 重写pushState和replaceState方法

  const createHistoryEvent = function (name) {

  // 拿到原来的处理方法

  const origin = window.history[name];

  return function(event) {

  let res = origin.apply(this, arguments);

  let e = new Event(name);

  e.arguments = arguments;

  window.dispatchEvent(e);

  return res;

  };

  };

  // history.pushState

  window.addEventListener('pushState', function () {

  listener()

  });

  // history.replaceState

  window.addEventListener('replaceState', function () {

  listener()

  });

  window.history.pushState = createHistoryEvent('pushState');

  window.history.replaceState = createHistoryEvent('replaceState');

  function listener() {

  const stayTime = getStayTime(); // 停留时间

  const currentPage = window.location.href; // 页面路径

  lazyReport('visit', {

  stayTime,

  page: beforePage,

  })

  beforePage = currentPage;

  }

  // 页面load监听

  window.addEventListener('load', function () {

  // beforePage = location.href;

  listener()

  });

  // unload监听

  window.addEventListener('unload', function () {

  listener()

  });

  // history.go()、history.back()、history.forward() 监听

  window.addEventListener('popstate', function () {

  listener()

  });

  }

  2. hash 路由

  在 hash 路由中,url 里的 hash 值发生变化时会触发 hashChange 的监听,所以只需在全局添加一个监听函数,然后在这个函数里实现数据采集上报就可以了。不过在 React 和 Vue 等框架中,hash 路由的跳转有时候是通过 pushState 实现的,所以还需要加上对 pushState 的监听,以下是具体代码示例及解析:

  javascript

  // hash路由监听

  export function hashPageTrackerReport() {

  let beforeTime = Date.now(); // 进入页面的时间

  let beforePage = ''; // 上一个页面

  function getStayTime() {

  let curTime = Date.now();

  let stayTime = curTime - beforeTime; //当前时间 - 进入时间

  beforeTime = curTime;

  return stayTime;

  }

  function listener() {

  const stayTime = getStayTime();

  const currentPage = window.location.href;

  lazyReport('visit', {

  stayTime,

  page: beforePage,

  })

  beforePage = currentPage;

  }

  // hash路由监听

  window.addEventListener('hashchange', function () {

  listener()

  });

  // 页面load监听

  window.addEventListener('load', function () {

  listener()

  });

  const createHistoryEvent = function (name) {

  const origin = window.history[name];

  return function(event) {

  //自定义事件

  let res = origin.apply(this, arguments);

  let e = new Event(name);

  e.arguments = arguments;

  window.dispatchEvent(e);

  return res;

  };

  };

  window.history.pushState = createHistoryEvent('pushState');

  // history.pushState

  window.addEventListener('pushState', function () {

  listener()

  });

  }

  (五)UV 统计

  UV 统计相对来说较为简单,只需在 SDK 初始化时上报一条消息即可完成相关数据的收集。

  四、数据上报方式

  (一)xhr 接口请求

  采用接口请求的方式来上报数据,其原理和其他业务请求类似,只不过传递的数据是埋点相关的数据。但这种方式存在一些问题,一方面,通常公司里处理埋点的服务器和处理业务逻辑的服务器并非同一台,所以往往需要手动去解决跨域问题;另一方面,如果在上报过程中出现页面刷新或者重新打开页面的情况,很可能会造成埋点数据的缺失,因此传统的 xhr 接口请求方式在适应埋点需求方面存在一定局限性。

  (二)img 标签

  利用 img 标签上报数据,是将埋点数据伪装成图片 url 的请求形式,这样做的好处是能够避免跨域问题。然而,浏览器对 url 的长度是有限制的,所以这种方式不太适合大数据量的上报,而且同样存在刷新或重新打开页面时数据丢失的问题。

  (三)sendBeacon

  这种上报方式不会出现跨域问题,也不存在刷新或重新打开页面导致的数据丢失情况,但它有兼容性方面的问题。在日常开发中,通常会采用 sendBeacon 上报和 img 标签上报相结合的方式,以此来兼顾各种情况,确保数据上报的有效性和稳定性。以下是具体的上报函数代码示例:

  javascript

  // 上报

  export function report(data) {

  const url = window['_monitor_report_url_'];

  // ------- fetch方式上报 -------

  // 跨域问题

  // fetch(url, {

  // method: 'POST',

  // body: JSON.stringify(data),

  // headers: {

  // 'Content-Type': 'application/json',

  // },

  // }).then(res => {

  // console.log(res);

  // }).catch(err => {

  // console.error(err);

  // })

  // ------- navigator/img方式上报 -------

  // 不会有跨域问题

  if (navigator.sendBeacon) { // 支持sendBeacon的浏览器

  navigator.sendBeacon(url, JSON.stringify(data));

  } else { // 不支持sendBeacon的浏览器

  let oImage = new Image();

  oImage.src = `${url}?logs=${data}`;

  }

  clearCache();

  }

  通过上述对前端实现监控 SDK 的介绍,涵盖了能拆分到数据上报环节,旨在帮助开发人员更好地构建和运用监控系统,以保障前端应用的稳定运行以及对用户行为等方面的有效洞察。


联系尚武科技
客户服务
石家庄APP开发
400-666-4864
为您提供售前购买咨询、解决方案推荐等1V1服务!
技术支持及售后
石家庄APP开发公司
0311-83796180
为您提供从产品到服务的全面技术支持 !
客户服务
石家庄小程序开发
石家庄小程序开发公司
加我企业微信
为您提供售前购买咨询、
解决方案推荐等1V1服务!
石家庄网站建设公司
咨询相关问题或预约面谈,可以通过以下方式与我们联系。
石家庄网站制作
在线联系:
石家庄Web开发
石家庄软件开发
石家庄软件开发公司
ADD/地址:
河北·石家庄
新华区西三庄大街86号河北互联网大厦B座二层
Copyright © 2008-2024尚武科技 保留所有权利。 冀ICP备12011207号-2 石家庄网站开发冀公网安备 13010502001294号
Copyright © 2024 www.sw-tech.cn, Inc. All rights reserved