<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入瀏覽器原理系列(3)瀏覽器頁面也是有生命周期的

          共 4172字,需瀏覽 9分鐘

           ·

          2021-09-24 23:25

          Android、iOS 和最新的 Windows 系統(tǒng)可以隨時(shí)自主地停止后臺(tái)進(jìn)程,及時(shí)釋放系統(tǒng)資源。也就是說,網(wǎng)頁可能隨時(shí)被系統(tǒng)丟棄掉。Page Visibility API 只在網(wǎng)頁對(duì)用戶不可見時(shí)觸發(fā),至于網(wǎng)頁會(huì)不會(huì)被系統(tǒng)丟棄掉,它就無能為力了。

          為了解決這個(gè)問題,W3C 新制定了一個(gè) Page Lifecycle API,統(tǒng)一了網(wǎng)頁從誕生到卸載的行為模式,并且定義了新的事件,允許開發(fā)者響應(yīng)網(wǎng)頁狀態(tài)的各種轉(zhuǎn)換。

          有了這個(gè) API,開發(fā)者就可以預(yù)測(cè)網(wǎng)頁下一步的狀態(tài),從而進(jìn)行各種針對(duì)性的處理。Chrome 68 支持這個(gè) API,對(duì)于老式瀏覽器可以使用谷歌開發(fā)的兼容庫 PageLifecycle.js。

          一、生命周期階段

          網(wǎng)頁的生命周期分成六個(gè)階段,每個(gè)時(shí)刻只可能處于其中一個(gè)階段。

          (1)Active 階段

          在 Active 階段,網(wǎng)頁處于可見狀態(tài),且擁有輸入焦點(diǎn)。

          (2)Passive 階段

          在 Passive 階段,網(wǎng)頁可見,但沒有輸入焦點(diǎn),無法接受輸入。UI 更新(比如動(dòng)畫)仍然在執(zhí)行。該階段只可能發(fā)生在桌面同時(shí)有多個(gè)窗口的情況。

          (3)Hidden 階段

          在 Hidden 階段,用戶的桌面被其他窗口占據(jù),網(wǎng)頁不可見,但尚未凍結(jié)。UI 更新不再執(zhí)行。

          (4)Terminated 階段

          在 Terminated 階段,由于用戶主動(dòng)關(guān)閉窗口,或者在同一個(gè)窗口前往其他頁面,導(dǎo)致當(dāng)前頁面開始被瀏覽器卸載并從內(nèi)存中清除。注意,這個(gè)階段總是在 Hidden 階段之后發(fā)生,也就是說,用戶主動(dòng)離開當(dāng)前頁面,總是先進(jìn)入 Hidden 階段,再進(jìn)入 Terminated 階段。

          這個(gè)階段會(huì)導(dǎo)致網(wǎng)頁卸載,任何新任務(wù)都不會(huì)在這個(gè)階段啟動(dòng),并且如果運(yùn)行時(shí)間太長,正在進(jìn)行的任務(wù)可能會(huì)被終止。

          (5)Frozen 階段

          如果網(wǎng)頁處于 Hidden 階段的時(shí)間過久,用戶又不關(guān)閉網(wǎng)頁,瀏覽器就有可能凍結(jié)網(wǎng)頁,使其進(jìn)入 Frozen 階段。不過,也有可能,處于可見狀態(tài)的頁面長時(shí)間沒有操作,也會(huì)進(jìn)入 Frozen 階段。

          這個(gè)階段的特征是,網(wǎng)頁不會(huì)再被分配 CPU 計(jì)算資源。定時(shí)器、回調(diào)函數(shù)、網(wǎng)絡(luò)請(qǐng)求、DOM 操作都不會(huì)執(zhí)行,不過正在運(yùn)行的任務(wù)會(huì)執(zhí)行完。瀏覽器可能會(huì)允許 Frozen 階段的頁面,周期性復(fù)蘇一小段時(shí)間,短暫變回 Hidden 狀態(tài),允許一小部分任務(wù)執(zhí)行。

          (6)Discarded 階段

          如果網(wǎng)頁長時(shí)間處于 Frozen 階段,用戶又不喚醒頁面,那么就會(huì)進(jìn)入 Discarded 階段,即瀏覽器自動(dòng)卸載網(wǎng)頁,清除該網(wǎng)頁的內(nèi)存占用。不過,Passive 階段的網(wǎng)頁如果長時(shí)間沒有互動(dòng),也可能直接進(jìn)入 Discarded 階段。

          這一般是在用戶沒有介入的情況下,由系統(tǒng)強(qiáng)制執(zhí)行。任何類型的新任務(wù)或 JavaScript 代碼,都不能在此階段執(zhí)行,因?yàn)檫@時(shí)通常處在資源限制的狀況下。

          網(wǎng)頁被瀏覽器自動(dòng) Discarded 以后,它的 Tab 窗口還是在的。如果用戶重新訪問這個(gè) Tab 頁,瀏覽器將會(huì)重新向服務(wù)器發(fā)出請(qǐng)求,再一次重新加載網(wǎng)頁,回到 Active 階段。

          二、常見場景

          以下是幾個(gè)常見場景的網(wǎng)頁生命周期變化。

          (1)用戶打開網(wǎng)頁后,又切換到其他 App,但只過了一會(huì)又回到網(wǎng)頁。

          網(wǎng)頁由 Active 變成 Hidden,又變回 Active。

          (2)用戶打開網(wǎng)頁后,又切換到其他 App,并且長時(shí)候使用后者,導(dǎo)致系統(tǒng)自動(dòng)丟棄網(wǎng)頁。

          網(wǎng)頁由 Active 變成 Hidden,再變成 Frozen,最后 Discarded。

          (3)用戶打開網(wǎng)頁后,又切換到其他 App,然后從任務(wù)管理器里面將瀏覽器進(jìn)程清除。

          網(wǎng)頁由 Active 變成 Hidden,然后 Terminated。

          (4)系統(tǒng)丟棄了某個(gè) Tab 里面的頁面后,用戶重新打開這個(gè) Tab。

          網(wǎng)頁由 Discarded 變成 Active。

          三、事件

          生命周期的各個(gè)階段都有自己的事件,以供開發(fā)者指定監(jiān)聽函數(shù)。這些事件里面,只有兩個(gè)是新定義的(freeze事件和resume事件),其它都是現(xiàn)有的。

          注意,網(wǎng)頁的生命周期事件是在所有幀(frame)觸發(fā),不管是底層的幀,還是內(nèi)嵌的幀。也就是說,內(nèi)嵌的<iframe>網(wǎng)頁跟頂層網(wǎng)頁一樣,都會(huì)同時(shí)監(jiān)聽到下面的事件。

          3.1 focus 事件

          focus事件在頁面獲得輸入焦點(diǎn)時(shí)觸發(fā),比如網(wǎng)頁從 Passive 階段變?yōu)?Active 階段。

          3.2 blur 事件

          blur事件在頁面失去輸入焦點(diǎn)時(shí)觸發(fā),比如網(wǎng)頁從 Active 階段變?yōu)?Passive 階段。

          3.3 visibilitychange 事件

          visibilitychange事件在網(wǎng)頁可見狀態(tài)發(fā)生變化時(shí)觸發(fā),一般發(fā)生在以下幾種場景。

          • 用戶隱藏頁面(切換 Tab、最小化瀏覽器),頁面由 Active 階段變成 Hidden 階段。

          • 用戶重新訪問隱藏的頁面,頁面由 Hidden 階段變成 Active 階段。

          • 用戶關(guān)閉頁面,頁面會(huì)先進(jìn)入 Hidden 階段,然后進(jìn)入 Terminated 階段。

          可以通過document.onvisibilitychange屬性指定這個(gè)事件的回調(diào)函數(shù)。

          3.4 freeze 事件

          freeze事件在網(wǎng)頁進(jìn)入 Frozen 階段時(shí)觸發(fā)。

          可以通過document.onfreeze屬性指定在進(jìn)入 Frozen 階段時(shí)調(diào)用的回調(diào)函數(shù)。

          function handleFreeze(e) {
          // Handle transition to FROZEN
          }
          document.addEventListener('freeze', handleFreeze);

          # 或者
          document.onfreeze = function() { ... }

          這個(gè)事件的監(jiān)聽函數(shù),最長只能運(yùn)行500毫秒。并且只能復(fù)用已經(jīng)打開的網(wǎng)絡(luò)連接,不能發(fā)起新的網(wǎng)絡(luò)請(qǐng)求。

          注意,從 Frozen 階段進(jìn)入 Discarded 階段,不會(huì)觸發(fā)任何事件,無法指定回調(diào)函數(shù),只能在進(jìn)入 Frozen 階段時(shí)指定回調(diào)函數(shù)。

          3.5 resume 事件

          resume事件在網(wǎng)頁離開 Frozen 階段,變?yōu)?Active / Passive / Hidden 階段時(shí)觸發(fā)。

          document.onresume屬性指的是頁面離開 Frozen 階段、進(jìn)入可用狀態(tài)時(shí)調(diào)用的回調(diào)函數(shù)。

          function handleResume(e) {
          // handle state transition FROZEN -> ACTIVE
          }
          document.addEventListener("resume", handleResume);

          # 或者
          document.onresume = function() { ... }

          3.6 pageshow 事件

          pageshow事件在用戶加載網(wǎng)頁時(shí)觸發(fā)。這時(shí),有可能是全新的頁面加載,也可能是從緩存中獲取的頁面。如果是從緩存中獲取,則該事件對(duì)象的event.persisted屬性為true,否則為false。

          這個(gè)事件的名字有點(diǎn)誤導(dǎo),它跟頁面的可見性其實(shí)毫無關(guān)系,只跟瀏覽器的 History 記錄的變化有關(guān)。

          3.7 pagehide 事件

          pagehide事件在用戶離開當(dāng)前網(wǎng)頁、進(jìn)入另一個(gè)網(wǎng)頁時(shí)觸發(fā)。它的前提是瀏覽器的 History 記錄必須發(fā)生變化,跟網(wǎng)頁是否可見無關(guān)。

          如果瀏覽器能夠?qū)?dāng)前頁面添加到緩存以供稍后重用,則事件對(duì)象的event.persisted屬性為true。如果為true。如果頁面添加到了緩存,則頁面進(jìn)入 Frozen 狀態(tài),否則進(jìn)入 Terminatied 狀態(tài)。

          3.8 beforeunload 事件

          beforeunload事件在窗口或文檔即將卸載時(shí)觸發(fā)。該事件發(fā)生時(shí),文檔仍然可見,此時(shí)卸載仍可取消。經(jīng)過這個(gè)事件,網(wǎng)頁進(jìn)入 Terminated 狀態(tài)。

          3.9 unload 事件

          unload事件在頁面正在卸載時(shí)觸發(fā)。經(jīng)過這個(gè)事件,網(wǎng)頁進(jìn)入 Terminated 狀態(tài)。

          四、獲取當(dāng)前階段

          如果網(wǎng)頁處于 Active、Passive 或 Hidden 階段,可以通過下面的代碼,獲得網(wǎng)頁當(dāng)前的狀態(tài)。

          const getState = () => {
          if (document.visibilityState === 'hidden') {
          return 'hidden';
          }
          if (document.hasFocus()) {
          return 'active';
          }
          return 'passive';
          };

          如果網(wǎng)頁處于 Frozen 和 Terminated 狀態(tài),由于定時(shí)器代碼不會(huì)執(zhí)行,只能通過事件監(jiān)聽判斷狀態(tài)。進(jìn)入 Frozen 階段,可以監(jiān)聽freeze事件;進(jìn)入 Terminated 階段,可以監(jiān)聽pagehide事件。

          五、document.wasDiscarded

          如果某個(gè)選項(xiàng)卡處于 Frozen 階段,就隨時(shí)有可能被系統(tǒng)丟棄,進(jìn)入 Discarded 階段。如果后來用戶再次點(diǎn)擊該選項(xiàng)卡,瀏覽器會(huì)重新加載該頁面。

          這時(shí),開發(fā)者可以通過判斷document.wasDiscarded屬性,了解先前的網(wǎng)頁是否被丟棄了。

          if (document.wasDiscarded) {
          // 該網(wǎng)頁已經(jīng)不是原來的狀態(tài)了,曾經(jīng)被瀏覽器丟棄過
          // 恢復(fù)以前的狀態(tài)
          getPersistedState(self.discardedClientId);
          }

          同時(shí),window對(duì)象上會(huì)新增window.clientIdwindow.discardedClientId兩個(gè)屬性,用來恢復(fù)丟棄前的狀態(tài)。

          六、參考鏈接

          • Page Lifecycle API, Philip Walton

          • Lifecycle API for Web Pages, W3C

          • Page Lifecycle 1 Editor’s Draft, W3C


          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操片在线观看 | 亚洲久精| 狼友视频入口 | 亚洲无码一区在线 | 国产欧美高清在线观看 |