当前位置: 首页 > news >正文

实时上报停留时长数据:TimeMe.js内置WebSocket通道3步集成教程

实时上报停留时长数据:TimeMe.js内置WebSocket通道3步集成教程

【免费下载链接】TimeMe.jsA JavaScript library to accurately time how long a user views a web page, disregarding idle time and time when the tab or window is minimized.项目地址: https://gitcode.com/gh_mirrors/ti/TimeMe.js

TimeMe.js 是一个专注于「用户页面停留时长统计」的轻量级 JavaScript 库,它能自动剔除用户切走标签页、最小化窗口以及长时间无操作的空闲时间,只统计真实的交互时长。更实用的是,TimeMe.js 内置了 WebSocket 数据上报通道,只需在初始化时传入两个配置项,就能把页面停留时长数据实时推送到你的后端服务器。本文带你用 3 步完成集成,全程不到 10 分钟。

为什么需要"实时"上报停留时长?

传统的停留时长统计通常靠onbeforeunload里的 HTTP 请求上报,但存在两个痛点:

  • 数据丢失:页面关闭瞬间发起的异步请求,浏览器往往来不及发完就终止了,统计数据经常漏报;
  • 数据失真:用户挂着页面去吃饭、切到别的标签页看视频,这些"假时长"都会污染你的统计口径。

TimeMe.js 的思路是:前端持续监听用户的真实活跃状态(鼠标移动、键盘输入、页面可见性变化等,逻辑见 timeme.js 中的listenForUserLeavesOrReturnsEventslistForIdleEvents方法),并在与服务器之间始终保持一条 WebSocket 长连接,在用户离开时把时长数据直接推送出去——连接是提前建好的,所以上报成功率高得多。

第 1 步:引入 TimeMe.js 库

二选一即可,新手推荐 npm 方式:

npm install timeme.js --save

或者直接克隆源码仓库后引用压缩版文件:

git clone https://gitcode.com/gh_mirrors/ti/TimeMe.js

在页面中引入压缩版 timeme.min.js:

<script src="timeme.min.js"></script>

如果你不想用构建工具,仓库根目录的 timeme.js 源码可直接引入;想先看效果的话,可以打开 demo/index.html 体验一个实时走时(并自动暂停)的示例页面。

第 2 步:3 行配置启用 WebSocket 通道

TimeMe.js 的 WebSocket 能力是内置的,不需要你手写任何new WebSocket代码。只要在初始化时多传一个websocketOptions配置即可:

TimeMe.initialize({ currentPageName: "my-home-page", // 当前页面名称,用于区分不同页面 idleTimeoutInSeconds: 30, // 30 秒无操作视为空闲,停止计时 websocketOptions: { websocketHost: "ws://your_host:your_port", // 你的 WebSocket 服务地址 appId: "insert-your-made-up-app-id" // 自定义应用标识 } });

就这 3 行配置,TimeMe.js 会帮你完成全部底层工作(相关实现见 timeme.js 的setUpWebsocket方法):

时机TimeMe.js 自动做的事
页面加载、连接建立立即向服务端发送一条INIT消息,宣告会话开始
用户活跃/空闲切换自动暂停或恢复计时(不占用的时长不计入)
用户关闭页面onbeforeunload时发送INSERT_TIME消息,携带本次停留时长

第 3 步:后端接收消息并落库

你的 WebSocket 服务端只需要处理两种 JSON 消息:

① 会话开始(INIT)

{ "type": "INIT", "appId": "insert-your-made-up-app-id" }

② 时长上报(INSERT_TIME)

{ "type": "INSERT_TIME", "appId": "insert-your-made-up-app-id", "timeOnPageMs": 52340, "pageName": "my-home-page" }

其中timeOnPageMs是用户在该页面的有效活跃毫秒数pageName就是第 2 步里配置的页面名。服务端收到INSERT_TIME后按appId + pageName入库即可。以 Node.js 的 ws 库为例,核心逻辑非常短:

const ws = require("ws"); const server = ws.createServer({ port: 8080 }); server.on("connection", (socket) => { socket.on("message", (raw) => { const msg = JSON.parse(raw); if (msg.type === "INSERT_TIME") { // 把 msg.timeOnPageMs 按 msg.appId / msg.pageName 写入数据库 console.log(msg.pageName, "停留了", msg.timeOnPageMs / 1000, "秒"); } }); });

让数据更准确的 3 个实用技巧

  1. 合理设置空闲阈值idleTimeoutInSeconds默认 30 秒,含义是"用户 30 秒没碰鼠标键盘就停表"。分析长文阅读类页面时,建议调大到 60 秒左右;
  2. 单页应用(SPA)记得切换页面名:路由变化时依次调用TimeMe.stopTimer()TimeMe.setCurrentPageName("new-page")TimeMe.startTimer(),各页时长就不会串到一起;
  3. 用聚合接口做自检TimeMe.getTimeOnAllPagesInSeconds()可以一次性拿到本次会话所有页面的时长列表,方便你在控制台里和后端收到的数据对账。

关于空闲检测与离开/返回回调的完整行为,可参考 tests/tests.js 中的单元测试用例;全部 API 的中文说明见 README.md 的 "API" 章节。

常见问题

Q:不开 WebSocket 行不行?

行。TimeMe.js 本身不强制要求,你也可以在任意事件点调用TimeMe.getTimeOnCurrentPageInSeconds()拿到时长后,自行用 HTTP 上报。但如前文所说,onbeforeunload里的异步 HTTP 请求在部分浏览器中可能发不出去,数据完整性不如内置的 WebSocket 通道。

Q:appId 是做什么用的?

它只是一个你自定义的字符串标识,用于区分不同应用或不同站点的上报数据,TimeMe.js 本身不做校验,服务端按它做数据隔离即可。

总结

步骤你做了什么耗时
① 引入 timeme.min.jsnpm 安装或克隆仓库~1 分钟
② 配置websocketOptions3 行初始化代码~2 分钟
③ 后端解析INSERT_TIME消息一条 JSON 入库~5 分钟

至此,你就拥有了一个"剔除空闲时间 + 实时可靠上报"的停留时长统计方案——对新手来说,这正是把 TimeMe.js 用在生产环境里最快的一条路径 🚀

【免费下载链接】TimeMe.jsA JavaScript library to accurately time how long a user views a web page, disregarding idle time and time when the tab or window is minimized.项目地址: https://gitcode.com/gh_mirrors/ti/TimeMe.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.cnnetsun.cn/news/4248178.html

相关文章:

  • Cushy主题与样式实战:从暗色模式切换到OKLab色彩方案编辑器手把手教程
  • quicktime_video_hack如何建立屏幕流会话:15步USB握手协议逐步图解
  • 基于相似性推理的博弈论方法:多智能体协作中的理性合作策略
  • 【AI大模型实战】手把手教你基于Dify搭建RAG知识库,全程干货,零基础小白也能轻松学会!!
  • 【AI大模型教程】一文讲清支持 MCP 的七大 AI 框架有哪些!零基础小白收藏这一篇就够了!!
  • 从内存数据库走向云原生数据底座,深入理解 SAP HANA Cloud
  • SAR成像全链路解析:从点目标仿真到实测数据处理
  • bravado响应处理完全手册:HttpFuture、超时降级fallback_result与错误捕获最佳实践
  • MPDroid进阶功能:输出设备管理、网络电台与Sticker评分完整攻略
  • extended_text_field 渲染层揭秘:ExtendedRenderEditable 光标定位与桌面端拼音输入修复
  • LRU缓存淘汰机制全揭秘:SDURLCache如何守护你的磁盘容量上限
  • 悟空Agent实战:LLaMA-Factory高危0day漏洞挖掘与修复
  • 大模型推理为什么又长又啰嗦?更多thinking≠更好结果,精准thinking可砍掉一半长度
  • 收藏!小白也能学会!LangChain.js智能体开发指南:10大编排模式详解与实战应用
  • Jeff Dean押注AI4S:从分布式系统到科学发现的基础设施革命
  • IntelliJ IDEA快捷键
  • 【AI大模型】腾讯屠榜MTEB,嵌入模型告别BERT,拥抱LLM
  • 智能体Agent:怎样用自然语言重构数据开发?看完这一篇你就懂了!!
  • 水下鱼类实例分割实战:从COCO数据集到YOLOv8训练全流程
  • 【大模型必备】位置编码终极指南:收藏学习RoPE如何改变Transformer架构
  • 7B扩散LLM,居然能跟671B的DeepSeek V3掰手腕,扩散vs自回归,谁才是未来?
  • C指针进阶:Beej‘s Guide to C指针运算与数组本质完整指南
  • WinDiskWriter:macOS上3步做出Windows启动盘
  • UUIDv1秒变v6的位运算魔法:uuid-creator TimeOrderedCodec源码剖析
  • 极简语言编年史:milliForth、sectorForth 与 sectorLISP 深度对比
  • 大模型应用开发全流程:从初始构思到实验、效果评估和产品化!
  • C语言内存管理
  • 多传感器模块设计指南:从选型到固件的可穿戴实践
  • 什么是Transformer?什么是视觉Transformer?与CNN的比较谁更胜一筹?
  • 【AI大模型】一文讲透 AI Agent 与 AI Workflow 的区别和深度解析:从自动化到智能化的演进