前端实现埋点&监控
实现埋点功能的意义主要体现在以下几个方面:
- 数据采集:埋点是数据采集领域(尤其是用户行为数据采集领域)的术语,它针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。通过埋点,可以收集到用户在应用中的所有行为数据,例如页面浏览、按钮点击、表单提交等。
- 数据分析:采集的数据可以帮助业务人员分析网站或者App的使用情况、用户行为习惯等,是后续建立用户画像、用户行为路径等数据产品的基础。通过数据分析,企业可以更好地了解用户需求,优化产品和服务。
- 改进决策:通过对埋点数据的分析,企业可以了解用户的真实需求和行为习惯,从而做出更符合市场和用户需求的决策,提高产品和服务的质量和竞争力。
- 优化运营:通过埋点数据,企业可以了解用户的兴趣和行为,从而更好地定位目标用户群体,优化运营策略,提高运营效率和收益。
- 预测趋势:通过对埋点数据的分析,企业可以预测市场和用户的未来趋势,从而提前做好准备,把握市场机遇,赢得竞争优势。
总之,实现埋点功能可以帮助企业更好地了解用户需求和行为习惯,优化产品和服务,改进决策,优化运营并预测趋势,具有重要的意义和作用。
常见的埋点包括:pv【PageView】上报(包括history上报、hash上报)、uv【UserView】上报、dom事件上报、js报错上报(包括常规错误上报、Promise报错上报)
下面我们通过使用nodejs、TypeScript、rollup等技术栈实现一个简易的埋点上报的sdk,并发布npm。
通过这篇文章你可以学习到:前端埋点&监控、区分js模块化、打包工具rollup、API之History、JS二进制、sendBeacon发送post请求等知识。
一、前置知识
1. 区分JS模块化
因为要在nodejs环境下并使用rollup打包输出支持不同规范的模块,因此了解JS模块化相关知识是必要的。
主流模块化规范有:
- CommonJS规范
- AMD规范
- CMD规范
- ESM规范
- UMD规范
这里建议看一下这边博客:前端模块化详解(完整版),里面详细讲解了主流模块
下面我们对主流模块做个总结,如下:
| 序号 |
模块化规范 |
备注 |
| 1 |
CommonJS |
CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD和CMD解决方案 |
| 2 |
AMD |
AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅。 |
| 3 |
CMD |
CMD规范整合了CommonJS和AMD规范的特点, CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行。不过,依赖SPM打包,模块的加载逻辑偏重 |
| 4 |
UMD |
UMD是AMD和CommonJS两者的结合,这个模式中加入了当前存在哪种规范的判断,所以能够“通用”,它兼容了AMD和CommonJS,同时还支持老式的“全局”变量规范 |
| 5 |
ESM |
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。 |
2. rollup
要发布npm包,打包库是必然的,而rollup比webpack更适合打包库,因此学习rollup也是必要的。
建议看一下这篇博客:安装 Rollup 以及 Rollup 和 Webpack 的区别
这里总结一下rollup和webpack的区别:
- webpack 由于年代相对久远,在 commonjs 后且 esMoudles 之前,所以通过 webpack 通过自己来实现 commonjs 等语法,rollup 则可以通过配置打包成想要的语法,比如 esm
- 所以说 rollup 很适合打包成 库,而 webpack 比较适合用来做来打包应用
- 由于rollup不能够直接读取node_modules中的依赖项,需要引入加载npm模块的插件:rollup-plugin-node-resolve
- 由于rollup默认只支持esm模块打包,所以需要引入插件来支持cjs模块:rollup-plugin-commonjs
- 由于 rollup 通过可以 esm 模块开发和打包,所以支持 tree-shaking 模式
- vite 就是 rollup 开发而来的
3. History
实现Page View埋点往往需要使用HistoryAPI,因为它可以帮助我们更好地控制页面的状态和导航。
在SPA中,页面的状态通常由内部状态管理,而不是通过URL来表现。因此,传统的PV埋点方法(例如通过document.referrer)可能无法正确计算PV。
使用History API可以让我们更精细地控制页面的导航和状态。我们可以使用history.pushState()方法将新的状态添加到历史记录中,并更新URL,但不会触发页面刷新。这样,我们可以在用户与页面交互时跟踪其导航路径,并计算PV。
另外,当用户点击浏览器的后退按钮时,我们可以使用popstate事件来获取上一个历史记录状态,并根据需要进行处理。这可以帮助我们处理用户在SPA中的导航,并提供更准确的PV数据。
History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录,它的实例方法back()、forward()、go()大家应该都比较熟悉就不一一介绍了,这里主要介绍一下pushState()和replaceState()以及popstate事件,注意:popstate事件并么有使用驼峰命名方式。
3.1 history.pushState()
在HTML文档中,history.pushState()方法向浏览器的会话历史栈增加了一个条目。该方法是异步的。为 popstate 事件增加监听器,以确定导航何时完成。state 参数将在其中可用。
语法:history.pushState(state, title, url)
其中,state 对象是一个 JavaScript 对象,其与通过 pushState() 创建的新历史条目相关联。每当用户导航到新的 state,都会触发 popstate 事件,并且该事件的 state 属性包含历史条目 state 对象的副本。
title标题,由于历史原因是个必填项。url可以是相对路径也可以是绝对路径,浏览器会跳转到对应页面。
对比window.loacation
从某种程度来说,调用 pushState() 类似于 window.location = "#foo"(window.location.hash将变成#foo,hash一般情况下为url后#及其后面一部分组成,这一部分为网页的位置,也称为为锚点),它们都会在当前的文档中创建和激活一个新的历史条目。但是 pushState() 有以下优势:
- 新的 URL 可以是任何和当前 URL 同源的 URL。然而,如果你仅修改 hash,将其设置到 window.location,将使你留在同一文档中。【pushState即使是和当前同源的url也能加一条历史到历史栈】
- 改变页面的 URL 是可选的。相反,设置 window.location = "#foo"; 仅仅会在当前 hash 不是 #foo 情况下,创建一条新的历史条目。
- 你可以使用你的新历史条目关联任意数据。使用基于 hash 的方式,你需要将所有相关的数据编码为一个短字符串。
更详细内容可以看MDN——pushState()方法
3.2 history.replaceState()
replaceState()方法使用state objects, title,和 URL 作为参数,修改当前历史记录实体,如果你想更新当前的 state 对象或者当前历史实体的 URL 来响应用户的的动作的话这个方法将会非常有用。
语法:history.replaceState(stateObj, title[, url])
更详细内容可以看MDN——replaceState()方法
3.3 popstate事件
每当激活同一文档中不同的历史记录条目时,popstate 事件就会在对应的 window 对象上触发。如果当前处于激活状态的历史记录条目是由 history.pushState() 方法创建的或者是由 history.replaceState() 方法修改的,则 popstate 事件的 state 属性包含了这个历史记录条目的 state 对象的一个拷贝。
调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。popstate 事件只会在浏览器某些行为下触发,比如点击后退按钮(或者在 JavaScript 中调用 history.back() 方法) 。即,在同一文档的两个历史记录条目之间导航会触发该事件。
更详细内容可以看MDN——popstate事件
4. JS二进制
JavaScript 提供了一些 API 来处理文件或原始文件数据,例如:File、Blob、FileReader、ArrayBuffer、base64等。它们之间的关系如下:
以下是我对谈谈JS二进制:File、Blob、FileReader、ArrayBuffer、Base64这篇文章的一点小结,详细内容请看原文,建议把原文的代码都敲一遍。
4.1 Blob
Blob(binary large object),即二进制大对象,它是 JavaScript 中的一个对象,表示原始的类似文件的数据。
Blob对象是一个只读不可修改的二进制文件,它的数据可以按文本或二进制的格式进行读取。
创建Blob对象
new Blob(array, option)
其中
- array:由 ArrayBuffer、ArrayBufferView、Blob、DOMString 等对象构成的,将会被放进 Blob
- options:可选的 BlobPropertyBag 字典,它可能会指定如下两个属性。
- type:默认值为 "",表示放入到blob中的数组内容的MIME类型。
- endings:默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入,不常用。
常见MIME类型如下:
示例:以下实现了实例化了一个Blob对象并使用URL.createObjectUrl()方法将其转化为一个URL。
点击这个url可以看到如下结果:

本文介绍了前端实现埋点和监控的重要性和作用,包括数据采集、数据分析、改进决策和优化运营等方面。文章详细讲解了JS模块化、rollup打包工具、History API、JS二进制相关知识,并阐述了如何使用sendBeacon发送请求。通过实例展示了如何实现PV上报、DOM事件监听、错误上报等功能,帮助读者掌握前端埋点的实现方法。
最低0.47元/天 解锁文章
499





