需求背景
一個(gè)頁面A
需要記錄在頁面點(diǎn)擊某個(gè)鏈接B
時(shí)的時(shí)間time1
,與后退返回該頁面時(shí)的時(shí)間time2
募书,得到中間的時(shí)間差time2-time1
折剃。
此處的難點(diǎn)在于:
1、點(diǎn)擊跳出A的B是不確定的殖蚕,有可能點(diǎn)擊的是廣告(點(diǎn)擊的是廣告轿衔,則幾乎很難監(jiān)聽到點(diǎn)擊廣告的事件),有可能是一個(gè)頁面鏈接(頁面鏈接有可能非常多睦疫,不可能每個(gè)鏈接都加一個(gè)監(jiān)聽事件)害驹;
2、記錄離開頁面前的時(shí)間蛤育,與返回頁面的時(shí)間宛官,離開頁面有可能是在當(dāng)前頁面刷新打開頁面,有可能是新開窗口打開瓦糕,情況有多種底洗。
解決思路
巧用pagehide
,pageshow
咕娄,document.hidden
亥揖,visibilitychange
組合實(shí)現(xiàn)。
pageshow
和 pagehide
事件
手機(jī)上的瀏覽器有一個(gè)特性,名叫“往返緩存”(back-forward cache费变,或bfcache)摧扇,可以在用戶使用瀏覽器的“后退”和“前進(jìn)”按鈕時(shí)加快頁面的轉(zhuǎn)換速度。這個(gè)緩存中不僅保存著頁面數(shù)據(jù)挚歧,還保存了DOM和JavaScript的狀態(tài)扛稽;實(shí)際上是將整個(gè)頁面都保存在了內(nèi)存里。如果頁面位于bfcache中滑负,那么再次打開該頁面就不會(huì)觸發(fā)load事件在张。盡管由于內(nèi)存中保存了整個(gè)頁面的狀態(tài),不觸發(fā)load事件也不應(yīng)該會(huì)導(dǎo)致什么問題矮慕,但為了更形象地說明bfcache的行為帮匾,F(xiàn)irefox還是提供了一些新事件。 第一個(gè)事件就是pageshow
凡傅,這個(gè)事件在頁面顯示時(shí)觸發(fā)辟狈,無論頁面是否來自bfcache。在重新加載頁面中夏跷,pageshow會(huì)在load事件觸發(fā)后觸發(fā)哼转;而對(duì)于bfcache中的頁面,pageshow會(huì)在頁面狀態(tài)完全恢復(fù)的那一刻觸發(fā)槽华。
1)load 和 unload 事件監(jiān)聽web頁面的進(jìn)入和離開壹蔓,一般用于頁面的首次加載、刷新和關(guān)閉等操作的監(jiān)聽猫态;
2)pageshow
和 pagehide
事件多用于監(jiān)聽瀏覽器的前進(jìn)和后退等佣蓉。
1、pageshow和load區(qū)別:
pageshow 事件類似于 load 事件亲雪,load 事件在頁面第一次加載時(shí)觸發(fā)勇凭, pageshow 事件在每次加載頁面時(shí)觸發(fā),即 load 事件在頁面從瀏覽器緩存中讀取時(shí)不觸發(fā)义辕。
一般情況下虾标,移動(dòng)端瀏覽器會(huì)將當(dāng)前已訪問頁面存入緩存中,緩存中保存著頁面數(shù)據(jù)灌砖,DOM和js的狀態(tài)璧函,前進(jìn)和后退操作時(shí)直接從瀏覽器緩存中讀取頁面內(nèi)容,而不進(jìn)行頁面刷新基显,所以監(jiān)聽前進(jìn)和后退操作時(shí)可用pageshow事件蘸吓。
觸發(fā)時(shí)間:load先觸發(fā),pageshow后觸發(fā)撩幽。
2库继、查看是否讀取緩存:
為了查看頁面是直接從服務(wù)器上載入還是從緩存中讀取,你可以使用 Event 對(duì)象的 persisted 屬性來判斷。 如果頁面從瀏覽器的緩存中讀取該屬性返回 ture宪萄,否則返回 false
3舅桩、示例:
window.addEventListener('pageshow', function(event) {
console.log(event.persisted);
})
4、pagehide和unload事件的區(qū)別:
pagehide 事件類似于 unload 事件雨膨,在用戶離開網(wǎng)頁時(shí)觸發(fā)(如點(diǎn)擊一個(gè)鏈接、刷新頁面读串、提交表單聊记、關(guān)閉瀏覽器、前進(jìn)恢暖、后退等)排监。
頁面緩存:pagehide觸發(fā)可以緩存頁面,但unload 事件觸發(fā)后無法緩存杰捂。
觸發(fā)時(shí)間:pagehide先觸發(fā)舆床,unload后觸發(fā)。
2嫁佳、查看是否讀取緩存:
同pageshow
pageshow挨队,pagehide兼容情況:基本移動(dòng)端上用都兼容
問題:但是有時(shí)候在一些機(jī)型中event. persisted判斷并不準(zhǔn)確,所以可以引用window.performance.navigation.type
做兼容處理蒿往。
window.performance
對(duì)象
performance.navigation.type
是一個(gè)無符號(hào)短整型盛垦,接口呈現(xiàn)了如何導(dǎo)航到當(dāng)前文檔的信息。它有四種type類型:
1瓤漏、TYPE_NAVIGATE (0)
:當(dāng)前頁面是通過點(diǎn)擊鏈接腾夯,書簽和表單提交,或者腳本操作蔬充,或者在url中直接輸入地址蝶俱,type值為0。
2饥漫、TYPE_RELOAD (1)
:點(diǎn)擊刷新頁面按鈕或者通過Location.reload()方法顯示的頁面榨呆,type值為1:。
3趾浅、TYPE_BACK_FORWARD (2)
:頁面通過歷史記錄和前進(jìn)后退訪問時(shí)愕提。type值為2。
4皿哨、TYPE_RESERVED (255)
: 任何其他方式浅侨,type值為255。
所以type為2可以作為頁面后退或者前進(jìn)時(shí)的一個(gè)判斷依據(jù)证膨。
window.addEventListener('pageshow', (e) => {
if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
// 頁面后退或者前進(jìn)時(shí)操作
}
document.hidden
屬性
頁面可見性判斷:document.hidden
與visibilitychange
事件如输,當(dāng)前頁面不在視野范圍內(nèi),則會(huì)觸發(fā)visibilitychange事件,并且改變document.hidden的值為true不见,當(dāng)回到頁面視野當(dāng)中澳化,也會(huì)觸發(fā)事件改變document.hidden的值為false。
// 兼容瀏覽器
const hidden = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ? 'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;
const event = hidden.replace(/hidden/i, 'visibilitychange');
document.addEventListener(event, () => {
console.log('當(dāng)前頁面是否被隱藏:', document[hidden]);
});
具體實(shí)現(xiàn)
頁面A點(diǎn)擊頁面中的鏈接B跳去新鏈接稳吮,記錄離開A頁面的時(shí)間與返回A頁面的時(shí)間缎谷。
B頁面離開方式:
1、當(dāng)前窗口打開頁面(非跳出型):監(jiān)聽pagehide & document.hidden=true
2灶似、新開窗口打開頁面(跳出型):監(jiān)聽pagehide & document.hidden=true
返回頁面A方式:
1列林、返回后頁面被動(dòng)刷新:監(jiān)聽pageshow
2、返回后頁面不刷新:監(jiān)聽document.hidden=false
終上所述酪惭,為了兼容設(shè)備前進(jìn)后退的多種差異情況希痴,需要融合幾種監(jiān)聽方式,以達(dá)到覆蓋多種情況的效果(經(jīng)測試春感,在安卓版本5砌创,7,10上組合方式都通過測試鲫懒,ios也通過了測試)嫩实。
// 兼容瀏覽器
const hidden = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ?
'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;
const event = hidden.replace(/hidden/i, 'visibilitychange');
document.addEventListener(event, () => {
if(document[hidden]) {
// 隱藏:記錄離開時(shí)間
} else {
// 顯示:記錄返回時(shí)間
}
});
window.addEventListener('pageshow', (e) => {
if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
// 頁面后退時(shí)操作:記錄返回時(shí)間
}
}
window.addEventListener('pagehide', (e) => {
// 頁面離開時(shí)操作:記錄離開時(shí)間
}
后記
在vue中,如果已經(jīng)創(chuàng)建了Vue示例再實(shí)行監(jiān)聽pageshow時(shí)間的話刀疙,是會(huì)失效的舶赔,所以,需要在未創(chuàng)建Vue示例前監(jiān)聽pageshow事件谦秧,并且可以通過window.postMessage
延遲傳遞信息:
window.addEventListener('pageshow', (t) => {
// persisted:查看頁面是直接從服務(wù)器上載入還是從瀏覽器緩存中讀取,true:緩存讀取
if (t.persisted || (window.performance && window.performance.navigation.type === 2)) {
setTimeout(() => {
window.postMessage('pageshowEvent', window.location.origin);
}, 2000);
}
new Vue({
el: '#app',
template: '<Main />',
components: { Main },
});
});
// 實(shí)現(xiàn)vue文件中
window.addEventListener('message', (e) => {
if (e.origin !== window.location.origin && e.data !== 'pageshowEvent') return;
// 后續(xù)處理
}, false);