相關學習推薦:javascript影片教學
最近雜七雜八的事情比較多,難得花時間彌補之前的系列,欠大家的埋設系列現在開始走起來
前端開發攻城獅開開心心的coding,非常自豪的進行了業務、UI 分離開發,各種設計模式、算法優化輪番上陣,代碼寫的Perfect(勞資代碼天下第一),沒有BUG,程序完美,兼容性No. 1,程式碼能打能抗品質高。下班輕鬆打卡,回家看娃。
實際上,開發環境與生產環境並不能等同,並且測試的過程再完善,依然會有漏測的情況存在。考慮到使用者使用客戶端環境、網路環境等等一系列的不確定因素存在。
所以在開發過程中一定要記得三大原則(我胡詌的)
埋點就像城市中的攝像頭,從產品的角度考慮,它可以監控到用戶在我們產品裡的行為軌跡,為產品的迭代、專案的穩定性提供依據,WHO、WHEN、WHERE、HOW、WHAT 是埋點擷取資料的基礎維度。
對前端開發而言,可以監控頁面資源載入效能,異常等等,提供了頁面體驗和健康指數,為後續效能最佳化提供依據,及時回報異常和發生場景。從而能夠及時修正問題,提升專案品質等。
埋點可以大概分為三類:
程式碼埋點 | 視覺化埋點 | 無痕埋點 | |
---|---|---|---|
典型場景 | 無痕埋點無法覆寫到,例如需要業務資料 | 簡單規範的頁面場景 | #簡單規範的頁面場景, |
優勢 | 業務資料明確 | 開發成本低,營運人員可直接進行相關埋點配置 | 無需配置,資料可回溯 |
不足 | 資料不可回溯,開發成本高 | 無法關聯業務數據,資料不可回溯 | 資料量較大,無法關聯業務資料 |
大部分情況,我們可以透過無痕埋點收集到所有的資訊數據,再配合可視化埋點,能夠具體定位到某一個點位,這樣大部分的埋點資訊都據此分析出來。
在特殊情況下,可以多加上業務代碼手動埋點,處理一下特別的場景(大部分情況是走強業務與正常的點擊,刷新事件無關需要上報的信息)
資源載入錯誤
JS 執行報錯
資源載入新效能
腳本
來定義埋點事件
LEVEL
: 描述埋點資料的日誌等級
TRACK
WILL_MOUNT
DID_MOUNTED
ERRORPERFORMANCE: 關於效能相關資料
#EVENT_NAME:具體的事件名稱
根據上述的維度,我們可以簡單設計如下的架構
##在瀏覽器中現在主要有2種請求方式,一個是, 一個是Fetch
。 代理XMLHttpRequest
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">function NewXHR() { var realXHR: any = new OldXHR(); // 代理模式里面有提到过
realXHR.id = guid() const oldSend = realXHR.send;
realXHR.send = function (body) {
oldSend.call(this, body) //记录埋点
}
realXHR.addEventListener(&#39;load&#39;, function () { //记录埋点
}, false);
realXHR.addEventListener(&#39;abort&#39;, function () { //记录埋点
}, false);
realXHR.addEventListener(&#39;error&#39;, function () { //记录埋点
}, false);
realXHR.addEventListener(&#39;timeout&#39;, function () { //记录埋点
}, false); return realXHR;
}复制代码</pre><div class="contentsignin">登入後複製</div></div>
代理Fetch<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"> const oldFetch = window.fetch; function newFetch(url, init) { const fetchObj = { url: url, method: method, body: body,
}
ajaxEventTrigger.call(fetchObj, AJAX_START); return oldFetch.apply(this, arguments).then(function (response) { if (response.ok) { //记录埋点
} else { //上报错误
} return response
}).catch(function (error) {
fetchObj.error = error //记录埋点
throw error
})
}复制代码</pre><div class="contentsignin">登入後複製</div></div>
監聽頁面的
,作為這次埋點行為的全域id,回報用戶id,裝置指紋,裝置資訊。在使用者未登入的情況下,透過裝置指紋來計算 UV
,透過 session id
計算 PV
。 異常捕獲
RUNTIME ERROR在
JS
中可以通過
捕捉運行時異常,一般使用
window.onerrorwindow.onerror = function(message, url, lineno, columnNo, error) { const lowCashMessage = message.toLowerCase() if(lowCashMessage.indexOf('script error') > -1) { return } const detail = { url: url filename: filename, columnNo: columnNo, lineno: lineno, stack: error.stack, message: message } //记录埋点}复制代码
Script Error
, 它產生的原因主要是頁面中載入的第三方跨域腳本報錯,例如託管在第三方CDN 中的
jsCORS
(Cross Origin Resource Sharing,跨域資源共享),如下步驟修改
Access-Control-Allow-Origin: * | 指定網域名稱
<script scr="crgt.js"></script> //加载crgt脚本,window.crgt = {getUser: () => string} try{ window.crgt.getUser(); }catch(error) { throw error // 输出正确的错误堆栈 }复制代码
window.addEventListener("unhandledrejection", event => { throw event.reason });复制代码
在浏览器中,可以通过 window.addEventListener('error', callback)
的方式监听资源加载异常,比如 js
或者 css
脚本文件丢失。
window.addEventListener('error', (event) => { if (event.target instanceof HTMLElement) { const target = parseDom(event.target, ['src']); const detail = { target: target, path: parseXPath(target), } // 记录埋点 } }, true)复制代码
通过 addEventListener click
监听 click
事件
window.addEventListener('click', (event) => { //记录埋点}, true)复制代码
在这里通过组件的 displaName
来定位元素的位置,displaName
表示组件的文件目录,比如 src/components/Form.js
文件导出的组件 FormItem
通过 babel plugin
自动添加属性 @components/Form.FormItem
,或者使用者主动给组件添加 static
属性 displayName
。
window.addEventListener('hashchange', event => { const { oldURL, newURL } = event; const oldURLObj = url.parseUrl(oldURL); const newURLObj = url.parseUrl(newURL); const from = oldURLObj.hash && url.parseHash(oldURLObj.hash); const to = newURLObj.hash && url.parseHash(newURLObj.hash); if(!from && !to ) return; // 记录埋点})复制代码
通过 addEventListener beforeunload
监听离开页面事件
window.addEventListener('beforeunload', (event) => { //记录埋点})复制代码
class Observable { constructor(observer) { observer(this.emit) } emit = (data) => { this.listeners.forEach(listener => { listener(data) }) } listeners = []; subscribe = (listener) => { this.listeners.push(listeners); return () => { const index = this.listeners.indexOf(listener); if(index === -1) { return false } this.listeners.splice(index, 1); return true; } } }复制代码
const clickObservable = new Observable((emit) => { window.addEventListener('click', emit) })复制代码
然而在处理 ajax
,需要将多种数据组合在一起,需要进行 merg 操作,则显得没有那么优雅,也很难适应后续复杂的数据流的操作。
const ajaxErrorObservable = new Observable((emit) => { window.addEventListener(AJAX_ERROR, emit) })const ajaxSuccessObservable = new Observable((emit) => { window.addEventListener(AJAX_SUCCESS, emit) })const ajaxTimeoutObservable = new Observable((emit) => { window.addEventListener(AJAX_TIMEOUT, emit) })复制代码
可以选择 RxJS 来优化代码
export const ajaxError$ = fromEvent(window, 'AJAX_ERROR', true)export const ajaxSuccess$ = fromEvent(window, 'AJAX_SUCCESS', true)export const ajaxTimeout$ = fromEvent(window, 'AJAX_TIMEOUT', true)复制代码
ajaxError$.pipe( merge(ajaxSuccess$, ajaxTimeout$), map(data=> (data) => ({category: 'ajax', data; data})) subscribe(data => console.log(data))复制代码
通过 merge
, map
两个操作符完成对数据的合并和处理。
core
event$
数据流合并snapshot
获取当前设备快照,例如url
,userID
,router
track
埋点类,组合数据流和日志。logger
logger
日志类info
warn
debug
error
observable
ajax
beforeUpload
opeartion
routerChange
logger
track
自建埋点系统是一个需要前后端一起合作的事情,如果人力不足的情况下,建议使用第三方分析插件,例如 Sentry 就能足够满足大部分日常使用
但还是建议多了解,在第三方插件出现不能满足业务需求的时候,可以顶上。
想了解更多编程学习,敬请关注php培训栏目!
以上是初探埋點系統的詳細內容。更多資訊請關注PHP中文網其他相關文章!