欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Vue3實(shí)現(xiàn)前端埋點(diǎn)上報(bào)插件并打包發(fā)布到npm的詳細(xì)過程

 更新時(shí)間:2022年10月20日 15:30:09   作者:迪迪滴  
這篇文章主要介紹了基于Vue3實(shí)現(xiàn)一個(gè)前端埋點(diǎn)上報(bào)插件并打包發(fā)布到npm,本項(xiàng)目采用pnpm進(jìn)行Monorepo環(huán)境搭建,因?yàn)槲磥磉@個(gè)項(xiàng)目可能會(huì)加入更多的工具包,需要的朋友可以參考下

前端埋點(diǎn)對(duì)于那些營(yíng)銷活動(dòng)的項(xiàng)目是必須的,它可以反應(yīng)出用戶的喜好與習(xí)慣,從而讓項(xiàng)目的運(yùn)營(yíng)者們能夠調(diào)整策略優(yōu)化流程提高用戶體驗(yàn)從而獲取更多的$。這篇文章將實(shí)現(xiàn)一個(gè)Vue3版本的埋點(diǎn)上報(bào)插件,主要功能有

  • 通過Vue自定義指令形式實(shí)現(xiàn)點(diǎn)擊事件上報(bào)
  • 提供手動(dòng)調(diào)用上報(bào)方法
  • 上報(bào)每個(gè)頁(yè)面訪問人數(shù)與次數(shù)(UV,PV)
  • 上報(bào)用戶在每個(gè)頁(yè)面停留時(shí)長(zhǎng)

項(xiàng)目環(huán)境搭建

本項(xiàng)目采用pnpm進(jìn)行Monorepo環(huán)境搭建,因?yàn)槲磥磉@個(gè)項(xiàng)目可能會(huì)加入更多的工具包.

安裝pnpm

npm install pnpm -g

初始化package.json

pnpm init

新建配置文件 .npmrc

shamefully-hoist = true

新建pnpm-workspace.yaml

packages:
  - "packages/**"
  - "play"

此時(shí)我們的packages目錄和play目錄便關(guān)聯(lián)起來的,我們后面就可以愉快的在本地調(diào)試了。其中packages是我們各種包存放的地方,具體我們本次開發(fā)的埋點(diǎn)插件v-tracking便是其中之一。play則是一個(gè)Vue3項(xiàng)目用來測(cè)試我們的本地包,它的創(chuàng)建方法這里就不再詳細(xì)說了。最終它的目錄結(jié)構(gòu)如下

插件開發(fā)

終端進(jìn)入v-tracking,執(zhí)行pnpm init讓它成為一個(gè)包,然后新建index.js作為入口。

在vue3是通過 app.use(plugin)的形式引入插件的,它會(huì)直接調(diào)用插件的install方法.install會(huì)接收到應(yīng)用實(shí)例和傳遞給app.use()的額外選項(xiàng)作為參數(shù)。所以我們?cè)?code>v-tracking/index.js默認(rèn)導(dǎo)出一個(gè)帶有install函數(shù)的對(duì)象

export default {
    install: (app, options) => {
        console.log(options)
    }
}

進(jìn)入paly執(zhí)行pnpm add v-tracking此時(shí)你會(huì)發(fā)現(xiàn)paly下的package.json多了個(gè)這樣的依賴

這樣就是表示play已經(jīng)關(guān)聯(lián)到本地的包v-tracking@1.0.0的包了,然后我們?cè)?code>paly的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
    }
})
app.mount('#app')

啟動(dòng)項(xiàng)目我們會(huì)發(fā)現(xiàn)install函數(shù)被調(diào)用了,并且獲取到了傳來的額外參數(shù).

點(diǎn)擊事件上報(bào)

點(diǎn)擊事件的上報(bào)我們提供兩種方式,一種是以Vue自定義指令的形式,一種是手動(dòng)調(diào)用上報(bào)方法。因?yàn)橹噶钚问降狞c(diǎn)擊上報(bào)并不能實(shí)現(xiàn)異步上報(bào),所以加入手動(dòng)調(diào)用上報(bào)的方法

vue自定義指令

首先我們簡(jiǎn)單了解一下什么是自定義指令。我們都用過Vue的內(nèi)置的一系列指令 (比如v-modelv-show) 等,而Vue還提供了注冊(cè)自定義指令的函數(shù)directive用法如下,其中el是我們綁定指令的dom,binding則是指令傳來的一系列參數(shù),比如

<div v-example:foo.bar="baz">

binding則是這樣一個(gè)對(duì)象

{
  arg: 'foo',
  modifiers: { bar: true },
  value: /* `baz` 的值 */,
  oldValue: /* 上一次更新時(shí) `baz` 的值 */
}

了解完指令我們便可以開始自定義指令click的開發(fā)了。其實(shí)很簡(jiǎn)單,就是監(jiān)聽el的點(diǎn)擊事件然后獲取到指令的value上報(bào)給后端即可

export default {
    install: (app, options) => {     
        app.directive('click', (el, bind) => {
            el.addEventListener('click', () => {
                console.log(bind.value)
            })
        })

    }
}

我們?cè)?code>play的page1.vue種進(jìn)行綁定指令測(cè)試

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

我們點(diǎn)擊test1便可以在控制臺(tái)看到我們需要上報(bào)的數(shù)據(jù)

手動(dòng)上報(bào)方法

我們可以手動(dòng)調(diào)用上報(bào)方法掛載在實(shí)例全局即可,在vue3種掛載全局屬性的方法是app.config.globalProperties.xxx,所以我們定義一個(gè)全局上報(bào)方法$vtrack

export default {
    install: (app, options) => {
        app.directive('click', (el, bind) => {
            el.addEventListener('click', () => {
                console.log(bind.value)
            })
        })
        //掛載全局用于手動(dòng)上報(bào)
        app.config.globalProperties.$vtrack = (params) => {
            console.log(params)
        }

    }
}

然后我們?cè)?code>page1.vue中進(jìn)行使用

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

<script setup>
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance()
proxy.$vtrack({ eventName: 'test1' })
</script>

同樣的我們可以獲取到我們需要的上報(bào)數(shù)據(jù)。

頁(yè)面訪問次數(shù)上報(bào)(pv,uv)

對(duì)于頁(yè)面訪問次數(shù)或者人數(shù)我們可以通過檢測(cè)路由的變化從而上報(bào)當(dāng)前頁(yè)面事件。比如在page1頁(yè)面我們可以以prefix_/page1(這個(gè)前綴可以由自己來定義)形式上報(bào)。但是在插件中如何檢測(cè)路由變化呢?

起初我想通過監(jiān)聽onhashchange事件來監(jiān)聽路由變化的,但是經(jīng)過測(cè)試發(fā)現(xiàn)Vue中的push事件根本不會(huì)觸發(fā)onhashchange。所以我便引入了@vue/reactivity,通過它的reactive讓傳入app實(shí)例進(jìn)行一個(gè)響應(yīng)式包裹,再通過effect函數(shù)監(jiān)聽路由變化從而實(shí)現(xiàn)統(tǒng)計(jì)每個(gè)頁(yè)面的進(jìn)入事件,首先安裝

pnpm add @vue/reactivity -w

然后引用

import { reactive,effect } from '@vue/reactivity'
//uv and pv
const getVisitor = (app, prefix) => {
  const globalProperties = reactive(app.config.globalProperties);
  effect(() => {
    const path = globalProperties.$route.path;
    console.log({
      eventName: `${prefix}_${path}`,
    });
  });
};

export default {
  install: (app, options) => {
    stayTime();
    getVisitor(app, "track");
    app.directive("click", (el, bind) => {
      el.addEventListener("click", () => {
        console.log(bind.value);
      });
    });
    //掛載全局用于手動(dòng)上報(bào)
    app.config.globalProperties.$vtrack = (params) => {
      console.log(params);
    };
  },
};

然后在項(xiàng)目中切換路由就會(huì)獲取到需要上報(bào)的事件

頁(yè)面停留時(shí)間(TP)

頁(yè)面停留時(shí)長(zhǎng)同樣借助effect函數(shù),通過計(jì)算頁(yè)面變化的時(shí)間差從而上報(bào)頁(yè)面停留時(shí)長(zhǎng)事件,一般當(dāng)進(jìn)入第二個(gè)頁(yè)面才會(huì)統(tǒng)計(jì)第一個(gè)頁(yè)面的TP,進(jìn)入三個(gè)頁(yè)面計(jì)算第二個(gè)頁(yè)面的TP。。。所以我們把邏輯寫在getVisitor函數(shù)中然后給它改個(gè)名

//上報(bào)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不上報(bào)
    if (!TP) return;
    console.log({
      eventName: `${prefix}_${path}`,
    });
    //頁(yè)面停留時(shí)長(zhǎng)小于0.5s不上報(bào)
    if (TP < 500) return;
    console.log({
      eventName: `${prefix}_${TP}_${lastPath}`,
    });
  });
};

export default {
  install: (app, options) => {
    getVisitorAndTP(app, "track");
    app.directive("click", (el, bind) => {
      el.addEventListener("click", () => {
        console.log(bind.value);
      });
    });
    //掛載全局用于手動(dòng)上報(bào)
    app.config.globalProperties.$vtrack = (params) => {
      console.log(params);
    };
  },
};

上傳TP事件的格式為prefix_TP_path,因此我們切換頁(yè)面的時(shí)候可以看到同時(shí)上報(bào)的兩個(gè)事件

獲取公共參數(shù)

根據(jù)用戶傳來的固定參數(shù)baseParams和事件前綴prefix調(diào)整我們上報(bào)事件形式。假設(shè)在main.js用戶傳來這些數(shù)據(jù)

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作為單獨(dú)參數(shù)上報(bào),不再使用上面的eventName形式,太懶了,上面的寫法不想改了??)

import { reactive, effect } from "@vue/reactivity";
//上報(bào)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不上報(bào)
    if (!TP) return;
    console.log({
      ...baseParams,
      UPVEventName: `${prefix}_${path}`,
    });
    //頁(yè)面停留時(shí)長(zhǎng)小于0.5s不上報(bào)
    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", (el, bind) => {
      el.addEventListener("click", () => {
        console.log({ ...bind.value, ...(baseParams || {}) });
      });
    });
    //掛載全局用于手動(dòng)上報(bào)
    app.config.globalProperties.$vtrack = (params) => {
      console.log(params);
    };
  },
};

此時(shí)這控制臺(tái)打印出事件類型上報(bào)格式為

引入axios

最后簡(jiǎn)單寫一個(gè)axios的請(qǐng)求函數(shù),這里不考慮請(qǐng)求失敗的情況,此時(shí)需要用戶傳入一個(gè)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,
  });
};

//上報(bào)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不上報(bào)
    if (!TP) return;
    request(baseUrl, {
      ...baseParams,
      UPVEventName: `${prefix}_${path}`,
    });
    //頁(yè)面停留時(shí)長(zhǎng)小于0.5s不上報(bào)
    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);
    app.directive("click", (el, bind) => {
      el.addEventListener("click", () => {
        request(baseUrl, { ...bind.value, ...(baseParams || {}) });
      });
    });
    //掛載全局用于手動(dòng)上報(bào)
    app.config.globalProperties.$vtrack = (params) => {
      request(baseUrl, { ...params, ...(baseParams || {}) });
    };
  },
};

此時(shí)便可以看到事件的請(qǐng)求了

打包發(fā)布

最后使用vite進(jìn)行打包發(fā)布,全局安裝vite

pnpm add vite -w -D

然后在v-tracking下新建vite.config.js,配置庫(kù)模式打包c(diǎn)js和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代表如果項(xiàng)目支持es格式的話就會(huì)使用dist/es/index.js這個(gè)路徑

{
  "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目錄下執(zhí)行pnpm publish進(jìn)行發(fā)布(這里需要注冊(cè)npm賬戶等等)

使用說明

安裝

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");

注意

因?yàn)樯婕暗铰酚蓹z測(cè),所以必須配合vue-router使用

Option

  • sbaseParams (string)

公共參數(shù),每次上報(bào)都會(huì)攜帶的參數(shù),比如用戶的登錄信息 uid 等

  • baseUrl (string)

上報(bào)的后臺(tái)請(qǐng)求地址,后端接口需按照前端請(qǐng)求參數(shù)設(shè)計(jì)

  • prefix (string)

PV&UV&TP 事件前綴,一般用于區(qū)分不同項(xiàng)目等(建議和普通事件前綴一致)

  • isVisTP (Boolean)

是否統(tǒng)計(jì)頁(yè)面 UV&PV&PT

Options 示例

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

點(diǎn)擊指令上報(bào)

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

后臺(tái)接收數(shù)據(jù)格式為

{ uid: 123 , eventName: "test1" }

手動(dòng)上報(bào)

<template>
    <div>page1</div>
    <div @click="track">click</div>
</template>
<script setup>
import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance()
//手動(dòng)上報(bào)事件
const track = ()=>{
  proxy.$vtrack({ eventName: 'test1'  })
}

</script>

后臺(tái)接收數(shù)據(jù)格式為

{ uid: 123, eventName: "test1" }

UV&PV

isVisTP為 true 時(shí)候插件會(huì)自動(dòng)上報(bào)每個(gè)頁(yè)面進(jìn)入時(shí)的數(shù)據(jù),其中后臺(tái)接收數(shù)據(jù)格式為

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

其中path為頁(yè)面路由路徑,如/page1

頁(yè)面停留時(shí)長(zhǎng)(TP)

isVisTP為 true 時(shí)候插件會(huì)自動(dòng)上報(bào)每個(gè)頁(yè)面用戶停留時(shí)長(zhǎng),其中后臺(tái)接收數(shù)據(jù)格式為

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

time 則表示時(shí)長(zhǎng)(ms)

寫在最后

本篇文章旨在提供一些思路,難免會(huì)有不妥或者錯(cuò)誤之處,也歡迎大家評(píng)論區(qū)指出不勝感激。倉(cāng)庫(kù)地址vue-utils

到此這篇關(guān)于基于Vue3實(shí)現(xiàn)一個(gè)前端埋點(diǎn)上報(bào)插件并打包發(fā)布到npm的文章就介紹到這了,更多相關(guān)Vue打包發(fā)布到npm內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue實(shí)現(xiàn)可增刪查改的成績(jī)單

    vue實(shí)現(xiàn)可增刪查改的成績(jī)單

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)可增刪查改的成績(jī)單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Vuei18n 在數(shù)組中的使用方式

    Vuei18n 在數(shù)組中的使用方式

    這篇文章主要介紹了Vuei18n 在數(shù)組中的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • el-form表單el-form-item label不換行問題及解決

    el-form表單el-form-item label不換行問題及解決

    這篇文章主要介紹了el-form表單el-form-item label不換行問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue組件實(shí)現(xiàn)彈出框點(diǎn)擊顯示隱藏效果

    vue組件實(shí)現(xiàn)彈出框點(diǎn)擊顯示隱藏效果

    這篇文章主要為大家詳細(xì)介紹了vue組件實(shí)現(xiàn)彈出框點(diǎn)擊顯示隱藏,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • vue使用refs獲取嵌套組件中的值過程

    vue使用refs獲取嵌套組件中的值過程

    這篇文章主要介紹了vue使用refs獲取嵌套組件中的值過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • vue實(shí)現(xiàn)拖拽交換位置

    vue實(shí)現(xiàn)拖拽交換位置

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)拖拽交換位置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Vue中關(guān)閉彈窗組件時(shí)銷毀并隱藏操作

    Vue中關(guān)閉彈窗組件時(shí)銷毀并隱藏操作

    這篇文章主要介紹了Vue中關(guān)閉彈窗組件時(shí)銷毀并隱藏操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Vue高級(jí)用法實(shí)例教程之動(dòng)態(tài)組件

    Vue高級(jí)用法實(shí)例教程之動(dòng)態(tài)組件

    讓多個(gè)組件使用同一個(gè)掛載點(diǎn),并動(dòng)態(tài)切換,這就是動(dòng)態(tài)組件,下面這篇文章主要給大家介紹了關(guān)于Vue高級(jí)用法實(shí)例教程之動(dòng)態(tài)組件的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • vue+axios?methods方法return取不到值問題及解決

    vue+axios?methods方法return取不到值問題及解決

    這篇文章主要介紹了vue+axios?methods方法return取不到值問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 在Vue中使用scoped屬性實(shí)現(xiàn)樣式隔離的原因解析

    在Vue中使用scoped屬性實(shí)現(xiàn)樣式隔離的原因解析

    scoped是Vue的一個(gè)特殊屬性,可以應(yīng)用于<style>標(biāo)簽中的樣式,這篇文章給大家介紹在Vue中,使用scoped屬性為什么可以實(shí)現(xiàn)樣式隔離,感興趣的朋友一起看看吧
    2023-12-12

最新評(píng)論