基于Vue3实现一个前端埋点上报插件并打包发布到npm(下)

简介: 基于Vue3实现一个前端埋点上报插件并打包发布到npm(下)

页面停留时间(TP)



页面停留时长同样借助effect函数,通过计算页面变化的时间差从而上报页面停留时长事件,一般当进入第二个页面才会统计第一个页面的TP,进入三个页面计算第二个页面的TP。。。所以我们把逻辑写在getVisitor函数中然后给它改个名

//上报uv&pv&TP
const getVisitorAndTP = (app, prefix) => {
  const globalProperties = reactive(app.config.globalProperties);
  let startTime = new Date().getTime();
  let path = "";
  let lastPath = "";
  effect(() => {
    const endTime = new Date().getTime();
    const TP = endTime - startTime;
    startTime = endTime;
    lastPath = path;
    path = globalProperties.$route.path;
    //间隔为0不上报
    if (!TP) return;
    console.log({
      eventName: `${prefix}_${path}`,
    });
    //页面停留时长小于0.5s不上报
    if (TP < 500) return;
    console.log({
      eventName: `${prefix}_${TP}_${lastPath}`,
    });
  });
};
export default {
  install: (app, options) => {
    app.directive("click", {
      created: (el, bind) => {
        el.addEventListener("click", () => {
          console.log(bind.value);
        });
      },
    });
    //挂载全局用于手动上报
    app.config.globalProperties.$vtrack = (params) => {
      console.log(params)
    }
  }
}

上传TP事件的格式为prefix_TP_path,因此我们切换页面的时候可以看到同时上报的两个事件

image.png


获取公共参数



根据用户传来的固定参数baseParams和事件前缀prefix调整我们上报事件形式。假设在main.js用户传来这些数据

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/index";
import vTracking from "v-tracking";
const app = createApp(App);
app.use(router);
app.use(vTracking, {
  baseParams: {
    uid: 123,
    userAgent: "Chrome",
  },
  prefix: "app",
});
app.mount("#app");

然后修改一下我们的插件(这里将uv/pv还有TP作为单独参数上报,不再使用上面的eventName形式,太懒了,上面的写法不想改了😑)

import { reactive, effect } from "@vue/reactivity";
//上报uv&pv&TP
const getVisitorAndTP = (app, prefix, baseParams) => {
  const globalProperties = reactive(app.config.globalProperties);
  let startTime = new Date().getTime();
  let path = "";
  let lastPath = "";
  effect(() => {
    const endTime = new Date().getTime();
    const TP = endTime - startTime;
    startTime = endTime;
    lastPath = path;
    path = globalProperties.$route.path;
    //间隔为0不上报
    if (!TP) return;
    console.log({
      ...baseParams,
      UPVEventName: `${prefix}_${path}`,
    });
    //页面停留时长小于0.5s不上报
    if (TP < 500) return;
    console.log({
      ...baseParams,
      TP: {
        path: lastPath,
        time: TP,
      },
    });
  });
};
export default {
  install: (app, options) => {
    const { prefix, baseParams } = options;
    getVisitorAndTP(app, prefix || "track", baseParams || {});
    app.directive("click", {
      created: (el, bind) => {
        el.addEventListener("click", () => {
          console.log({ ...bind.value, ...(baseParams || {}) });
        });
      },
    });
    //挂载全局用于手动上报
    app.config.globalProperties.$vtrack = (params) => {
      console.log(params);
    };
  },
};

此时这控制台打印出事件类型上报格式为

image.png


引入axios



最后简单写一个axios的请求函数,这里不考虑请求失败的情况,此时需要用户传入一个baseUrl

import { reactive, effect } from "@vue/reactivity";
import axios from "axios";
axios.defaults.headers["Content-Type"] = "application/json";
const request = (baseUrl, params) => {
  axios({
    url: baseUrl,
    method: "post",
    data: params,
  });
};
//上报uv&pv&TP
const getVisitorAndTP = (app, prefix, baseParams, baseUrl) => {
  const globalProperties = reactive(app.config.globalProperties);
  let startTime = new Date().getTime();
  let path = "";
  let lastPath = "";
  effect(() => {
    const endTime = new Date().getTime();
    const TP = endTime - startTime;
    startTime = endTime;
    lastPath = path;
    path = globalProperties.$route.path;
    //间隔为0不上报
    if (!TP) return;
    request(baseUrl, {
      ...baseParams,
      UPVEventName: `${prefix}_${path}`,
    });
    //页面停留时长小于0.5s不上报
    if (TP < 500) return;
    request(baseUrl, {
      ...baseParams,
      TP: {
        path: lastPath,
        time: TP,
      },
    });
  });
};
export default {
  install: (app, options) => {
    const { prefix, baseParams,baseUrl } = options;
     getVisitorAndTP(app, prefix || "track", baseParams || {}, baseUrl);
    getVisitorAndTP(app, prefix || "track", baseParams || {});
    app.directive("click", {
      created: (el, bind) => {
        el.addEventListener("click", () => {
          console.log({ ...bind.value, ...(baseParams || {}) });
        });
      },
    });
    //挂载全局用于手动上报
    app.config.globalProperties.$vtrack = (params) => {
      console.log(params);
    };
  },
};

此时便可以看到事件的请求了

image.png


打包发布



最后使用vite进行打包发布,全局安装vite

pnpm add vite -w -D

然后在v-tracking下新建vite.config.js,配置库模式打包cjs和es格式

import { defineConfig } from "vite";
import { resolve } from "path";
export default defineConfig({
  build: {
    target: "modules",
    //压缩
    minify: true,
    rollupOptions: {
      input: ["index.js"],
      //忽略文件
      external: ["@vue/reactivity", "axios"],
      output: [
        {
          format: "es",
          //不用打包成.es.js,这里我们想把它打包成.js
          entryFileNames: "[name].js",
          //配置打包根目录
          dir: resolve(__dirname, "./dist/es"),
        },
        {
          format: "cjs",
          //不用打包成.mjs
          entryFileNames: "[name].js",
          //配置打包根目录
          dir: resolve(__dirname, "./dist/lib"),
        },
      ],
    },
    lib: {
      entry: "./index.js",
      name: "vtrack",
    },
  },
});

然后将v-tracking/package.json入口文件指向打包后路径,其中module代表如果项目支持es格式的话就会使用dist/es/index.js这个路径

{
  "name": "v-tracking",
  "version": "1.0.0",
  "main": "dist/lib/index.js",
  "module": "dist/es/index.js",
  "description": "",
  "keywords": [],
  "files": [
    "dist"
  ],
  "dependencies": {
    "@vue/reactivity": "^3.2.37",
    "axios": "^0.27.2"
  },
  "author": "",
  "license": "MIT"
}

最后在v-tracking目录下执行pnpm publish进行发布(这里需要注册npm账户等等)

image.png


使用说明



安装

npm install v-tracking -S

在 main.js 中引入插件

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/index";
import vTracking from "v-tracking";
const app = createApp(App);
app.use(router);
app.use(vTracking, Options);
app.mount("#app");

注意

因为涉及到路由检测,所以必须配合vue-router使用


Options


  • baseParams (string)

公共参数,每次上报都会携带的参数,比如用户的登录信息 uid 等

  • baseUrl (string)

上报的后台请求地址,后端接口需按照前端请求参数设计

  • prefix (string)

PV&UV&TP 事件前缀,一般用于区分不同项目等(建议和普通事件前缀一致)

  • isVisTP (Boolean)

是否统计页面 UV&PV&PT


Options 示例

app.use(vTracking, {
  baseParams: {
    uid: 123
  },
  baseUrl: "http://example/event",
  prefix: "app",
  isVisTP: false,
});

点击指令上报


<template>
    <div>page1</div>
    <div v-click="{ eventName: 'test1' }">click</div>
</template>

后台接收数据格式为

{ uid: 123 , eventName: "test1" }

手动上报


<template>
    <div>page1</div>
    <div @click="track">click</div>
</template>
<script setup>
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance()
//手动上报事件
const track = ()=>{
  proxy.$vtrack({ eventName: 'test1'  })
}
</script>

后台接收数据格式为

{ uid: 123, eventName: "test1" }

UV&PV


isVisTP为 true 时候插件会自动上报每个页面进入时的数据,其中后台接收数据格式为

{ uid: 123, UPVEventName: `${prefix}_${path}` }

其中path为页面路由路径,如/page1


页面停留时长(TP)



isVisTP为 true 时候插件会自动上报每个页面用户停留时长,其中后台接收数据格式为


{
  uid: 123,
  TP: { path: "/page2", time: 1269446 },
}

time 则表示时长(ms)


写在最后



本篇文章旨在提供一些思路,难免会有不妥或者错误之处,也欢迎大家评论区指出不胜感激。仓库地址vue-utils

相关文章
|
3天前
|
前端开发 Java 开发工具
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
|
3天前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
3月前
|
JavaScript 前端开发 Docker
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
在使用 Deno 构建项目时,生成的可执行文件体积较大,通常接近 100 MB,而 Node.js 构建的项目体积则要小得多。这是由于 Deno 包含了完整的 V8 引擎和运行时,使其能够在目标设备上独立运行,无需额外安装依赖。尽管体积较大,但 Deno 提供了更好的安全性和部署便利性。通过裁剪功能、使用压缩工具等方法,可以优化可执行文件的体积。
202 3
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
|
2月前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】React与Vue:前端框架的巅峰对决与选择策略
|
2月前
|
前端开发 JavaScript 数据管理
React与Vue:两大前端框架的较量与选择策略
【10月更文挑战第23天】React与Vue:两大前端框架的较量与选择策略
|
3月前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
2月前
|
JavaScript 前端开发 搜索推荐
Vue的数据驱动视图与其他前端框架的数据驱动方式有何不同?
总的来说,Vue 的数据驱动视图在诸多方面展现出独特的优势,其与其他前端框架的数据驱动方式的不同之处主要体现在绑定方式、性能表现、触发机制、组件化结合、灵活性、语法表达以及与后端数据交互等方面。这些差异使得 Vue 在前端开发领域具有独特的地位和价值。
|
3月前
|
前端开发 JavaScript 安全
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
222 4
|
2月前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】 React与Vue:前端框架的巅峰对决与选择策略
|
3月前
|
前端开发 JavaScript API
2025年前端框架是该选vue还是react?有了大模型-例如通义灵码辅助编码,就不用纠结了!vue用的多选react,react用的多选vue
本文比较了Vue和React两大前端框架,从状态管理、数据流、依赖注入、组件管理等方面进行了详细对比。当前版本和下载量数据显示React更为流行,但Vue在国内用户量增长迅速。Vue 3通过组合式API提供了更灵活的状态管理和组件逻辑复用,适合中小型项目;React则更适合大型项目和复杂交互逻辑。文章还给出了选型建议,强调了多框架学习的重要性,认为技术问题已不再是选型的关键,熟悉各框架的最佳实践更为重要。
326 0

推荐镜像

更多