赞
踩
最近菜鸟做项目,需要做简单的数据埋点,不是企业级的,反正看渡一的视频,企业级特别复杂,包括但不限于:错误收集、点击地方、用户行为……
菜鸟的需求就是简单收集一下用户的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项目进行前端埋点,记录页面停留时间
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。