解決瀏覽器返回頁面不刷新的問題

現象

因爲 IOS 系統的頁面緩存機制,常常會遇到在移動端返回到上一個頁面不刷新的狀況。css

好比今天在開發微信 H5 頁面的時候,在IOS微信內置瀏覽器中返回上一頁時,上一個頁面不會被刷新。 而一般在瀏覽器緩存機制中,在返回上一頁的操做中, html/css/js/接口 等動靜態資源不會從新請求,可是js會從新加載。但在IOS微信頁面中js也會保存上一頁面最後執行的狀態,不會從新執行js。 使用這種模式的緩存機制能夠加快渲染速度,可是部分數據須要常常展現和編輯的狀況下會致使不一樣步。好比‘詳情頁’跳轉到‘編輯頁’,編輯完後再返回到‘詳情頁’,若是‘詳情頁’數據展現未進行同步修改那確定是不能接受的。 在webview和5+的混合app模式中,也會遇到這種返回上一個頁面不刷新的問題html

產生緣由

瀏覽器前進/後退緩存前端

這裏提到一個概念,瀏覽器前進/後退緩存(Backward/Forward Cache, BF Cache),固然也有人叫 disk Cache。 BF Cache 是一種瀏覽器優化, HTML 標準並未指定其如何進行緩存,所以緩存行爲是各瀏覽器各自實現,因此不盡相同。 因爲不是 HTTP 緩存,因此經過頭文件緩存設置 no-cache 是無效的。固然也不能以 HTTP 緩存機制來理解 BF Cache。ios

解決思路

設置瀏覽歷史當前記錄web

//監聽後退返回事件 --解決微信返回不刷新問題
pushHistory: function(){
    window.addEventListener("popstate", function(e) {
        self.location.reload();
    }, false);
    var state = {
        title : "",
        url : "#"
    };
    window.history.replaceState(state, "", "#");
},

/** * 頁面初始化調用pushHistory,監聽popstate事件和執行replaceState() * 當執行replaceState()時,不會觸發popstate事件,因此不會重複刷新 * 當在ios微信內置瀏覽器中執行瀏覽器前進後退操做時,觸發popstate事件,執行location.reload() * 可是在谷歌瀏覽器中執行瀏覽器前進後退操做時,不會觸發popstate事件!由於不在一個document中了 * 可是若是手動改變URL的哈希值,好比www.baidu.com# 改爲 www.baidu.com#1 會觸發popstate事件,執行location.reload() * 以上若是有一項不理解或不清晰,請往下看原理深究,你會找到答案 **/
複製代碼

原理深究

前端路由實現(history)原理chrome

之前瀏覽器操做瀏覽器歷史記錄主要依據history對象。在它的 proto 繼承有 back、forward、go 等函數。 而 HTML5 後新增 popState 來控制瀏覽歷史記錄的 api。有能夠存儲當前歷史記錄點的 pushState、替換當前歷史記錄點的 replaceState、和監聽歷史記錄點的 onPopState。api

window.history.pushState(state, title, url)瀏覽器

  • state (狀態對象): 一個js對象,能夠用來存儲一些簡單的數據。值得一提的是若是被激活的歷史記錄條目是經過 pushState 或 replaceState 調用而生成的,popState事件的state屬性包含歷史條目的狀態對象的副本。能夠從 history.state中讀取存儲的 state。
  • title (標題): 目前被瀏覽器忽略了,因此一般傳入一個空字符串
  • URL (地址): 新的歷史記錄條目的地址。值得一提的是調用 pushState() 後瀏覽器並不會當即加載這個 URL,但可能會在用戶從新打開瀏覽器時等某些狀況加載這個 URL。而且新 URL 必須與當前 URL 同源,不然會拋出一個異常。 新 URL 可爲絕對路徑或者是相對路徑。

window.history.replaceState(state, title, url)緩存

  • 相同之處是兩個API都會操做瀏覽器的歷史記錄,而不會引發頁面的刷新,也不會去驗證這個新條目對應的網頁是否存在。
  • 不一樣之處在於 pushState 會增長一條新的歷史記錄,而 replaceState 則會替換當前的歷史記錄。
  • 兩個 API 都絕對不會觸發 hashchange 事件,即便新的 URL 與舊的 URL 僅哈希不一樣也是如此
  • 若是傳遞了 stateObj,就會更新當前條目關聯的狀態對象;若是傳遞了 url,就會替換當前條目的頁面地址和更改瀏覽器地址欄的地址。

window.onpopstate事件bash

  • 僅在瀏覽器前進後退操做、history.go/back/forward 調用、hashChange 的時候觸發
  • history.pushState 和 history.replaceState 都不會觸發這個事件
  • firfox,chrome 在頁面首次打開時都不會觸發 popstate 事件,可是 safari 會
  • popstate 事件做用範圍僅在於一個 document 裏面,因爲 pushState 和 hashchange 都不會改變網頁的內容也就是 document,因此這樣的網頁裏面纔能有效使用 popstate。假如咱們輸入一個網頁,而且在它裏面添加了 popstate 回調;而後經過連接跳轉的方式轉到另一個網頁;再點擊後退按鈕回到第一個網頁。這樣的狀況,第一個網頁裏面的 popstate 回調,除了有可能由於頁面初始化被觸發外,瀏覽器的後退前進是不會觸發它的,由於這種方式改變了窗口的 document。可是!!!在 iOS 微信內置瀏覽器上,重複上述的操做,會觸發 onpopstate 事件!,因此咱們才能解決在微信瀏覽器上頁面返回不刷新的問題

pushState 和replaceState 的第一個參數 stateObj,會與第三個參數對應的歷史條目綁定在一塊,當 popstate 事件觸發的時候,意味着有新的歷史記錄條目被激活,在 popstate 的事件對象裏面,有一個 state 屬性,會返回這個激活條目關聯的 stateObj 對象的拷貝。一個歷史記錄條目只有當它是被 pushState 建立的,或者用 replaceState 改過的,纔可能有關聯的 stateObj 對象,因此當某些非這2種條件的歷史記錄條目被激活的時候,可能拿到的 stateObj 就是 null。

禁止返回上一頁的一種方案

/**
* 向歷史記錄中手動添加一條記錄
* 用戶選擇返回的時候,每次都會消耗一個 history 實體,此時觸發 popstate 監聽事件,再手動添加一條history實體記錄
* 因此用戶不管點擊多少次都會永遠留在這個頁面了,固然頁面也不會刷新
**/
function pushHistory() {
    window.history.pushState(null, null, "#");
    window.addEventListener("popstate", function (e) {
        console.log(e);
        window.history.pushState(null, null, "#");
    }, false);
}
複製代碼

URL中的#

URL 中的 # 就表示的是 URL 的哈希值

  • #表明網頁中的一個位置,其右邊的字符,就是該位置的標識符。
設置方法:
step1:設置一個錨點<a href="#print">定位到print位置</a>
step2:在頁面須要定位的內容加上id="print"。例如:<div id="print"></div> 或者 <a name="print"></a>
測試:step1設置的錨點,step2中id爲print的內容會滾動到頁面頂端(可觀察滾動條的距離)。同時,頁面的url末端中會出現 # print的哈希值。
複製代碼
  • HTTP請求不包含#
    #號是用來指導瀏覽器動做的,對服務器端徹底無用。
    在第一個#後面出現的任何字符,都會被瀏覽器解讀爲位置標識符。這意味着,這些字符都不會被髮送到服務器端。
    訪問下面的網址: www.w3cschool.cn/#hello 瀏覽器實際發出的請求時這樣的:

  • 改變#不觸發網頁重載
    單單改變#後的內容,瀏覽器只會滾動到相應位置,不會從新加載網頁。
    瀏覽器也不會從新向服務器請求頁面

  • 改變#會改變瀏覽器的訪問歷史
    每一次改變#後的部分,都會在瀏覽器的訪問歷史中增長一個記錄,使用"後退"按鈕,就能夠回到上一個位置。

  • window.location.hash讀取#值
    window.location.hash這個屬性可讀可寫。讀取時,能夠用來判斷網頁狀態是否改變;寫入時,則會在不重載網頁的前提下,創造一條訪問歷史記錄。

  • onhashchange事件
    這是一個HTML 5新增的事件,當#值發生變化時,就會觸發這個事件。IE8+、Firefox 3.6+、Chrome 5+、Safari 4.0+支持該事件。

    // 它的使用方法有三種:
    window.onhashchange = func;
    <body onhashchange="func();">
    window.addEventListener("hashchange", func, false);
    複製代碼
  • Google抓取#的機制
    默認狀況下,Google的網絡蜘蛛忽視URL的#部分。
    可是,Google還規定,若是你但願Ajax生成的內容被瀏覽引擎讀取,那麼URL中可使用"#!",Google會自動將其後面的內容轉成查詢字符串_escaped_fragment_的值。
    好比,Google發現新版twitter的URL:twitter.com/#!/username
    就會自動抓取另外一個URL:twitter.com/?escaped_fragment=/username 經過這種機制,Google就能夠索引動態的Ajax內容。

相關文章
相關標籤/搜索