成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

騰訊二面:現(xiàn)在要你實(shí)現(xiàn)一個(gè)埋點(diǎn)監(jiān)控SDK,你會(huì)怎么設(shè)計(jì)?

開發(fā) 前端
我們要設(shè)計(jì)SDK,首先要清楚它的基本使用方法,才知道后面的代碼框架要怎么搭;然后是明確SDK的職能范圍:需要能處理用戶行為、頁(yè)面性能以及錯(cuò)誤報(bào)警三類監(jiān)控;最后是react、vue的項(xiàng)目,通常會(huì)做錯(cuò)誤邊界處理,要怎么接入我們自己的SDK。

大家好,我是年年!

這是小伙伴上周被問到的一個(gè)綜合性設(shè)計(jì)題,如果是沒有用過埋點(diǎn)監(jiān)控系統(tǒng),或者沒有深入了解,基本就涼涼。

這篇文章會(huì)講清楚:

  1. 埋點(diǎn)監(jiān)控系統(tǒng)負(fù)責(zé)處理哪些問題,需要怎么設(shè)計(jì)api?
  2. 為什么用img的src做請(qǐng)求的發(fā)送,sendBeacon又是什么?
  3. 在react、vue的錯(cuò)誤邊界中要怎么處理?

什么是埋點(diǎn)監(jiān)控SDK

舉個(gè)例子,公司開發(fā)上線了一個(gè)網(wǎng)站,但開發(fā)人員不可能預(yù)測(cè),用戶實(shí)際使用時(shí)會(huì)發(fā)生什么:用戶瀏覽過哪幾個(gè)頁(yè)面?幾成用戶會(huì)點(diǎn)擊某個(gè)彈窗的確認(rèn)按鈕,幾成會(huì)點(diǎn)擊取消?有沒有出現(xiàn)頁(yè)面崩潰?

所以我們需要一個(gè)埋點(diǎn)監(jiān)控SDK去做數(shù)據(jù)的收集,后續(xù)再統(tǒng)計(jì)分析。有了分析數(shù)據(jù),才能有針對(duì)性對(duì)網(wǎng)站進(jìn)行優(yōu)化:PV特別少的頁(yè)面就不要浪費(fèi)大量人力;有bug的頁(yè)面趕緊修復(fù),不然要325了。

比較有名的埋點(diǎn)監(jiān)控有Google Analytics,除了web端,還有iOS、安卓的SDK。

埋點(diǎn)監(jiān)控的職能范圍

因?yàn)闃I(yè)務(wù)需要的不同,大部分公司都會(huì)自己開發(fā)一套埋點(diǎn)監(jiān)控系統(tǒng),但基本上都會(huì)涵蓋這三類功能:

用戶行為監(jiān)控

負(fù)責(zé)統(tǒng)計(jì)PV(頁(yè)面訪問次數(shù))、UV(頁(yè)面訪問人數(shù))以及用戶的點(diǎn)擊操作等行為。

這類統(tǒng)計(jì)是用的最多的,有了這些數(shù)據(jù)才能量化我們的工作成果。

頁(yè)面性能監(jiān)控

開發(fā)和測(cè)試人員固然在上線之前會(huì)對(duì)這些數(shù)據(jù)做評(píng)估,但用戶的環(huán)境和我們不一樣,也許是3G網(wǎng),也許是很老的機(jī)型,我們需要知道在實(shí)際使用場(chǎng)景中的性能數(shù)據(jù),比如頁(yè)面加載時(shí)間、白屏?xí)r間等。

錯(cuò)誤報(bào)警監(jiān)控

獲取錯(cuò)誤數(shù)據(jù),及時(shí)處理才能避免大量用戶受到影響。除了全局捕獲到的錯(cuò)誤信息,還有在代碼內(nèi)部被catch住的錯(cuò)誤告警,這些都需要被收集到。

下面會(huì)從api的設(shè)計(jì)出發(fā),對(duì)上述三種類型進(jìn)一步展開。

SDK的設(shè)計(jì)

在開始設(shè)計(jì)之前,先看一下SDK怎么使用。

import StatisticSDK from 'StatisticSDK';
// 全局初始化一次
window.insSDK = new StatisticSDK('uuid-12345');
<button onClick={()=>{
window.insSDK.event('click','confirm');
...// 其他業(yè)務(wù)代碼
}}>確認(rèn)</button>

首先把SDK實(shí)例掛載到全局,之后在業(yè)務(wù)代碼中調(diào)用,這里的新建實(shí)例時(shí)需要傳入一個(gè)id,因?yàn)檫@個(gè)埋點(diǎn)監(jiān)控系統(tǒng)往往是給多個(gè)業(yè)務(wù)去使用的,通過id去區(qū)分不同的數(shù)據(jù)來源。

首先實(shí)現(xiàn)實(shí)例化部分:

class StatisticSDK {
constructor(productID){
this.productID = productID;
}
}

數(shù)據(jù)發(fā)送

數(shù)據(jù)發(fā)送是一個(gè)最基礎(chǔ)的api,后面的功能都要基于此進(jìn)行。通常這種前后端分離的場(chǎng)景會(huì)使用AJAX的方式發(fā)送數(shù)據(jù),但是這里使用圖片的src屬性。原因有兩點(diǎn):

  1. 沒有跨域的限制,像srcipt標(biāo)簽、img標(biāo)簽都可以直接發(fā)送跨域的GET請(qǐng)求,不用做特殊處理。
  2. 兼容性好,一些靜態(tài)頁(yè)面可能禁用了腳本,這時(shí)script標(biāo)簽就不能使用了。

但要注意,這個(gè)圖片不是用來展示的,我們的目的是去「?jìng)鬟f數(shù)據(jù)」,只是借助img標(biāo)簽的的src屬性,在其url后面拼接上參數(shù),服務(wù)端收到再去解析。

class StatisticSDK {
constructor(productID){
this.productID = productID;
}
send(baseURL,query={}){
query.productID = this.productID;
let queryStr = Object.entries(query).map(([key, value]) => `${key}=${value}`).join('&')
let img = new Image();
img.src = `${baseURL}?${queryStr}`
}
}

img標(biāo)簽的優(yōu)點(diǎn)是不需要將其append到文檔,只需設(shè)置src屬性便能成功發(fā)起請(qǐng)求。

通常請(qǐng)求的這個(gè)url會(huì)是一張1X1px的GIF圖片,網(wǎng)上的文章對(duì)于這里為什么返回圖片的是一張GIF都是含糊帶過,這里查閱了一些資料并測(cè)試了:

1.同樣大小,不同格式的的圖片中GIF大小是最小的,所以選擇返回一張GIF,這樣對(duì)性能的損耗更小。

2.如果返回204,會(huì)走到img的onerror事件,并拋出一個(gè)全局錯(cuò)誤;如果返回200和一個(gè)空對(duì)象會(huì)有一個(gè)CORB的告警。

3.當(dāng)然如果不在意這個(gè)報(bào)錯(cuò)可以采取返回空對(duì)象,事實(shí)上也有一些工具是這樣做的。

有一些埋點(diǎn)需要真實(shí)的加到頁(yè)面上,比如垃圾郵件的發(fā)送者會(huì)添加這樣一個(gè)隱藏標(biāo)志來驗(yàn)證郵件是否被打開,如果返回204或者是200空對(duì)象會(huì)導(dǎo)致一個(gè)明顯圖片占位符。

<img src="http://www.example.com/logger?event_id=1234">

更優(yōu)雅的web beacon

這種打點(diǎn)標(biāo)記的方式被稱web beacon(網(wǎng)絡(luò)信標(biāo))。除了gif圖片,從2014年開始,瀏覽器逐漸實(shí)現(xiàn)專門的API,來更優(yōu)雅的完成這件事:Navigator.sendBeacon。

使用很簡(jiǎn)單。

Navigator.sendBeacon(url,data)

相較于圖片的src,這種方式的更有優(yōu)勢(shì):

  • 不會(huì)和主要業(yè)務(wù)代碼搶占資源,而是在瀏覽器空閑時(shí)去做發(fā)送。
  • 并且在頁(yè)面卸載時(shí)也能保證請(qǐng)求成功發(fā)送,不阻塞頁(yè)面刷新和跳轉(zhuǎn)。

現(xiàn)在的埋點(diǎn)監(jiān)控工具通常會(huì)優(yōu)先使用sendBeacon,但由于瀏覽器兼容性,還是需要用圖片的src兜底。

用戶行為監(jiān)控

上面實(shí)現(xiàn)了數(shù)據(jù)發(fā)送的api,現(xiàn)在可以基于它去實(shí)現(xiàn)用戶行為監(jiān)控的api。

class StatisticSDK {
constructor(productID){
this.productID = productID;
}
// 數(shù)據(jù)發(fā)送
send(baseURL,query={}){
query.productID = this.productID;
let queryStr = Object.entries(query).map(([key, value]) => `${key}=${value}`).join('&')
let img = new Image();
img.src = `${baseURL}?${queryStr}`
}
// 自定義事件
event(key, val={}) {
let eventURL = 'http://demo/'
this.send(eventURL,{event:key,...val})
}
// pv曝光
pv() {
this.event('pv')
}
}

用戶行為包括自定義事件和pv曝光,也可以把pv曝光看作是一種特殊的自定義行為事件。

頁(yè)面性能監(jiān)控

頁(yè)面的性能數(shù)據(jù)可以通過performance.timing這個(gè)API獲取到,獲取的數(shù)據(jù)是單位為毫秒的時(shí)間戳。

上面的不需要全部了解,但比較關(guān)鍵的數(shù)據(jù)有下面幾個(gè),根據(jù)它們可以計(jì)算出FP/DCL/Load等關(guān)鍵事件的時(shí)間點(diǎn):

  1. 頁(yè)面首次渲染時(shí)間:FP(firstPaint)=domLoading-navigationStart。
  2. DOM加載完成:DCL(DOMContentEventLoad)=domContentLoadedEventEnd-navigationStart。
  3. 圖片、樣式等外鏈資源加載完成:L(Load)=loadEventEnd-navigationStart。

上面的數(shù)值可以跟performance面板里的結(jié)果對(duì)應(yīng)。

回到SDK,我們只用實(shí)現(xiàn)一個(gè)上傳所有性能數(shù)據(jù)的api就可以了:

class StatisticSDK {
constructor(productID){
this.productID = productID;
// 初始化自動(dòng)調(diào)用性能上報(bào)
this.initPerformance()
}
// 數(shù)據(jù)發(fā)送
send(baseURL,query={}){
query.productID = this.productID;
let queryStr = Object.entries(query).map(([key, value]) => `${key}=${value}`).join('&')
let img = new Image();
img.src = `${baseURL}?${queryStr}`
}
// 性能上報(bào)
initPerformance(){
let performanceURL = 'http://performance/'
this.send(performanceURL,performance.timing)
}
}

并且,在構(gòu)造函數(shù)里自動(dòng)調(diào)用,因?yàn)樾阅軘?shù)據(jù)是必須要上傳的,就不需要用戶每次都手動(dòng)調(diào)用了。

錯(cuò)誤告警監(jiān)控

錯(cuò)誤報(bào)警監(jiān)控分為JS原生錯(cuò)誤和React/Vue的組件錯(cuò)誤的處理。

JS原生錯(cuò)誤

除了try catch中捕獲住的錯(cuò)誤,我們還需要上報(bào)沒有被捕獲住的錯(cuò)誤——通過error事件和unhandledrejection事件去監(jiān)聽。

error

error事件是用來監(jiān)聽DOM操作錯(cuò)誤DOMException和JS錯(cuò)誤告警的,具體來說,JS錯(cuò)誤分為下面8類:

  1. InternalError: 內(nèi)部錯(cuò)誤,比如如遞歸爆棧。
  2. ?RangeError: 范圍錯(cuò)誤,比如new Array(-1)。
  3. EvalError: 使用eval()時(shí)錯(cuò)誤。
  4. ReferenceError: 引用錯(cuò)誤,比如使用未定義變量。
  5. SyntaxError: 語(yǔ)法錯(cuò)誤,比如var a = 。
  6. TypeError: 類型錯(cuò)誤,比如[1,2].split('.')。
  7. URIError: 給 encodeURI或 decodeURl()傳遞的參數(shù)無效,比如decodeURI('%2')。
  8. Error: 上面7種錯(cuò)誤的基類,通常是開發(fā)者拋出。

也就是說,代碼運(yùn)行時(shí)發(fā)生的上述8類錯(cuò)誤,都可以被檢測(cè)到。

unhandledrejection

Promise內(nèi)部拋出的錯(cuò)誤是無法被error捕獲到的,這時(shí)需要用unhandledrejection事件。

回到SDK的實(shí)現(xiàn),處理錯(cuò)誤報(bào)警的代碼如下:

class StatisticSDK {
constructor(productID){
this.productID = productID;
// 初始化錯(cuò)誤監(jiān)控
this.initError()
}
// 數(shù)據(jù)發(fā)送
send(baseURL,query={}){
query.productID = this.productID;
let queryStr = Object.entries(query).map(([key, value]) => `${key}=${value}`).join('&')
let img = new Image();
img.src = `${baseURL}?${queryStr}`
}
// 自定義錯(cuò)誤上報(bào)
error(err, etraInfo={}) {
const errorURL = 'http://error/'
const { message, stack } = err;
this.send(errorURL, { message, stack, ...etraInfo})
}
// 初始化錯(cuò)誤監(jiān)控
initError(){
window.addEventListener('error', event=>{
this.error(error);
})
window.addEventListener('unhandledrejection', event=>{
this.error(new Error(event.reason), { type: 'unhandledrejection'})
})
}
}

和初始化性能監(jiān)控一樣,初始化錯(cuò)誤監(jiān)控也是一定要做的,所以需要在構(gòu)造函數(shù)中調(diào)用。后續(xù)開發(fā)人員只用在業(yè)務(wù)代碼的try catch中調(diào)用error方法即可。

React/Vue組件錯(cuò)誤

成熟的框架庫(kù)都會(huì)有錯(cuò)誤處理機(jī)制,React和Vue也不例外。

React的錯(cuò)誤邊界

錯(cuò)誤邊界是希望當(dāng)應(yīng)用內(nèi)部發(fā)生渲染錯(cuò)誤時(shí),不會(huì)整個(gè)頁(yè)面崩潰。我們提前給它設(shè)置一個(gè)兜底組件,并且可以細(xì)化粒度,只有發(fā)生錯(cuò)誤的部分被替換成這個(gè)「兜底組件」,不至于整個(gè)頁(yè)面都不能正常工作。

它的使用很簡(jiǎn)單,就是一個(gè)帶有特殊生命周期的類組件,用它把業(yè)務(wù)組件包裹起來。

這兩個(gè)生命周期是getDerivedStateFromError和componentDidCatch。

代碼如下:

// 定義錯(cuò)誤邊界
class ErrorBoundary extends React.Component {
state = { error: null }
static getDerivedStateFromError(error) {
return { error }
}
componentDidCatch(error, errorInfo) {
// 調(diào)用我們實(shí)現(xiàn)的SDK實(shí)例
insSDK.error(error, errorInfo)
}
render() {
if (this.state.error) {
return <h2>Something went wrong.</h2>
}
return this.props.children
}
}
...
<ErrorBoundary>
<BuggyCounter />
</ErrorBoundary>

回到SDK的整合上,在生產(chǎn)環(huán)境下,被錯(cuò)誤邊界包裹的組件,如果內(nèi)部拋出錯(cuò)誤,全局的error事件是無法監(jiān)聽到的,因?yàn)檫@個(gè)錯(cuò)誤邊界本身就相當(dāng)于一個(gè)try catch。所以需要在錯(cuò)誤邊界這個(gè)組件內(nèi)部去做上報(bào)處理。也就是上面代碼中的componentDidCatch生命周期。

Vue的錯(cuò)誤邊界

vue也有一個(gè)類似的生命周期來做這件事,不再贅述:errorCaptured。

Vue.component('ErrorBoundary', {
data: () => ({ error: null }),
errorCaptured (err, vm, info) {
this.error = `${err.stack}\n\nfound in ${info} of component`
// 調(diào)用我們的SDK,上報(bào)錯(cuò)誤信息
insSDK.error(err,info)
return false
},
render (h) {
if (this.error) {
return h('pre', { style: { color: 'red' }}, this.error)
}
return this.$slots.default[0]
}
})
...
<error-boundary>
<buggy-counter />
</error-boundary>

現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了一個(gè)完整的SDK的骨架,并且處理了在實(shí)際開發(fā)時(shí),react/vue項(xiàng)目應(yīng)該怎么接入。

實(shí)際生產(chǎn)使用的SDK會(huì)更健壯,但思路也不外乎,感興趣的可以去讀一讀源碼。

結(jié)語(yǔ)

文章比較長(zhǎng),但想答好這個(gè)問題,這些知識(shí)儲(chǔ)備都是必須的。

我們要設(shè)計(jì)SDK,首先要清楚它的基本使用方法,才知道后面的代碼框架要怎么搭;然后是明確SDK的職能范圍:需要能處理用戶行為、頁(yè)面性能以及錯(cuò)誤報(bào)警三類監(jiān)控;最后是react、vue的項(xiàng)目,通常會(huì)做錯(cuò)誤邊界處理,要怎么接入我們自己的SDK。

責(zé)任編輯:姜華 來源: 前端私教年年
相關(guān)推薦

2020-07-28 07:56:59

網(wǎng)站

2024-08-28 08:38:51

2019-08-12 10:45:54

Flutter框架Native

2021-11-30 07:51:29

共享內(nèi)存進(jìn)程

2022-08-31 07:54:08

采集sdk埋點(diǎn)數(shù)據(jù)

2025-04-29 02:00:00

高并發(fā)系統(tǒng)場(chǎng)景

2016-12-12 13:42:54

數(shù)據(jù)分析大數(shù)據(jù)埋點(diǎn)

2023-12-29 11:32:27

2023-12-14 17:27:28

架構(gòu)設(shè)計(jì)數(shù)據(jù)表

2023-11-08 07:05:07

架構(gòu)設(shè)計(jì)群聊系統(tǒng)

2025-03-17 02:00:00

2021-02-19 07:59:21

數(shù)據(jù)埋點(diǎn)數(shù)據(jù)分析大數(shù)據(jù)

2023-06-29 08:43:44

DNS解析IP

2023-12-13 18:46:50

FlutterAOP業(yè)務(wù)層

2020-10-16 15:06:59

開發(fā)技術(shù)方案

2019-10-31 13:58:32

阿里電商系統(tǒng)

2023-11-01 18:10:45

架構(gòu)設(shè)計(jì)技術(shù)

2024-09-14 14:14:26

Dubbo框架微服務(wù)

2023-10-08 22:38:52

2021-01-14 05:23:32

高并發(fā)消息中間件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲精品一区二区在线观看 | 伊人焦久影院 | 欧美日韩久 | 欧美精品一区二区蜜桃 | 国内久久精品 | 亚洲欧洲一区 | 欧美视频成人 | 久久久亚洲 | 在线日韩 | 日本一卡精品视频免费 | 国产高清精品在线 | 国产视频在线观看一区二区三区 | 成人av在线播放 | 亚洲精品国产a久久久久久 午夜影院网站 | 日韩欧美在线免费 | 日本精品一区二区三区四区 | 成人小视频在线观看 | 麻豆久久久久久久 | 毛片视频免费观看 | 91看片视频 | 国产日韩精品一区二区三区 | 国产成人精品一区二区三区 | 色橹橹欧美在线观看视频高清 | 中文在线一区二区 | 在线成人免费视频 | 久久久蜜桃一区二区人 | 国产欧美精品 | 美女天天操 | 中文字幕成人 | 日韩不卡一二区 | 国产精品片 | 日日夜夜天天 | 精品成人在线视频 | 91久久精品日日躁夜夜躁国产 | 国产成人精品午夜 | 久久久久久国产 | 一区二区三区四区免费在线观看 | 免费欧美视频 | 夜夜骑综合 | 欧美淫片 | 一二区视频 |