Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
頁面停留時間(Time on Page)簡稱 Tp,是網站分析中很常見的一個指標,用于反映用戶在某些頁面上停留時間的長短,傳統的Tp統計方法會存在一定的統計盲區,比如無法監控單頁應用,沒有考慮用戶切換Tab、最小化窗口等操作場景。 基于上述背景,重新調研和實現了精確統計頁面停留時長的方案,需要 兼容單頁應用和多頁應用,并且不耦合或入侵業務代碼。
我們可以把一個頁面生命周期抽象為三個動作: 「進入」、「活躍狀態切換」、「離開」
如下圖,計算頁面停留時長既如何監控這三個動作,然后在對應觸發的事件中記錄時間戳,比如要統計活躍停留時長就把 active 區間相加即可,要統計總時長既 tn -t0 。
2.1 如何監聽頁面的進入和離開?
對于常規頁面的 首次加載、頁面關閉、刷新 等操作都可以通過 window.onload 和 window.onbeforeunload 事件來監聽頁面進入和離開,瀏覽器前進后退可以通過 pageshow 和 pagehide 處理。
對于單頁應用內部的跳轉可以轉化為兩個問題:1.監聽路由變化,2.判斷變化的URL是否為不同頁面 。
2.1.1 監聽路由變化
目前主流的單頁應用大部分都是基于 browserHistory (history api) 或者 hashHistory 來做路由處理,我們可以通過監聽路由變化來判斷頁面是否有可能切換。注意是有可能切換,因為URL發生變化不代表頁面一定切換,具體的路由配置是由業務決定的(既URL和頁面的匹配規則)。
browserHistory
路由的變化本質都會調用 History.pushState() 或 History.replaceState() ,能監聽到這兩個事件就能知道。通過 popstate 事件能解決一半問題,因為 popstate 只會在瀏覽器前進后退的時候觸發,當調用 history.pushState() or history.replaceState() 的時候并不會觸發。
The popstate event is fired when the active history entry changes. If the history entry being activated was created by a call to history.pushState() or was affected by a call to history.replaceState(), the popstate event's state property contains a copy of the history entry's state object.
Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling。history.back() or history.forward() in JavaScript).
這里需要通過猴子補丁(Monkeypatch)解決,運行時重寫 history.pushState 和 history.replaceState 方法:
let _wr = function (type) { let orig = window.history[type] return function () { let rv = orig.apply(this, arguments) let e = new Event(type.toLowerCase()) e.arguments = arguments window.dispatchEvent(e) return rv } } window.history.pushState = _wr('pushState') window.history.replaceState = _wr('replaceState') window.addEventListener('pushstate', function (event) {}) window.addEventListener('replacestate', function (event) {})
hashHistory
hashHistory 的實現是基于 hash 的變化,hash 的變化可以通過 hashchange 來監聽
2.1.2 判斷URL是否為不同頁面
問題本質是怎么定義一個頁面,這里我們無法自動獲取,因為不同業務場景定義不同,需要業務方在初始化的時候配置 rules 參數,默認不傳入 rules 的情況取 location.pathname 為 key,key 不相同則判斷為不同的頁面,配置的語法:
new Tracer({ rules: [ { path: '/index' }, { path: '/detail/:id' }, { path: '/user', query: {tab: 'profile'} } ] )
對于頁面進入和離開相關事件整理
2.2 如何監聽頁面活躍狀態切換?
可以通過 Page Visibility API 以及在 window 上聲明 onblur/onfocus 事件來處理。
2.2.1 Page Visibility API
一個網頁的可見狀態可以通過 Page Visibility API 獲取,比如當用戶 切換瀏覽器Tab、最小化窗口、電腦睡眠 的時候,系統API會派發一個當前頁面可見狀態變化的 visibilitychange 事件,然后在事件綁定函數中通過 document.hidden 或者 document.visibilityState 讀取當前狀態。
document.addEventListener('visibilitychange', function (event) { console.log(document.hidden, document.visibilityState) })
2.2.2 onblur/onfocus
2.3 什么時機上報數據?
2.3.1 頁面離開時上報
對于頁面刷新或者關閉窗口觸發的操作可能會造成數據丟失
2.3.2 下次打開頁面時上報
會丟失歷史訪問記錄中的最后一個頁面數據
目前采用的方案2,對于單頁內部跳轉是即時上報,對于單頁/多頁應用觸發 window.onbeforeunload 事件的時候會把當前頁面數據暫存在 localStorage 中,當用戶下次進入頁面的時候會把暫存數據上報。有個細節問題,如果用戶下次打開頁面是在第二天,對于統計當天的活躍時長會有一定的誤差,所以在數據上報的同時會把該條數據的頁面進入時間/離開時間帶上。
3.1 UML類關系圖
Tracer
核心類,用來實例化一個監控,對原生事件和自定義事件的封裝,監聽 enter activechange exit 事件來操作當前 Page 實例。
P.S. 取名來自暴雪旗下游戲守望先鋒英雄獵空(Tracer),直譯為:追蹤者。
Page
頁面的抽象類,用來實例化一個頁面,封裝了 enter exit active inactive 等操作,內部通過 state 屬性來維護當前頁面狀態。
3.2 事件派發關系圖
Desktop
Mobile
對于頁面停留時長的定義可能在不同場景會有差異,比如內部業務系統或者OA系統,產品可能更關心用戶在頁面的活躍時長;而對于資訊類型的產品,頁面可見時長會更有價值。單一的數據對業務分析是有限的,所以在具體的代碼實過程中我們會把停留時長分三個指標,這樣能更好的幫助產品/運營分析。
active 頁面活躍時長
visible 頁面可見時長 //僅支持Desktop
duration 頁面總停留時長
移動端的兼容性目前還沒完全覆蓋;
對于頁面的配置目前還不夠靈活,考慮支持 react-router / vue-router 的配置;
byted-cg-tracer 待封裝;開發中
https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onhashchange
https://developer.mozilla.org/en-US/docs/Web/Events/popstate
https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
https://stackoverflow.com/questions/4570093/how-to-get-notified-about-changes-of-the-history-via-history-pushstate
.什么是時間戳?
時間戳是字符或編碼信息的序列,用于標識何時發生特定事件,通常給出日期和時間,有時精確到幾分之一秒。 該術語源自辦公室中使用的橡皮圖章,用于在紙質文檔上用墨水在當前日期和時間上加蓋戳記,以記錄接收文檔的時間。 此類時間戳的常見用例是在紙質信件上的郵戳或考勤打卡紙上的“入”和“出”時間。在現代,該術語的使用已擴展為指附加到數字數據的數字日期和時間信息。 例如,計算機文件包含時間戳,該時間戳指示文件的最后修改時間,數碼相機將時間戳添加到所拍攝的照片中,記錄拍攝日期和時間。
對于電子商務應用,如電子合同簽署、電子文檔簽名等,都需要一個能證明合同簽署時間和文檔簽名時間的可信證明,但是由于用戶桌面電腦或手機或者服務器時間是可以隨意修改的,如果簽署合同和文檔時用這些不可信的時間,則無法保證合同的簽署時間可信。因此合同簽署和文檔簽名需要一個權威第三方來提供可信賴的且不可抵賴的時間戳服務。
2.密信時間戳服務工作原理
密信(MeSign)App提供的電子文檔數字簽名服務免費為用戶配套提供Adobe全球信任的時間戳服務,密信時間戳服務符合RFC3161國際標準和相應的國家標準。時間戳服務就是將經過時間戳服務器簽名的一個可信賴的日期和時間與特定電子數據綁定在一起,為PDF簽名應用提供可信的時間證明。 其工作原理示意圖如下左圖所示,用戶對待簽名文件生成摘要數據,并把此數據提交給時間戳服務器請求簽名,時間戳服務器對摘要數據和一個
來自權威時間源的一個日期/時間記錄進行簽名,生成時間戳簽名數據返回給簽名工具,簽名工具把此時間戳數據寫入到待簽名的PDF文件即完成時間戳簽名。密信時間戳服務時間源來自國家授時中心可靠計時系統。
用戶在使用密信App的電子簽名服務數字簽名文檔和簽署電子合同時,密信App自動調用密信時間戳服務并自動把時間戳簽名數據同文件簽名數據和LTV數據一起按照國際標準寫入到待簽名的PDF文件中完成PDF文件數字簽名。用戶無需手動配置時間戳服務網址,無需另外花錢購買時間戳服務,使用密信App完成數字簽名的時間戳服務完全免費和完全全自動。
3.使用密信App簽名的文檔的時間戳是什么樣的?
如下左圖所示,使用密信(MeSign)App簽名的文檔使用Adobe閱讀器打開后顯示的詳細時間戳信息,顯示時間戳頒發機構為 MeSign Time Stamping Signer。如下中圖所示,Adobe閱讀器顯示的簽名有效性小結信息,顯示“簽名包含嵌入的時間戳”和“安全時間戳時間已驗證”,表明此文檔采用了Adobe全球信任的時間戳服務。如下右圖所示,點擊Adobe閱讀器左邊的簽名圖標也會顯示此簽名包含嵌入的時間戳。
為了確保所有已簽名PDF文件的時間戳簽名全球信任和國密合規,密信技術獨創了雙算法證書雙時間戳簽名技術,自動對待簽名的同一個PDF文件用RSA和SM2算法計算兩份摘要(HASH)數據,并分別用RSA時間戳證書和SM2時間戳證書實現雙時間戳簽名,再把這兩個時間戳簽名數據寫入到PDF文件中,完成雙證書雙時間戳簽名,確保用戶使用Adobe閱讀器顯示RSA時間戳簽名信息和國密閱讀器顯示國密時間戳簽名信息,只有這樣,才能真正滿足Adobe全球信任和國密合規的雙標準要求,才能真正實現已簽署合同和文件的全球范圍具有法律效力。
使用Adobe閱讀器打開上面已下載的已簽名樣板文件,查看時間戳簽名信息和顯示的效果如下左圖所示。而使用支持國密算法的 密信閱讀器(內測版)打開此文件,查看時間戳簽名信息和顯示的效果如下圖右圖所示。從這個樣板文件的兩個時間戳時間對比可以看出,密信App文檔簽名時先用國密時間戳證書簽名后用RSA時間戳簽名。
密信可信時間戳免費試用搜索:密信MeSign
試用地址:https://www.mesign.com/zh-cn/tsa/index.html
例:倒計時
案例分析:
1.這個倒計時是不斷變化的,因此需要定時器來自動變化(setInterval)
2.三個黑色盒子里面分別存放時分秒
3.三個黑色盒子利用innerHTML放入計算的小時分鐘秒數
4.第一次執行也是間隔毫秒數,因此剛刷新頁面會有空白
5.最好采取封裝函數的方式,這樣可以先調用一次這個函數,防止剛開始刷新頁面有空白問題。
倒計時的算法:
1.核心算法:輸入的時間減去現在的時間就是剩余的時間,即倒計時,但是不能拿著時分秒相減,比如05分減去25分,
結果會是負數的
2.用時間戳來做,用戶輸入時間總的毫秒數減去現在時間的總的毫秒數,得到的就是剩余時間的毫秒數.
3.把剩余時間總的毫秒數轉換為天、時、分、秒、(時間戳轉換為時分秒)*/
//轉換公式如下:
/*d = parseInt(總秒數/60/60/24); //計算天數
h = parseInt(總秒數/60/60%24); //計算小時
m = parseInt(總秒數/60%24); //計算分鐘
s = parseInt(總秒數%60); //計算當前秒數*/
function conus(time){
var dqtime = +new Date(time);
var zqtime = +new Date();
var times = (dqtime - zqtime) / 1000;
var t = parseInt(times / 60 / 60 / 24);//天
t = t < 10 ? '0' + t:t;
var s = parseInt(times / 60 / 60 % 24);//時
s = s < 10 ? '0' + s:s;
var f = parseInt(times / 60 % 60);//分
f = f < 10 ? '0' + f:f;
var m = parseInt(times % 60);//秒
m = m < 10 ? '0' + m:m;
return t + '天' + s + '時' + f + '分' + m + '秒';
}
console.log(conus('2022-6-7 12:00:00'));
源碼如下
*請認真填寫需求信息,我們會在24小時內與您取得聯系。