【解決方案】數據埋點的一點思路與vue的SPA單頁面實踐

一、前言

數據埋點是監控用戶在應用中的表現行爲,對於TO C的產品迭代來說越來越重要。

數據埋點是產品需求分析的來源,檢驗功能是否達到預期。前端是更貼近用戶,我來說說數據埋點在系統開發中的方案。

二、數據埋點方案分析

不同的產品對於數據的關注的角度不同,根據需求來採集和設計不同的方案。比如信息流的產品抖音,關注用戶的停留時間更高。比如商品類的注重的是“復購率”,統計新老用戶。

數據埋點統計通常分爲:

(1)頁面訪問量統計

(2)功能點擊量統計

我們今天只討論頁面訪問量統計,在開發系統中自己定義頁面訪問量相關數據的統計。

通常我們接入的是第三方的服務,比如百度統計,就有相關頁面訪問統計,以及用戶的畫像等等。但是作爲開發人員來說,如果在自己做的系統中,按照自己的需求定製化數據埋點是不是很cool。

1、頁面訪問量相關統計

頁面訪問量通常分爲:PV和UV。

(1)PV:頁面訪問人次。

(2)UV:頁面訪問人數。

頁面訪問量,並非僅僅是內容吸引力決定的,影響因素有:入口,頁面位置,到主頁面深度等等。入口主要是UI視覺相關人員設計。入口位置可以通過數據分析後進行調整,到主頁面深度可以分析用戶的訪問路徑進行調整。

採集頁面加載 from、to 以獲知用戶訪問路徑:

image

分析可以知道用戶普遍訪問深度,每一層和每一個頁面的流失率可以很直觀看出來,從而調整核心頁面入口源和深度。

還有一些特殊情況出現:比如PV穩定的頁面訪問量突然暴跌,可能是加載失敗或者報錯。

三、數據埋點方案實際操作

接下來我通過自己的系統接入數據埋點:https://chat.chengxinsong.cn

技術架構:vue+vuex+koa2+mysql+websocketIO+redis等。

1、方案一:全局定義Router.beforeEach方法

在main.js中全局定義

/*全局PV統計*/
router.beforeEach((to, from, next) => {
  let flag = localStorage.getItem("HappyChatUserInfo") !== null ? true: false;
  let data = {
    type: 'visit',
    user_id: flag ? JSON.parse(localStorage.getItem("HappyChatUserInfo")).user_id: '獲取不到userId',
    time: (new Date()).getTime(),
    params: {
      from: {
        name: from.name || '',
        path: from.path || '',
        query: from.query || ''
      },
      to: {
        name: to.name || '',
        path: to.path || '',
        query: to.query || ''
      }
    }
  }
  App.methods.logEvent(data);
  next()
})

停留時間可以通過from和to頁面的時間(跳轉頁time - 當前頁time)。關閉應用的時候如何統計?可以考慮window.onunload方法

window.onunload = function unloadPage() {
  let data = {
    type: 'close',
    user_id: localStorage.getItem("HappyChatUserInfo") !== null ? JSON.parse(localStorage.getItem("HappyChatUserInfo")).user_id: '獲取不到userId',
    time: (new Date()).getTime(),
    params: {
      from: {
        name: '關閉前',
        path: router.currentRoute.path || '',
        query: router.currentRoute.params || ''
      },
      to: {
        name: '關閉',
        path: '',
        query: ''
      }
    }
  }
  App.methods.logEvent(data);
}

這時候我們需要去定義接口傳參,接口方法是logEvent。

我們自定義Vue插件App的method,用於埋點數據向服務器發送。

我們在App.vue中定義

具體方法

      /* 數據埋點 */
      logEvent(opts) {
        let data = {
          type: opts.type,
          user_id: opts.user_id,
          time: opts.time,
          params: opts.params || {}
        }
        return Api.pvLog(data).then(res => {
          console.log(res)
        }).catch(function (error) {
          console.log(error);
        });
      }

其中Api是axios的接口統一封裝的方法。

數據到了後端怎麼保存,保存哪些參數,根據需求來定義,比如常見的:客戶端IP,數據類型type,用戶id,訪問時間,from中的頁面名字name,路徑path,查詢茶樹query等等。

後端接口的控制層。(接口需不需要驗證,根據需求來設計。)

let pvLog = async (ctx, next) => {
    const data = ctx.request.body;
    let req = ctx.req;
    let clientIP = req.headers['x-forwarded-for'] ||
        req.connection.remoteAddress ||
        req.socket.remoteAddress ||
        req.connection.socket.remoteAddress;
    userModel.logPV([clientIP, data.type, data.user_id, data.time,
        data.params.from.name || '', data.params.from.path || '', JSON.stringify(data.params.from.query) || '',
        data.params.to.name || '', data.params.to.path || '', JSON.stringify(data.params.to.query) || '']);
    ctx.body = {
        success: true
    }
};

接下來就是數據入庫操作,代碼就不放了,源碼地址:

1、後端代碼:https://github.com/saucxs/hap...

2、前端代碼:https://github.com/saucxs/hap...

歡迎fork和start。

2、方案二:全局註冊混入beforeRouteEnter和beforeRouteLeave

雖然官方說,慎用全局混入對象。

放一下示例代碼

import Vue from 'vue'

Vue.mixin({
    beforeRouteEnter (to, from, next) {
        next(vm => {
            vm.$app.logEvent({
                type: 'visit',
                name: to.name,
                time: new Date().valueOf(),
                params: {
                    from: {
                        name: from.name,
                        path: from.path,
                        query: from.query
                    },
                    to: {
                        name: to.name,
                        path: to.path,
                        query: to.query
                    }
                }
            })
        })
    },
    beforeRouteLeave (to, from, next) {
        this.$app.logEvent({
            type: 'visit',
            name: to.name,
            time: new Date().valueOf(),
            params: {
                from: {
                    name: from.name,
                    path: from.path,
                    query: from.query
                },
                to: {
                    name: to.name,
                    path: to.path,
                    query: to.query
                }
            }
        })
        next()
    }
})

我們需要考慮是否在應用關閉的時候觸發beforeRouteLeave方法?

還有兩個問題:

(1)每一個頁面都有鉤子函數beforeRouteEnter,beforeRouteLeave方法,如何進行合併。

(2)有時候涉及到子路由問題,子路由的頁面會調用2次beforeRouteEnter和beforeRouteLeave方法,PV會翻倍。

所以覺得方案二僅供參考,慎用。

其中,this.$app.logEvent(vm.$app.logEvent)等同於方案一的App.logEvent。

四、全局PV統計方案總結

根據實際需求和評估使用不同的方案:

(1)SPA應用,單入口,在入口文件全局定義Router.beforeEach就可以,就是方案1。

(2)MPA應用,多入口,可以封裝公用的邏輯,免去重複構造entry成本。

(3)SPA+MPA混合應用:採用MPA方案就行。

(4)SSR應用:追求SEO採用服務端渲染(SSR),採用不同的模板渲染頁面,直接統計調用模板的次數就可以知道PV相關數據。

至於 UV,統計 PV 時採集 userId 去重即可。若應用無用戶管理體系,採集 IP、deviceId 也可粗略得知 UV(不精準)。

作者簡介

暱稱:saucxs | songEagle | 鬆寶寫代碼

github:https://github.com/saucxs

一、技術產品

二、開源作品:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章