您好,登錄后才能下訂單哦!
本篇內容介紹了“JS前端監控采集用戶行為的方法有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
在一個產品中,用戶最基本的行為就是切換頁面。用戶使用了哪些功能,也能從切換頁面中體現出來。因此通用數據一般是在頁面切換時產生,表示某個用戶訪問了某個頁面。
頁面切換對應到前端就是路由切換,可以通過監聽路由變化來拿到新頁面的數據。Vue 在全局路由守衛中監聽路由變化,任意路由切換都能執行這里的回調函數。
// Vue3 路由寫法 const router = createRouter({ ... }) router.beforeEach(to => { // to 代表新頁面的路由對象 recordBehaviors(to) })
React 在組件的 useEffect 中實現相同的功能。不過要注意一點,監聽所有路由變化,則需要所有路由都經過這個組件,監聽才有效果。具體的方法是配置路由時加 *
配置:
import HomePage from '@/pages/Home' <Route path="*" component={HomePage} />,
然后在這個組件的的 useEffect 中監聽路由變化:
// HomePage.jsx const { pathname } = useLocation(); useEffect(() => { // 路由切換這個函數觸發 recordBehaviors(pathname); }, [pathname]);
上面代碼中,在路由切換時都調用了 recordBehaviors()
方法并傳入了參數。Vue 傳的是一個路由對象,React 傳的是路由地址,接下來就可以在這個函數內收集數據了。
明確了在哪里收集數據,我們還要知道收集哪些數據。收集行為數據最基本的字段如下:
app
:應用的名稱/標識
env
:應用環境,一般是開發,測試,生產
version
:應用的版本號
user_id
:當前用戶 ID
user_name
:當前用戶名
page_route
:頁面路由
page_title
:頁面名稱
start_at
:進入時間
end_at
:離開時間
上面的字段中,應用標識、環境、版本號統稱應用字段,用于標志數據的來源。其他字段主要分為 用戶,頁面,時間三類,通過這三類數據就可以簡單的判斷出一件事:誰到過哪個頁面,并停留了多長時間。
應用字段的配置和獲取方式我們在上一節 搭建前端監控,如何采集異常數據? 中講過,就不做多余介紹了,獲取字段的方式都是通用的。
下面介紹其他的幾類數據如何獲取。
現代前端應用存儲用戶信息的方式基本都是一樣的,localStorage 存一份,狀態管理里存一份。因此獲取用戶信息從這兩處的任意一處獲得即可。這里簡單介紹下如何從狀態管理中獲取。
最簡單的方法,在函數 recordBehaviors()
所處的 js 文件中,直接導入用戶狀態:
// 從狀態管理里中導出用戶數據 import { UserStore } from '@/stores'; let { user_id, user_name } = UserStore;
這里的 @/stores
指向我項目中的文件 src/stores/index.ts
,表示狀態管理的入口文件,使用時替換成自己項目的實際位置。實際情況中還會有用戶數據為空的問題,這里需要單獨處理一下,方便我們在后續的數據查看中能看出分別:
import { UserStore } from '@/stores'; // 收集行為函數 const recordBehaviors = ()=> { let report_date = { ... } if(UserStore) { let { user_id, user_name} = UserStore report_date.user_id = user_id || 0 report_date.user_name = user_name || '未命名' } else { report_date.user_id = user_id || -1 report_date.user_name = user_name || '未獲取' } }
上面代碼中,首先判斷了狀態管理中是否有用戶數據,如果有則獲取,沒有則指定默認值。這里指定默認值的細節要注意,不是隨便指定的,比如 user_id 的默認值有如下意義:
user_id 為 0
:表示有用戶數據,但沒有 user_id 字段或該字段為空
user_id 為 -1
:表示沒有用戶數據,因而 user_id 字段獲取不到
用戶數據是經常容易出錯的地方,因為涉及到登錄狀態和權限等復雜問題。指定了上述默認值后,就可以從收集到的行為數據中判斷出某個頁面用戶狀態是否正常。
前面我們在監聽路由變化的地方調用了 recordBehaviors 函數并傳入了參數,頁面信息可以從參數中拿到,我們先看在 Vue 中怎么獲取:
// 路由配置 { path: '/test', meta: { title: '測試頁面' }, component: () => import('@/views/test/Index.vue') } // 獲取配置 const recordBehaviors = (to)=> { let page_route = to.path let page_title = to.meta.title }
Vue 中比較簡單,可以直接從參數中拿到頁面數據。相比之下,React 的參數只是一個路由地址,想拿到頁面名稱還需要做單獨處理。
一般在設計權限時,我們會在服務端會維護一套路由數據,包含路由地址和名稱。路由數據在登錄后獲取,存在狀態管理中,那么有了 pathname 就可以從路由數據中找到對應的路由名稱。
// React 中 import { RouteStore } from '@/stores'; const recordBehaviors = (pathname) => { let { routers } = RouteStore; // 取出路由數據 let route = routers.find((row) => (row.path = pathname)); if (route) { let page_route = route.path; let page_title = route.title; } };
這樣,頁面信息的 page_route、page_title 兩個字段也拿到了。
行為數據中用兩個字段 start_at
、end_at
分別表示用戶進入頁面和離開頁面的時間。這兩個字段非常重要,我們在后續使用數據的時候可以判斷出很多信息,比如:
某個用戶在某個頁面停留了多久?
某個段時間內,某個用戶停留在哪幾個頁面?
某個時間段內,哪個頁面的用戶停留時間最長?
某個頁面,哪些用戶的使用率最高?
還有很多信息,都能根據這兩個時間字段判斷。開始時間很好辦,函數觸發時直接獲取當前時間:
var start_at = new Date();
結束時間這里需要考慮的情況比較多。首先要確定數據什么時候上報?用戶進入頁面后上報,還是離開頁面時上報?
如果進入頁面時上報,可以保證行為數據一定會被記錄,不會丟失,但此時 end_at 字段必然為空。這樣的話,就需要在離開頁面時再調接口,將這條記錄的 end_time 更新,這種方式的實現比較麻煩一些:
// 進入頁面時調用 const recordBehaviors = () => { let report_date = {...} // 此時 end_at 為空 http.post('/behaviors/insert', report_date).then(res=> { let id = res.id // 數據 id localStorage.setItem('CURRENT_BEHAVIOR_ID', id) }) } // 離開頁面時調用: const updateBehaviors = ()=> { let id = localStorage.getItem('CURRENT_BEHAVIOR_ID') let end_at = new Date() http.post('/behaviors/update/'+id, end_at) // 根據 id 更新結束時間 localStorage.removeItem('CURRENT_BEHAVIOR_ID') }
上面代碼中,進入頁面先上報數據,并保存下 id,離開頁面再根據 id 更新這條數據的結束時間。
如果在離開頁面時上報,那么就要保證離開頁面前上報接口已經觸發,否則會導致數據丟失。在滿足這個前提條件下,上報邏輯會變成這樣:
// 進入頁面時調用 const recordBehaviors = () => { let report_date = {...} // 此時 end_at 為空 localStorage.setItem('CURRENT_BEHAVIOR', JSON.stringify(report_date)); } // 離開頁面時調用 const reportBehaviors = () => { let end_at = new Date() let report_str = localStorage.getItem('CURRENT_BEHAVIOR') if(report_str) { let report_date = JSON.parse(report_str) report_date.end_at = end_at http.post('/behaviors/insert', report_date) } else { console.log('無行為數據') } }
對比一下這兩種方案,第一種的弊端是接口需要調兩次,這會使接口請求量倍增。第二種方案只調用一次,但是需要特別注意可靠性處理,總體來說第二種方案更好些。
除了通用數據,大部分情況我們還要在具體的頁面中收集某些特定的行為。比如某個關鍵的按鈕有沒有點擊,點了多少次;或者某個關鍵區域用戶有沒有看到,看到(曝光)了多少次等等。
收集數據還有一個更專業的叫法 ———— 埋點。直觀理解是,哪里需要上報數據,就埋一個上報函數進去。
通用數據針對所有頁面自動收集,特定數據就需要根據每個頁面的實際需求手動添加。以一個按鈕為例:
<button onClick={onClick}>點擊</button>; const onClick = (e) => { // console.log(e); repoerEvents(e); };
上面代碼中,我們想記錄這個按鈕的點擊情況,所以做了一個簡單的埋點 ———— 在按鈕點擊事件中調用 repoerEvents()
方法,這個方法內部會收集數據并上報。
這是最原始的埋點方式,直接將上報方法放到事件函數中。repoerEvents() 方法接收一個事件對象參數,在參數中獲取需要上報的事件數據。
特定數據與通用數據的許多字段是一樣的,收集特定數據需要的基本字段如下:
app
:應用的名稱/標識
env
:應用環境,一般是開發,測試,生產
version
:應用的版本號
user_id
:當前用戶 ID
user_name
:當前用戶名
page_route
:頁面路由
page_title
:頁面名稱
created_at
:觸發時間
event_type
:事件類型
action_tag
:行為標識
action_label
:行為描述
這些基本字段中,前 7 個字段與前面通用數據的獲取完全一樣,這里就不贅述了。實際上特定數據需要獲取的專有字段只有 3 個:
event_type
:事件類型
action_tag
:行為標識
action_label
:行為描述
這三個字段也非常容易獲取。event_type 表示事件觸發的類型,比如點擊、滾動、拖動等,可以在事件對象中拿到。action_tag 和 action_label 是必須指定的屬性,表示本次埋點的標識和文字描述,用于在后續的數據處理時方便查閱和統計。
了解了采集特定數據是怎么回事,接下來我們用代碼實現。
假設要為登錄按鈕做埋點,按照上面的數據采集方式,我們書寫代碼如下:
<button data-tag="user_login" data-label="用戶登錄" onClick={onClick}> 登錄 </button>; const onClick = (e) => { // console.log(e); repoerEvents(e); };
代碼中,我們通過元素的自定義屬性傳遞了 tag 和 label 兩個標識,用于在上報函數中獲取。
上報函數 repoerEvents() 代碼邏輯如下:
// 埋點上報函數 const repoerEvents = (e)=> { let report_date = {...} let { tag, label } = e.target.dataset if(!tag || !label) { return new Error('上報元素屬性缺失') } report_date.event_type = e.type report_date.action_tag = tag report_date.action_label = label // 上報數據 http.post('/events/insert', report_date) }
這樣就實現了一個基本的特定數據埋點上報功能。
現在我們回過頭來梳理一下這個上報流程,雖然基本功能實現了,但是還有些不合理之處,比如:
必須為元素指定事件處理函數
必須為元素添加自定義屬性
在原有事件處理函數中手動添加埋點,侵入性高
首先我們的埋點方式是基于事件的,也就是說,不管元素本身是否需要事件處理,我們都要給他加上,并在函數內部調用 repoerEvents() 方法。如果一個項目需要埋點的地方非常多,這種方式的接入成本就會非常高。
參考之前做異常監控的邏輯,我們換一個思路:能否全局監聽事件自動上報呢?
思考一下,如果要做全局監聽事件,那么只能監聽需要埋點的元素的事件。那么如何判斷哪些元素需要埋點呢?
上面我們為埋點的元素指定了 data-tag
和 data-label
兩個自定義屬性,那是不是根據這兩個自定義屬性判斷就可以?我們來試驗一下:
window.addEventListener('click', (event) => { let { tag, label, trigger } = event.target.dataset; if (tag && label && trigger == 'click') { // 說明該元素需要埋點 repoerEvents(event); } });
上面代碼還多判斷了一個自定義屬性 dataset.trigger,表示元素在哪種事件觸發時需要上報。全局監聽事件需要這個標識,這樣可避免事件沖突。
添加全局監聽后,收集某個元素的特定數據就簡單了,方法如下:
<button data-tag="form_save" data-label="表單保存" data-trigger="click"> 保存 </button>
試驗證明,上述全局處理的方式是可行的,這樣的話就不需要在每一個元素上添加或修改事件處理函數了,只需要在元素中添加三個自定義屬性 data-tag
,data-label
,data-trigger
就能自動實現數據埋點上報。
上面全局監聽事件上報的方式已經比手動埋點高效了許多,現在我們再換一個場景。
一般情況下當埋點功能成熟之后,會封裝成一個 SDK 供其他項目使用。如果我們將采集數據按照 SDK 的思路實現,讓開發者在全局監聽事件,是不是一個好的方式呢?
顯然是不太友好的。如果是一個 SDK,那么最好的方式是將所有內容聚合成一個組件,在組件內實現上報的所有功能,而不是讓使用者在項目中添加監聽事件。
封裝組件的話,那么組件的功能最好是將要添加埋點的元素包裹,這樣自定義元素也就不需要指定了,而轉為組件的屬性,然后在組件內實現事件監聽。
以 React 為例,我們看一下如何將上面的采集功能封裝為組件:
import { useEffect, useRef } from 'react'; const CusReport = (props) => { const dom = useRef(null); const handelEvent = () => { console.log(props); // {tag:xx, label:xx, trigger:xx} repoerEvents(props); }; useEffect(() => { if (dom.current instanceof HTMLElement) { dom.current.addEventListener(props.trigger, handelEvent); } }, []); return ( <span ref={dom} className="custom-report"> {props.children} </span> ); }; export default CusReport;
組件使用方式如下:
<CusReport tag="test" label="功能測試" trigger="click"> <button>測試</button> </CusReport>
這樣就比較優雅了,不需要修改目標元素,只要把組件包裹在目標元素之外即可。
“JS前端監控采集用戶行為的方法有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。