vue項(xiàng)目前端埋點(diǎn)

埋點(diǎn)方案的確定
業(yè)界的埋點(diǎn)方案主要分為以下三類:

代碼埋點(diǎn):在需要埋點(diǎn)的節(jié)點(diǎn)調(diào)用接口卸奉,攜帶數(shù)據(jù)上傳睬塌。如百度統(tǒng)計(jì)等;
可視化埋點(diǎn):使用可視化工具進(jìn)行配置化的埋點(diǎn)昆庇,即所謂的「無(wú)痕埋點(diǎn)」末贾,前端在頁(yè)面加載時(shí),可以讀取配置數(shù)據(jù)整吆,自動(dòng)調(diào)用接口進(jìn)行埋點(diǎn)拱撵。如開(kāi)源的Mixpanel;
無(wú)埋點(diǎn):前端自動(dòng)采集全部事件并上報(bào)埋點(diǎn)數(shù)據(jù)。如國(guó)內(nèi)的神策數(shù)據(jù)等表蝙;
在當(dāng)時(shí)排期緊湊拴测,人力緊缺的情況下,顯然不允許我們?nèi)ラ_(kāi)發(fā)可視化埋點(diǎn)方案和無(wú)埋點(diǎn)方案勇哗,所以只能采取代碼埋點(diǎn)方案昼扛。

命令式埋點(diǎn)

命令式埋點(diǎn),顧名思義欲诺,開(kāi)發(fā)者需要手動(dòng)在需要埋點(diǎn)的節(jié)點(diǎn)處進(jìn)行埋點(diǎn)抄谐。如點(diǎn)擊按鈕或鏈接后的回調(diào)函數(shù)、頁(yè)面ready時(shí)進(jìn)行請(qǐng)求的發(fā)送扰法。大家肯定都很熟悉這樣的代碼:

// 頁(yè)面加載時(shí)發(fā)送埋點(diǎn)請(qǐng)求
$(document).ready(function(){
   // ... 這里存在一些業(yè)務(wù)邏輯
   sendRequest(params);
});
// 按鈕點(diǎn)擊時(shí)發(fā)送埋點(diǎn)請(qǐng)求
$('button').click(function(){
   // ... 這里存在一些業(yè)務(wù)邏輯
   sendRequest(params);
});

可以很容易發(fā)現(xiàn)蛹含,這樣的做法很有可能會(huì)將埋點(diǎn)代碼侵入業(yè)務(wù)代碼,這使整體業(yè)務(wù)代碼變得繁瑣塞颁,容易出錯(cuò)浦箱,且后續(xù)代碼會(huì)愈加膨脹,難以維護(hù)祠锣。所以酷窥,我們需要讓埋點(diǎn)的代碼與具體的業(yè)務(wù)邏輯解耦,即 聲明式埋點(diǎn) 伴网,從而提高埋點(diǎn)的效率和代碼的可維護(hù)性蓬推。

聲明式埋點(diǎn)

理論上,聲明式埋點(diǎn)只需要關(guān)注兩個(gè)問(wèn)題:

需要埋點(diǎn)的DOM節(jié)點(diǎn)澡腾;
所需攜帶的數(shù)據(jù)
因此沸伏,可以很快想出一個(gè)聲明式埋點(diǎn)的方法:

// key表示埋點(diǎn)的唯一標(biāo)識(shí)糕珊;act表示埋點(diǎn)方式
<button data-stat="{key:'111', act: 'click'}">埋點(diǎn)</button>
那么可以去遍歷DOM樹(shù),找到 [data-stat] 的節(jié)點(diǎn)毅糟,給這個(gè)button綁上click事件红选,把這些參數(shù)在回調(diào)函數(shù)中通過(guò)請(qǐng)求發(fā)出去。

在DOM節(jié)點(diǎn)(html)上聲明埋點(diǎn)姆另,與業(yè)務(wù)邏輯(通常在Javascript文件中)就解耦了喇肋。調(diào)用也很方便。

看起來(lái)很美迹辐,但這樣就能解決問(wèn)題了嗎苟蹈?顯然是不夠的。還需要解決以下問(wèn)題:

遍歷DOM樹(shù)的時(shí)機(jī)問(wèn)題右核,一個(gè)簡(jiǎn)單的例子,一個(gè)表格的行數(shù)據(jù)是通過(guò)異步加載渺绒,而表格行中的操作按鈕需要埋點(diǎn)贺喝,那么在DOM ready的時(shí)候去遍歷,顯然是無(wú)法找到的
綁定埋點(diǎn)事件次數(shù)的問(wèn)題宗兼,怎樣保證埋點(diǎn)事件不會(huì)被重復(fù)綁定到元素上躏鱼,一次操作發(fā)了N個(gè)埋點(diǎn)請(qǐng)求?
如何處理特有的埋點(diǎn)行為,如頁(yè)面展現(xiàn)埋點(diǎn)殷绍,區(qū)域展現(xiàn)埋點(diǎn)?
如何在解綁時(shí)染苛,銷毀已綁定的事件?

1.自定義指令實(shí)現(xiàn)埋點(diǎn)數(shù)據(jù)統(tǒng)計(jì)
在項(xiàng)目中通常需要做數(shù)據(jù)埋點(diǎn),這個(gè)時(shí)候主到,使用自定義指令將會(huì)變非常簡(jiǎn)單

在項(xiàng)目入口文件 main.js 中配置我們的自定義指令

// 坑位埋點(diǎn)指令

Vue.directive('stat', {
  bind(el, binding) {
    el.addEventListener('click', () => {
      const data = binding.value;
      let prefix = 'store';
      if (OS.isAndroid || OS.isPhone) {
        prefix = 'mall';
      }
      analytics.request({
        ty: `${prefix}_${data.type}`,
        dc: data.desc || ''
      }, 'n');
    }, false);
  }
});

2.使用路由攔截統(tǒng)計(jì)頁(yè)面級(jí)別的 PV
由于第一次在單頁(yè)應(yīng)用中嘗試數(shù)據(jù)埋點(diǎn)茶行,在項(xiàng)目上線一個(gè)星期之后,數(shù)據(jù)統(tǒng)計(jì)后臺(tái)發(fā)現(xiàn)登钥,首頁(yè)的 PV 遠(yuǎn)遠(yuǎn)高于其它頁(yè)面畔师,數(shù)據(jù)很不正常。后來(lái)跟數(shù)據(jù)后臺(tái)的人溝通詢問(wèn)他們的埋點(diǎn)統(tǒng)計(jì)原理之后牧牢,才發(fā)現(xiàn)其中的問(wèn)題所在看锉。

傳統(tǒng)應(yīng)用,一般都在頁(yè)面加載的時(shí)候塔鳍,會(huì)有一個(gè)異步的 js 加載伯铣,就像百度的統(tǒng)計(jì)代碼類似,所以我們每個(gè)頁(yè)面的加載的時(shí)候轮纫,都會(huì)統(tǒng)計(jì)到數(shù)據(jù)腔寡;然而在單頁(yè)應(yīng)用,頁(yè)面加載初始化只有一次蜡感,所以其它頁(yè)面的統(tǒng)計(jì)數(shù)據(jù)需要我們自己手動(dòng)上報(bào)

解決方案

使用 vue-routerbeforeEach 或者 afterEach 鉤子上報(bào)數(shù)據(jù)蹬蚁,具體使用哪個(gè)最好是根據(jù)業(yè)務(wù)邏輯來(lái)選擇恃泪。

const analyticsRequest = (to, from) => {
  // 只統(tǒng)計(jì)頁(yè)面跳轉(zhuǎn)數(shù)據(jù),不統(tǒng)計(jì)當(dāng)前頁(yè) query 不同的數(shù)據(jù)
  // 所以這里只使用了 path, 如果需要統(tǒng)計(jì) query 的犀斋,可以使用 to.fullPath
  if (to.path !== from.path) {
    analytics.request({
      url: `${location.protocol}//${location.host}${to.path}`
    });
  }
};

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 這里做登錄等前置邏輯判斷
    // 判斷通過(guò)之后贝乎,再上報(bào)數(shù)據(jù)
    ...
    analyticsRequest(to, from);
  } else {
    // 不需要判斷的,直接上報(bào)數(shù)據(jù)
    analyticsRequest(to, from);
    next();
  }
});

在組件中使用我們的自定義指令


image.png
基于 jquery + widget 的老項(xiàng)目叽粹,

那么在這些項(xiàng)目中的DOM操作是jquery甚至原生DOM API來(lái)實(shí)現(xiàn)览效,Vue的自定義指令就無(wú)法工作

基于MutationObserver API的Mixin

MutationObserver是在DOM3標(biāo)準(zhǔn)中提出的標(biāo)準(zhǔn)API,提供讓開(kāi)發(fā)者感知到在某一個(gè)DOM節(jié)點(diǎn)變更的能力虫几〈覆樱可以監(jiān)聽(tīng)以下場(chǎng)景:

  • childList: 目標(biāo)節(jié)點(diǎn)的子節(jié)點(diǎn)插入刪除引起的變更

  • attributes: 目標(biāo)節(jié)點(diǎn)屬性改變引起的變更

  • characterData: 目標(biāo)節(jié)點(diǎn)的文本節(jié)點(diǎn)改變引起的變更,如通過(guò)appendData()等

  • subtree: 目標(biāo)節(jié)點(diǎn)的子孫節(jié)點(diǎn)改變引起的變更

  • attributeOldValue:當(dāng)attribute監(jiān)聽(tīng)被設(shè)定為true時(shí)辆脸,可以記錄改變前的屬性值

  • characterDataOldValue:當(dāng)characterData監(jiān)聽(tīng)被設(shè)定為true時(shí)但校,可以記錄改變前的屬性值

  • attributeFilter:可以設(shè)定需要監(jiān)聽(tīng)的屬性列表

但為了保證MutationObserver可以在所有瀏覽器上正常工作,我們?nèi)匀灰肓诉@個(gè)API的polyfill,詳情可見(jiàn)這里啡氢。

在此能力的前提下状囱,我們就可以在任意的DOM操作下觸發(fā)Vue進(jìn)行重新解析指令。

我們將 MutationObserver 封裝進(jìn)一個(gè) Vue mixin , 非Vue應(yīng)用的業(yè)務(wù)代碼只需要引入這個(gè)mixin倘是,這樣也可以很好地解耦亭枷。

詳細(xì)的實(shí)現(xiàn)原理可以見(jiàn)以下偽代碼:

let observer;
export default {
  ready() {
    // 開(kāi)啟監(jiān)聽(tīng)
    observer = new MutationObserver(mutations => {
      this.$compile(this.$el);
    });
    observer.observe(this.$el, config);
  },
  destroyed() {
    // 清理工作
    observer.disconnect();
    observer.takeRecords();
  }
}

參考:
https://segmentfault.com/a/1190000011066120
https://zhuanlan.zhihu.com/p/27659302
http://webcomponents.org/polyfills/
https://www.zhihu.com/question/67951942/answer/259125217

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搀崭,隨后出現(xiàn)的幾起案子叨粘,更是在濱河造成了極大的恐慌,老刑警劉巖瘤睹,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件升敲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡轰传,警方通過(guò)查閱死者的電腦和手機(jī)冻晤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绸吸,“玉大人鼻弧,你說(shuō)我怎么就攤上這事〗踝拢” “怎么了攘轩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)码俩。 經(jīng)常有香客問(wèn)我度帮,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上鸯匹,老公的妹妹穿的比我還像新娘半火。我一直安慰自己琅攘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般腺晾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辜贵,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天悯蝉,我揣著相機(jī)與錄音,去河邊找鬼托慨。 笑死鼻由,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的厚棵。 我是一名探鬼主播嗡靡,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窟感!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起歉井,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柿祈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后哩至,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躏嚎,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年菩貌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卢佣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡箭阶,死狀恐怖虚茶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仇参,我是刑警寧澤嘹叫,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站诈乒,受9級(jí)特大地震影響罩扇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一喂饥、第九天 我趴在偏房一處隱蔽的房頂上張望消约。 院中可真熱鬧,春花似錦员帮、人聲如沸或粮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)被啼。三九已至,卻和暖如春棠枉,著一層夾襖步出監(jiān)牢的瞬間浓体,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工辈讶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留命浴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓贱除,卻偏偏與公主長(zhǎng)得像生闲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子月幌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345