<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          轉(zhuǎn)轉(zhuǎn)前端自動埋點核心原理揭秘

          共 4478字,需瀏覽 9分鐘

           ·

          2021-12-18 18:09

          大廠技術(shù)??高級前端??Node進階

          點擊上方?程序員成長指北,關(guān)注公眾號

          回復(fù)1,加入高級Node交流群

          前言

          項目上線幾天后,產(chǎn)品同學(xué)突然向你要一個需求文檔內(nèi)沒有,但很重要的埋點,怎么辦?是不是又到了甩鍋的時間了?為了避免這種情況,且鑒于我司"沒有落地的決策是垃圾"的精神,我們產(chǎn)出了這個自動埋點工具。

          調(diào)研

          現(xiàn)在業(yè)界有很多數(shù)據(jù)采集的廠商,像 Heap、GrowingIo、神策等。也都提出了可視化埋點、無埋點等方案,其中可視化埋點是需要圈選的,并不能滿足我們的需求。而無埋點方案也分很多種,一種是保存所有用戶點擊的 xpath,然后交由后端進行分析,另一種是分析所有標(biāo)簽,收集所有的控件,當(dāng)用戶點擊到控件后進行發(fā)送。這兩種方案雖然能解決我們一部分問題,但是功能還遠遠不夠,暴露的問題也很多,像:

          1. 不能靈活的自定義屬性
          2. 傳輸時效差
          3. 數(shù)據(jù)可靠性欠佳
          4. 服務(wù)器和網(wǎng)絡(luò)傳輸壓力更大 等問題

          至此業(yè)界暫時沒有一個完美的方案解決這個問題。所以我們拋棄了上述所有方案,決定實現(xiàn)一套更優(yōu)的方案-自動埋點。

          以往大家思想都是從頁面結(jié)構(gòu)出發(fā),而我們是直接基于事件的角度來實現(xiàn)的。下面就來聊一聊我們實現(xiàn)的核心方法

          關(guān)鍵技術(shù)

          1. 豐富的配置項
          2. 事件捕獲
          3. 埋點發(fā)送

          配置項

          一個功能強大的庫,少不了豐富的配置功能。我們的配置項主要分為使用范圍、自定義上報事件、路由配置、頁面類型等等。其中使用范圍,我們控制了發(fā)送埋點是項目級的還是頁面級的。自定義上報事件是用來區(qū)分具體上報曝光還是點擊或者其他埋點的。路由配置支持 hash 和 history 類型。頁面類型主要是用來控制發(fā)送參數(shù)時,參數(shù)組合格式配置等等。具體實現(xiàn)暫不展開說明,也非本文重點,接下來我們說一下這個方案的核心模塊

          事件捕獲

          首先我們知道,項目中埋點上報大體分為:

          1. 曝光
          2. 點擊
          3. 模塊曝光
          4. 關(guān)閉 ...

          那我們完全可以捕捉到這些事件,并觸發(fā)埋點就可以達到我們的目的了,接下來我們逐個分析。

          曝光埋點

          曝光埋點我將其分為三種:

          1. 直接進入全新頁面
          2. SPA 項目的頁面跳轉(zhuǎn)后的頁面
          3. 當(dāng)前頁面隱藏后再喚出的情況

          第一種的解決方案:前期實現(xiàn),認(rèn)為加載了這個 js,就算是曝光了,但是后期因為業(yè)務(wù)復(fù)雜度問題,用戶狀態(tài)繁多,我們將其放置到首個接口請求后進行發(fā)送,這樣就可以讓統(tǒng)計更精細化,也更符合 PM 的預(yù)期。

          第二種的解決方案:hash 路由:我們可以通過監(jiān)聽hashchange解決 history 路由:我們對pushStatereplaceState等原生事件進行了重寫,直接上代碼

          let?that?=?this;
          let?reWrite?=?function?(type)?{
          ??let?real?=?history[type];
          ??return?function?()?{
          ????let?realFun?=?real.apply(this,?arguments);
          ????let?newEvent?=?new?Event(type?+?'AutoLego');
          ????newEvent.arguments?=?arguments;
          ????window.dispatchEvent(newEvent);
          ????return?realFun;
          ??};
          };
          history.pushState?=?reWrite('pushState');
          history.replaceState?=?reWrite('replaceState');
          window.addEventListener('replaceStateAutoLego',?function?(e)?{
          ??that.skipRouterCommon();
          });
          window.addEventListener('pushStateAutoLego',?function?(e)?{
          ??that.skipRouterCommon();
          });

          在執(zhí)行生的pushStatereplaceState之后調(diào)用我們自定義事件pushStateAutoLegoreplaceStateAutoLego,來達到發(fā)送埋點的目的

          第三種的解決方案:這個比較簡單,就是監(jiān)聽visibilitychange事件,來發(fā)送埋點

          document.addEventListener('visibilitychange',?()?=>?{
          ??if?(document.visibilityState?===?'visible')?{
          ????that.showLego();
          ??}
          });

          點擊埋點

          為了更加精準(zhǔn)的發(fā)送埋點,滿足實時性、自定義屬性、可靠性等特點,這邊采用重寫addEventListener方法并配合當(dāng)前點擊 DOM 節(jié)點的屬性來進行實現(xiàn),,部分代碼邏輯

          //?擴展監(jiān)聽事件
          Element.prototype.realAddEventListener?=?Element.prototype.addEventListener;
          Element.prototype.addEventListener?=?function?(a,?b,?c)?{
          ??if?(a?===?'click')?{
          ????this.realAddEventListener(a,?reWriteClick(b),?c);
          ??}?else?{
          ????this.realAddEventListener(a,?b,?c);
          ??}
          };
          //?擴展移出監(jiān)聽事件
          Element.prototype.realRemoveEventListener?=
          ??Element.prototype.removeEventListener;
          Element.prototype.removeEventListener?=?function?(a,?b,?c)?{
          ??if?(a?===?'click')?{
          ????this.realRemoveEventListener(a,?reWriteClick(b),?c);
          ??}?else?{
          ????this.realRemoveEventListener(a,?b,?c);
          ??}
          };

          其中只針對click事件進行了重寫,在執(zhí)行真正的點擊回調(diào)函數(shù)b之前,進行數(shù)據(jù)處理并發(fā)送埋點。這樣就實現(xiàn)了針對真實的點擊事件進行捕捉,避免了收集大量的無效點擊和自定義屬性等問題,也不用做定時發(fā)送這樣的邏輯來影響性能了。

          模塊曝光

          模塊曝光顧名思義,就是只某個模塊展現(xiàn),就會發(fā)送一個展現(xiàn)埋點。通常應(yīng)用在不同狀態(tài)展示不同模塊的情況。遺憾的是,因為技術(shù)有限,我還無法將其完全自動化,只能做到半自動化(⊙︿⊙),如果大家有更好的方法,希望大家不吝賜教。下邊說一下,我實現(xiàn)的具體方法。我針對要曝光的模塊,在 dom 上做了一個標(biāo)記autolego-,當(dāng) dom 發(fā)生變化時,匹配到這個標(biāo)記的時候,發(fā)送埋點,具體實現(xiàn)如下:

          //?代碼略有刪減
          let?that?=?this;
          var?MutationObserver?=?window.MutationObserver;
          function?dfs(item)?{
          ??if?(item.id.indexOf('autolego-')?>?-1)?{
          ????that.showElLego(item);
          ??}
          ??if?(item.childNodes)?{
          ????item.childNodes.forEach((childItem)?=>?{
          ??????dfs(childItem);
          ????});
          ??}
          }
          var?observer?=?new?MutationObserver(function?(mutations,?observer)?{
          ??mutations.forEach((item)?=>?{
          ????dfs(item.target);
          ??});
          });
          observer.observe(document.body,?{
          ??subtree:?true,
          ??childList:?true,
          ??attributes:?true,
          });

          從代碼中,大家可以發(fā)現(xiàn),核心點只有兩個,其中一個是利用了MutationObserver方法,另一個是對 DOM 樹進行了深度遍歷。

          關(guān)閉

          因為瀏覽器的兼容問題的存在,關(guān)閉事件這邊分別采用onpagehideonbeforeunload對 IOS 和其他系統(tǒng)來區(qū)分實現(xiàn)

          if?(!!navigator.userAgent.match(/\(i[^;]+;(?U;)??CPU.+Mac?OS?X/))?{
          ??window.onpagehide?=?()?=>?{
          ????this.unLoadLego();
          ??};
          }?else?{
          ??window.onbeforeunload?=?()?=>?{
          ????this.unLoadLego();
          ??};
          }

          事件捕獲我們暫且說到這里,延伸這個思路,我們還可以做的更多,數(shù)據(jù)更加全面準(zhǔn)確。

          發(fā)送

          在發(fā)送埋點這里大家需要注意一下,像關(guān)閉埋點,如果頁面關(guān)閉過快,我們的埋點會被 abort 掉,所以這里推薦大家用 ajax 來實現(xiàn), 如果對兼容性要求不高,也可以用navigator.sendBeacon來實現(xiàn),這個方法主要是為了滿足統(tǒng)計和診斷代碼而生的,也希望以后兼容性能越來越好。

          if?(navigator.sendBeacon)?{
          ??var?fd?=?new?FormData();
          ??fd.append('legoType',?'autoLego');
          ??navigator.sendBeacon(`https://${urlPath}`,?fd);
          }

          效果

          如圖可見,當(dāng)點擊 banner 的時候,自動發(fā)送了點擊埋點,同時觸發(fā)了關(guān)閉頁面的埋點,以及進入到下個頁面的曝光埋點。與此同時,在 backup 區(qū)域,也帶上了更多信息,數(shù)據(jù)更加的全面精準(zhǔn)。備注:效果是通過我們的可視化埋點平臺演示的,后期有機會我們也會介紹,敬請期待

          總結(jié)

          通過上面的介紹,相信大家已經(jīng)對 自動埋點 的核心技術(shù)已經(jīng)很清楚了。

          其實就是針對生方法的重寫來實現(xiàn)同步發(fā)送埋點的效果。

          相較于市面上的無埋點方案,自動埋點的可控性,可擴展性也是非常有優(yōu)勢的。

          關(guān)于自動埋點暫時只寫到這里啦,雖然有些功能還有欠缺,但我相信,不斷迭代擴展是肯定可以替代掉手動埋點的。不對的地方也希望大家多多指正,一起進步~

          Node 社群


          我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。


          ???“分享、點贊、在看” 支持一波??

          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人久久免费 | 久久久六月 | 色综合色 | 国产日韩欧美视频 | 激情婷婷综合 |