通過自定義 Vue 指令實現前端曝光埋點

前言

互聯網發展至今,數據的重要性已經不言而喻,尤其是在電商公司,數據的統計分析尤爲重要,通過數據分析可以提升用戶的購買體驗,方便運營和產品調整銷售策略等等。埋點就是網站分析的一種常用的數據採集方法。

埋點按照獲取數據的方式一般可以分爲以下 3 種:

  • 頁面埋點:統計用戶進入或離開頁面的各種維度信息,如頁面瀏覽次數(PV)、瀏覽頁面人數(UV)、頁面停留時間、瀏覽器信息等。
  • 點擊埋點:統計用戶在應用內的每一次點擊事件,如新聞的瀏覽次數、文件下載的次數、推薦商品的命中次數等。
  • 曝光埋點:統計具體區域是否被用戶瀏覽到,如活動的引流入口的顯示、投放廣告的顯示等。

政採雲前端團隊(ZooTeam)通過渾儀系統實現數據採集及數據可視化,分析量化的能力,想了解渾儀系統或者還不瞭解埋點的同學可以先閱讀 前端工程實踐之數據埋點分析系統

渾儀系統的數據採集是基於代碼侵入式埋點方案實現的,提供了自動發送和手動調用埋點信息上報接口發送兩種方式實現埋點數據上報。其中頁面埋點點擊埋點是使用自動上報的方式實現,在 DOM 節點掛載特殊屬性,通過埋點採集 JSSDK 監聽掛載了相應屬性對應的事件,在事件觸發時進行埋點數據上報。曝光埋點由於涉及到有效曝光邏輯的判斷,自動上報不能滿足相應的需求,所以我們採用手動調用接口方式進行埋點數據上報。

有效曝光

先舉個例子:

上圖是某電商首頁底部的推薦區域,爲了衡量用戶對推薦結果的感興趣程度,需要計算推薦區域的點擊率(點擊次數/曝光次數)。爲了保證點擊率的準確性,我們必須確保用戶真正的瀏覽到了這些商品,由於用戶瀏覽商品的不確定性會發生相同商品的頻繁曝光,我們還要避免重複的曝光行爲。所以我們需要制定一套邏輯來規定何時進行曝光埋點的數據上報。比如:

  1. 商品卡片必須完全的出現在瀏覽器可視化區域內。
  2. 商品必須在可視化區域內停留 5s 以上。
  3. 用戶進入頁面到離開頁面相同的商品只進行一次曝光。

滿足以上規定的曝光就是一次有效曝光。瞭解了有效曝光後,我們來看看曝光埋點實現最重要的一環,如何判斷元素出現在頁面的可視化區域內。

判斷元素出現在頁面的可視化區域內

我們首先想到商品曝光類似於圖片懶加載的形式,通過監聽 scroll 事件,調用 Element.getBoundingClientRect() 方法以獲取相關元素的邊界信息,然後判斷元素是否出現在頁面的可視化區域內。由於 scroll 事件頻發觸發,計算量很大,所以很容易造成性能問題,雖然我們可以採用防抖節流等方式去解決。

目前有一個新的 IntersectionObserver API,提供了一種異步檢測目標元素與祖先元素或 viewport(可視窗口)相交情況變化的方法。可以自動"觀察"元素是否可見。

IntersectionObserver基本用法

let options = {
    rootdocument.querySelector('#scrollArea'),
    rootMargin'0px',
    threshold1.0
}
let callback =(entries, observer) => {
  entries.forEach(entry => {});
};
let observer = new IntersectionObserver(callback, options);

IntersectionObserver 是瀏覽器原生提供的構造函數,接受兩個參數:callback 是可見性變化時的回調函數,option 是配置對象(該參數可選),返回一個 observer 實例。我們可以看到,創建一個 IntersectionObserver 對象,接受兩個參數:callback 可見性變化時的回調函數,該回調函數將會在目標(target)元素和根(root)元素的交集大小超過閾值(threshold)規定的大小時候被執行。

options 是配置對象,它有以下字段:

  • root:指定根 ( root) 元素,用於檢查目標的可見性。必須是目標元素的父級元素。如果未指定或者爲  null,則默認爲瀏覽器視窗。
  • rootMargin:根 ( root) 元素的外邊距。類似於 CSS 中的 margin 屬性。默認值爲 0。
  • threshold:target 元素和 root 元素相交程度達到該值的時候  callback 函數將會被執行,可以是單一的Number 也可以是 Number 數組,當爲數組時每達到該值都會執行  callback 函數。

我們通過實例的方法可以指定觀察哪個 DOM 節點。實例的方法有:

  • IntersectionObserver.observe():使 IntersectionObserver 開始監聽一個目標元素。
  • IntersectionObserver.disconnect():使 IntersectionObserver 對象停止監聽工作。
  • IntersectionObserver.takeRecords():返回所有觀察目標的 IntersectionObserverEntry 對象數組。
  • IntersectionObserver.unobserve():使 IntersectionObserver 停止監聽特定目標元素。

IntersectionObserverEntry 對象提供目標元素的信息,一共有七個屬性:

  • IntersectionObserverEntry.target :需要觀察的目標元素,是一個 DOM 節點對象 。
  • IntersectionObserverEntry.boundingClientRect:返回包含目標元素的邊界信息。邊界的計算方式與  Element.getBoundingClientRect() 相同。
  • IntersectionObserverEntry.intersectionRect :用來描述根和目標元素的相交區域的信息。
  • IntersectionObserverEntry.intersectionRatio:返回  intersectionRect 與  boundingClientRect 的比例值,0 爲完全不可見,1 爲完全可見。
  • IntersectionObserverEntry.isIntersecting:返回一個布爾值, 如果根與目標元素相交(即從不可視狀態變爲可視狀態),則返回  true。如果返回  false,變換是從可視狀態到不可視狀態。
  • IntersectionObserverEntry.rootBounds :根元素的區域的信息。
  • IntersectionObserverEntry.time:可見性狀態發生改變時間的時間戳,單位爲毫秒。

目標元素的可見性變化時,就會調用觀察器的回調函數 callbackcallback函數的參數 entries 是一個數組,每個成員都是一個 IntersectionObserverEntry 對象,observer 是被調用的 IntersectionObserver 實例。callback 函數一般會被調用兩次,一次是目標元素進入可視化區域,另一次是離開可視化區域。配置 options.threshold 會影響 callback 函數的調用次數。

我們再來看看 Intersection Observer API 的瀏覽器兼容情況

我們看到是存在兼容性問題的,好在已經有了兼容的 polyfill (https://github.com/w3c/IntersectionObserver/tree/master/polyfill)。當前瀏覽器不支持 Intersection Observer API 時,使用 Element.getBoundingClientRect() 去實現 Intersection Observer API。

具體實現

瞭解了 Intersection Observer 的基本用法了以後,下面我們來實現前端的曝光埋點。因爲業務是基於 Vue 實現的,所以我們通過自定義 Vue 指令實現前端的曝光埋點。

首先我們自定義一個 visually 指令,當指令第一次綁定在元素上時使用 IntersectionObserver 監聽目標元素,當指令從元素上解綁時停止監聽目標元素。

const options = {
    rootnull//默認瀏覽器視窗
    threshold1 //元素完全出現在瀏覽器視窗內才執行callback函數。
}
const callback =(entries, observer) => {
  entries.forEach(entry => {});
};
const observer = new IntersectionObserver(callback, options);
const addListenner = (ele, binding) => {
 observer.observe(ele);
};
const removeListener = (ele) => {
  observer.unobserve(ele);
};
//自定義曝光指令
Vue.directive('visually', {
  bind: addListenner,
  unbind: removeListener,
});

我們需要一個 List 將已經上報過的埋點信息記錄下來,防止重複曝光。

let visuallyList = []; //記錄已經上報過的埋點信息
const addListenner = (ele, binding) => {
 if(visuallyList.indexOf(binding.value) !== -1return;
 
 observer.observe(ele);
};

我們將要上報的信息綁定在目標元素的 'visually-data' 屬性中,當目標元素出現在視窗內時,並停留 5 秒以上時,我們上報埋點信息。

let timer = {}; //增加定時器對象
const callback = entries => {
  entries.forEach(entry => {
    let visuallyData = null;
    try {
      visuallyData = JSON.parse(entry.target.getAttribute('visually-data'));
    } catch (e) {
      visuallyData = null;
      console.error('埋點數據格式異常', e);
    }
    //沒有埋點數據取消上報
    if (!visuallyData) {
      observer.unobserve(entry.target);
      return;
    }
    
    if (entry.isIntersecting) {
      timer[visuallyData.id] = setTimeout(function({
        //上報埋點信息
        sendUtm(visuallyData).then(res => {
          if (res.success) {
            //上報成功後取消監聽
            observer.unobserve(entry.target);
            visuallyList.push(visuallyData.id);
            timer[visuallyData.id] = null;
          }
        });
      }, 5000);
  } else {
    if (timer[visuallyData.id]) {
      clearTimeout(timer[visuallyData.id]);
      timer[visuallyData.id] = null;
    }
  }
  });
};

最後我們引入 polyfill 實現 IE 的兼容,封裝一個全局指令。

require('intersection-observer');
export default Vue => {
 ...
  //自定義曝光指令
  Vue.directive('visually', {
    bind: addListenner,
    unbind: removeListener,
  });
};

我們通過 Vue.use() 引入組件後,就可以在業務代碼中直接通過指令實現曝光埋點。曝光數據 visuallyData 中必須要有一個唯一 ID。

<div v-visually="visuallyData.id" :visually-data="JSON.stringify(visuallyData)" class="browse"></div>

總結

埋點是數據分析的基礎,埋點數據統計的準確性對後續的數據分析非常重要,所以我們在統計曝光埋點的時候一定要基於適用場景優先制定曝光埋點的規則。本文只是針對前端曝光埋點的實現方案,如有問題處,請大佬們多多交流。

本文分享自微信公衆號 - 1024譯站(trans1024)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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