vue 数据埋点

最近菜鸟做项目,需要做简单的数据埋点,不是企业级的,反正看渡一的视频,企业级特别复杂,包括但不限于:错误收集、点击地方、用户行为……

菜鸟的需求就是简单收集一下用户的ip、地址、每个界面的访问时间,而且面向的是全球用户,所以自己简单写了。

文章目录

  • 一、获取IP、地址
  • 二、数据埋点
    • 解决传入的是跳转后的界面
    • 解决监听不到关闭网页的问题
  • 三、按钮埋点

一、获取IP、地址

这里菜鸟最开始想到的就是高德、腾讯地图这样的平台,里面都是有ip逆向解析的,但是菜鸟一看高德的api,发现不支持国外!!!
在这里插入图片描述
然后百度虽然支持,但是也不是很准确,最重要的一点是总是提示我跨域了,不知道咋解决!!!
在这里插入图片描述
所以菜鸟就在网上找到一个免费且比较准确的

https://ipinfo.io/account/profile

反正电脑不是手机,定位确实不好搞,反正菜鸟感觉定位的地方就是离你最近的转发基站的位置,而不是你本来的位置!

希望有知道可以精确定位的读者可以 指点江山,激扬文字

二、数据埋点

这里菜鸟最开始想的是 vue3 的 mixins,发现 vue3 不推荐 mixins,但是可以直接使用组合式 API 的组合式函数,就相当于把mixins当函数提出去,然后需要用到的时候直接引入就行!

然后菜鸟写的代码就是这样的了:
src/mixins/pagetime.js

/**
 * 书写界面停留时间函数
 */
import { onBeforeUnmount } from "vue";
import { DataburialpointApi } from "@/network/api";
import { useRoute } from "vue-router";

// 按照惯例,组合式函数名以“use”开头
export function usePageTime() {
  const _routeObj = {
    "/content": "首页",
    "/services/0": "NGS- Whole Genome Sequencing",
    "/services/1": "NGS- mRNA Sequencing",
    "/services/2": "NGS- Small RNA Sequencing",
    "/services/3": "NGS-Lnc RNA Sequencing",
    "/services/4": "NGS- Metagenome Sequencing",
    "/services/5": "NGS- Microbial diversity Sequencing",
    "/services/6": "NGS- Hi-C Sequencing",
    "/services/7": "Nanopore- Nanopore Ultra-long Sequencing",
    "/services/8": "Nanopore- Nanopore Sequencing",
    "/services/9": "Nanopore- Direct RNA Sequencing",
    "/services/10": "Nanopore- Lnc RNA Sequencing",
    "/services/11": "Nanopore- CircRNA Sequencing",
    "/services/12": "Nanopore- TAIL Iso Sequencing",
    "/services/13": "Nanopore- Isoform Sequencing",
    "/services/14": "Nanopore- Direct-CDNA Sequencing",
    "/services/15": "Nanopore- Single-cell full-length transcriptome Sequencing",
    "/services/16": "Nanopore- Full-length 16S/18S/ITS Amplicon Sequencing",
    "/services/17": "Nanopore- 16S-23S Amplicon Metagenomic Sequencing",
    "/services/18": "Nanopore- Metagenome Sequencing",
    "/services/19": "Nanopore- PORE-C Sequencing",
    "/services/20": "Pacbio Revio- Revio Sequencing",
    "/resources/sample": "资源-样本要求",
    "/about": "关于我们",
    "/contactus": "联系我们",
  };
  let _startTime = new Date();
  let _useTime = 0;
  // 路由菜单相关
  let route = useRoute();
  onBeforeUnmount(() => {
    _useTime = new Date() - _startTime;
    console.log(_routeObj[route.path]);
    console.log(_useTime);
    let _formdata = {};
    _formdata.visitorId = localStorage.getItem("userId");
    _formdata.url = route.path;
    _formdata.classify = _routeObj[route.path];
    _formdata.time = _useTime;
    DataburialpointApi(_formdata)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });
  });
}

结果压根不靠谱,这样发送给后端的是跳转后的界面,而不是跳转前的,所以每次都是把上一个界面的访问时间和新跳转的界面发送过去了!!!而且还有一个bug 就是别人不切换界面,那我就不知道别人上一个界面访问了多久,且当别人访问一个界面后,直接点击关闭标签,也无法知道这个界面访问了多久

接下来一个一个解决!

解决传入的是跳转后的界面

菜鸟发现app.vue里有监听路由,那岂不是可以监听路由改变的时候发一个emitter,然后这里监听到了,取旧的值不就行了?于是代码变成了这样:
src/mixins/pagetime.js

/**
 * 书写界面停留时间函数
 */
import { ref, onBeforeUnmount } from "vue";
import { DataburialpointApi } from "@/network/api";
import emitter from "@/tools/eventBus";

// 按照惯例,组合式函数名以“use”开头
export function usePageTime() {
  const _routeObj = {
    "/content": "首页",
    "/services/0": "NGS- Whole Genome Sequencing",
    "/services/1": "NGS- mRNA Sequencing",
    "/services/2": "NGS- Small RNA Sequencing",
    "/services/3": "NGS-Lnc RNA Sequencing",
    "/services/4": "NGS- Metagenome Sequencing",
    "/services/5": "NGS- Microbial diversity Sequencing",
    "/services/6": "NGS- Hi-C Sequencing",
    "/services/7": "Nanopore- Nanopore Ultra-long Sequencing",
    "/services/8": "Nanopore- Nanopore Sequencing",
    "/services/9": "Nanopore- Direct RNA Sequencing",
    "/services/10": "Nanopore- Lnc RNA Sequencing",
    "/services/11": "Nanopore- CircRNA Sequencing",
    "/services/12": "Nanopore- TAIL Iso Sequencing",
    "/services/13": "Nanopore- Isoform Sequencing",
    "/services/14": "Nanopore- Direct-CDNA Sequencing",
    "/services/15": "Nanopore- Single-cell full-length transcriptome Sequencing",
    "/services/16": "Nanopore- Full-length 16S/18S/ITS Amplicon Sequencing",
    "/services/17": "Nanopore- 16S-23S Amplicon Metagenomic Sequencing",
    "/services/18": "Nanopore- Metagenome Sequencing",
    "/services/19": "Nanopore- PORE-C Sequencing",
    "/services/20": "Pacbio Revio- Revio Sequencing",
    "/resources/sample": "资源-样本要求",
    "/about": "关于我们",
    "/contactus": "联系我们",
  };
  let _startTime = new Date();
  let _useTime = ref(0);

  emitter.on("pageTime", (oldValue) => {
    _useTime.value = new Date() - _startTime;
    console.log(_routeObj[oldValue]);
    console.log(_useTime.value);
    let _formdata = {};
    _formdata.visitorId = localStorage.getItem("userId");
    _formdata.url = oldValue;
    _formdata.classify = _routeObj[oldValue];
    _formdata.time = _useTime.value;
    DataburialpointApi(_formdata)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });
  });

  onBeforeUnmount(() => {
    emitter.off("pageTime");
  });
}

但是菜鸟一想,不对呀,全部和路由相关,直接写路由里面不好吗?这样写成mixins还要每个界面都这这样引入!

// mixin
import { usePageTime } from "@/mixin/pagetime";
usePageTime();

所以直接用路由守卫了,代码如下:
src/mixins/pagetime.js

import { createRouter, createWebHashHistory } from "vue-router";
import Home from "@/views/home/home.vue";
import { DataburialpointApi } from "@/network/api";

const routes = [
  {
    path: "/",
    name: "home",
    redirect: "/content",
    component: Home,
    children: [
      {
        path: "/content",
        name: "content",
        component: () => import("../views/content/content.vue"),
      },
      {
        path: "/services/:type",
        name: "services",
        component: () => import("../views/services/services.vue"),
      },
      {
        path: "/resources",
        name: "resources",
        redirect: "/resources/sample",
        component: () => import("../views/resources/resources.vue"),
        children: [
          {
            path: "/resources/sample",
            name: "sample",
            component: () => import("../views/resources/components/sample.vue"),
          },
        ],
      },
      {
        path: "/news",
        name: "news",
        component: () => import("../views/news/news.vue"),
      },
      {
        path: "/about",
        name: "about",
        component: () => import("../views/about/aboutus.vue"),
      },
      {
        path: "/contactus",
        name: "contactus",
        component: () => import("../views/contactus/contactus.vue"),
      },
    ],
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

let startTime = Date.now();
localStorage.setItem("lastPageTime", startTime);
let currentTime;
const _routeObj = {
  "/content": "首页",
  "/services/0": "NGS- Whole Genome Sequencing",
  "/services/1": "NGS- mRNA Sequencing",
  "/services/2": "NGS- Small RNA Sequencing",
  "/services/3": "NGS-Lnc RNA Sequencing",
  "/services/4": "NGS- Metagenome Sequencing",
  "/services/5": "NGS- Microbial diversity Sequencing",
  "/services/6": "NGS- Hi-C Sequencing",
  "/services/7": "Nanopore- Nanopore Ultra-long Sequencing",
  "/services/8": "Nanopore- Nanopore Sequencing",
  "/services/9": "Nanopore- Direct RNA Sequencing",
  "/services/10": "Nanopore- Lnc RNA Sequencing",
  "/services/11": "Nanopore- CircRNA Sequencing",
  "/services/12": "Nanopore- TAIL Iso Sequencing",
  "/services/13": "Nanopore- Isoform Sequencing",
  "/services/14": "Nanopore- Direct-CDNA Sequencing",
  "/services/15": "Nanopore- Single-cell full-length transcriptome Sequencing",
  "/services/16": "Nanopore- Full-length 16S/18S/ITS Amplicon Sequencing",
  "/services/17": "Nanopore- 16S-23S Amplicon Metagenomic Sequencing",
  "/services/18": "Nanopore- Metagenome Sequencing",
  "/services/19": "Nanopore- PORE-C Sequencing",
  "/services/20": "Pacbio Revio- Revio Sequencing",
  "/resources/sample": "资源-样本要求",
  "/about": "关于我们",
  "/contactus": "联系我们",
};

router.beforeEach((to, from) => {
  // console.log(to, from);
  if (to && _routeObj[from.path]) {
    // 第一步:页面跳转后记录一下当前的时间 currentTime
    currentTime = Date.now();
    // 第二步:通过计算currentTime - startTime 的 差值 之后,再上报数据
    let _formdata = {};
    _formdata.visitorId = localStorage.getItem("userId");
    _formdata.url = from.path;
    _formdata.classify = _routeObj[from.path];
    _formdata.time = currentTime - startTime;
    // console.log(_formdata);
    // debugger;
    DataburialpointApi(_formdata)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });
    // 第三步:每次都要初始化一下 startTime
    startTime = Date.now();
    localStorage.setItem("lastPageTime", startTime);
  }
});
export default router;

注意:
上面的 localStorage.setItem(“lastPageTime”, startTime); 是后续做 解决监听不到关闭网页的问题 的时候加上的!

解决监听不到关闭网页的问题

在app.vue里面加上监听页面关闭事件,这个必须用原生js,vue 的 onBeforeUnmount 根本不能在界面标签关闭时做操作!

import { useMainStore } from "@/store";
import { useRoute } from "vue-router";
// 路由菜单相关
let route = useRoute();
const mainStore = useMainStore();
onMounted(() => {
  // 监听页面关闭事件
  window.addEventListener("beforeunload", function (event) {
    // 计算页面停留时间(以毫秒为单位)
    const stayTime = new Date() - localStorage.getItem("lastPageTime");

    let _formdata = {};
    _formdata.visitorId = localStorage.getItem("userId");
    _formdata.url = route.path;
    _formdata.classify = mainStore.routeObj[route.path];
    _formdata.time = stayTime;

    // console.log(_formdata);
    // debugger;

    DataburialpointApi(_formdata)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });

    // 确保关闭页面时不会阻止默认行为
    delete event["returnValue"];
  });
});

注意:
这里 mainStore.routeObj 和上面的 _routeObj 是同一个东西,但是不能直接在 route/index.js 中使用 pinia,会报错,搜索是因为在 pinia 挂载之前使用了,具体怎么解决不知道!!!望读者 指点江山,激扬文字!!!

三、按钮埋点

到这里,菜鸟的需求就结束了,但是菜鸟拓展了一下,其实这个也很简单,就是每个按钮点击请求的时候,让后端统计一下,或者前端请求一下后端的某个接口就行!当然如果能直接封装成 自定义指令 就最好了!!!

参考文章:
vue项目进行前端埋点,记录页面停留时间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/514583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

doccano标注工具|为机器学习建模做数据标注

目录 一、标记流程 二、配置环境 2.1 安装 2.2 运行doccano 三、案例 3.1 创建项目 3.2 上传数据 3.3 定义标签 3.4 添加成员 3.5 开始标注 3.6 导出数据 3.7 导出数据 doccano doccano是开源的数据…

在ssh 工具 Linux screen会话中使用鼠标进行上下滚动

经过几次发现 除xshell外, WindTerm finalshell MobaXterm 都是进入会话后,发现其界面无法滚动屏幕向上查看 如果想要在Linux screen会话中使用鼠标进行上下滚动。必须首先进入该screen的回滚(scrollback模式)才能进行上下滚动 第一步&#xff…

小白理智进入IT行业

职业测试 🔔 0基础请先按照以下步骤做一遍,试试自己的毅力 1️⃣ 请在b站搜索王佩丰vba,然后试着看一下 2️⃣ 看完视频,有两种结果 😭 视频看到一半就终止,代码只会复制粘贴。 🎉 看完全部视…

MHA高可用-解决MySQL主从复制的单点问题

目录 一、MHA的介绍 1.什么是 MHA 2.MHA 的组成 2.1 MHA Node(数据节点) 2.2 MHA Manager(管理节点) 3.MHA 的特点 4. MHA工作原理总结如下: 二、搭建 MySQL MHA 实验环境 …

mongoDB 优化(2)索引

执行计划 语法: db.collection_xxx_t.find({"param":"xxxxxxx"}).explain(executionStats) 感觉这篇文章写得很好,可以参考 MongoDB——索引(单索引,复合索引,索引创建、使用)_mongo …

软考高级架构师:CISC (复杂指令集计算机) 和 RISC (精简指令集计算机)概念和例题

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

开源博客项目Blog .NET Core源码学习(13:App.Hosting项目结构分析-1)

开源博客项目Blog的App.Hosting项目为MVC架构的,主要定义或保存博客网站前台内容显示页面及后台数据管理页面相关的控制器类、页面、js/css/images文件,页面使用基于layui的Razor页面(最早学习本项目就是想学习layui的用法,不过最…

flink1.18源码本地调试环境

01 源码本地调试环境搭建 1. 从github拉取源码创建本地项⽬ https://github.com/apache/flink.git 可以拉取github上官⽅代码 https://github.com/apache/flink.git GitHub - apache/flink: Apache Flink 2. 配置编译环境 ctrlaltshifts (或菜单)打…

UE5启用SteamOS流程

一、安装OnlineSubsystemSteam插件 1、在UE里安装OnlineSubsystemSteam 2、设置默认开始地图 3、设置DefaultEngine.ini文件: 打开项目根目录/Config/DefaultEngine.ini文件 打开官网的配置说明 复制并粘贴到该文件中 4、设置运行模式 5、测试 确保Steam平台已…

Ansible批量操作(上传文件、删除文件指定文件内容、执行sh文件等)

官方网站 https://www.ansible.com/ 一、Ansible 简介 1、Ansible是新出现的自动化运维工具,完全基于Python开发,集合了众多运维工具(puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行…

【详解】Windows系统安装Nginx及简单使用

【详解】Windows系统安装Nginx及简单使用 一、Nginx是什么? “Nginx 是一款轻量级的 HTTP 服务器,采用事件驱动的异步非阻塞处理方式框架,这让其具有极好的 IO 性能,时常用于服务端的反向代理和负载均衡。”Nginx 是一款 http 服…

RocketMQ是什么?

文章目录 一、RocketMQ是什么?二、RocketMQ 应用场景三、RocketMQ 优缺点1.优点2、缺点 一、RocketMQ是什么? RocketMQ 是一款纯 java、分布式、队列模型的消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。 二、Rocke…

软件杯 深度学习YOLO抽烟行为检测 - python opencv

文章目录 1 前言1 课题背景2 实现效果3 Yolov5算法3.1 简介3.2 相关技术 4 数据集处理及实验5 部分核心代码6 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于深度学习YOLO抽烟行为检测 该项目较为新颖,适合作为竞赛课…

BGP-(as-path-filter)

BGP-as-path-filter,缺省 as-path-filter,正则表达式,as-path过滤器,对于BGP的as-path属性实际上可以看成是一个包含空格的字符串。 特点:1、通过对BGP路由的as-path属性进行匹配达到对BGP路由的过滤。 2、在route-…

OpenHarmony实战:轻量级系统之移植验证

OpenHarmony芯片移植完成后,需要开展OpenHarmony兼容性测试以及芯片SDK功能性测试。除可获得测试认证之外,还可以在开发阶段提前发现缺陷,大幅提高代码质量。 OpenHarmony兼容性测试 OpenHarmony兼容性测试是XTS(OpenHarmony生态…

传统模型用腻了?GCN图卷积神经网络一键实现西储大学轴承故障诊断!发文新思路!

​ 声明:文章是从本人公众号中复制而来,因此,想最新最快了解各类智能优化算法及其改进的朋友,可关注我的公众号:强盛机器学习,不定期会有很多免费代码分享~ 目录 数据介绍与故障诊断讲解 1.数据…

[lesson04]布尔类型和引用

布尔类型和引用 布尔类型 C中的布尔类型 C在C语言的基本类型系统上增加了boolC中的bool可取的值只有true和false理论上bool只占用一个字节 注意: true代表真值,编译器内部用1来表示 false代表非真值,编译器内部用0来表示 bool类型只有true(…

RabbitMQ3.x之六_RabbitMQ使用场景

RabbitMQ3.x之六_RabbitMQ使用场景 文章目录 RabbitMQ3.x之六_RabbitMQ使用场景1. 为什么选择 RabbitMQ?1. 可互操作2. 灵活3. 可靠 2. 常见用户案例1. 服务解耦2. 远程过程调用3. 流处理4. 物联网 1. 为什么选择 RabbitMQ? RabbitMQ 是一个可靠且成熟的…

IDEA 中能提高开发效率的插件

目录 前言 插件 Rainbow Brackets AceJump POJO to JSON Json Helper MybatisX Maven Helper PlantUML Integration TONYYI Lingma 前言 IDEA 里又很多好用的插件可以帮助我们提升开发效率,这里罗列下自己开发过程中常用的插件,善于利用插件&…

STM32的定时器中断Cubemx

STM32的定时器中断Cubemx 0.定时器简介1.配置时钟2.配置定时器3.创建工程4.补充源码 0.定时器简介 基本定时器功能: 16位向上、向下、向上/下自动装载计数器16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意…