在瀏覽器中改變地址欄url,將會觸發頁面資源的重新加載,這使得我們可以在不同的頁面間進行跳轉,得以瀏覽不同的內容。但隨着單頁應用的增多,越來越多的網站采用ajax來加載資源。因為異步加載的特性,地址欄上的資源路徑沒有被改變,隨之而來的問題就是頁面的狀態無法被保存。這導致我們難以通過熟悉的方式(點擊瀏覽器前進/后退按鈕),在前后的頁面狀態間進行切換。
為了解決ajax頁面狀態不能返回的問題,人們想出了一些曲線救國的方法,比如利用瀏覽器hash的特性,將新的資源路徑偽裝成錨點,通過onhashchange事件來改變狀態,同時又避免了瀏覽器刷新。但這樣始終顯得有些hack。
現在HTML5規范為 window.history引入了兩個新api,pushState 和 replaceState,我們可以使用它很方便的達到改變url不重載頁面的目的
——引用於https://blog.csdn.net/helloxiaoliang/article/details/73850428
location.hash和location
當一個頁面的(顯示)狀態被改變時,可以通過location.hash實現刷新頁面后或者copy頁面的URL再打開后保持狀態而不還原。
當hash只有一個#時,location.hash返回空字符串;當hash不止一個#時,location.hash返回#以及#后面的部分。
當 一個window的 hash (URL 中 # 以及#后面的部分,可通過location.hash返回)改變時就會觸發 hashchange 事件。
window.location屬性返回一個有文檔的當前位置的信息的Location對象。
語法:var oldLocation = location; location = newLocation;
無論什么時候,當一個新值被賦予location對象,一個文檔將通過這個URL被加載,就像是location.assign()被這個URL調用了一樣。
location.reload(true);:強制從服務器重新加載當前頁面。
location.replace('http://example.com/#' + location.pathname);:將通過使用replace()方法插入location.pathname的值進hash來重新加載頁面。注意,這replace()方法和字符串的replace()方法不是一個東西。
location.search = sData;:通過改變search屬性來發送一個字符串數據給服務器。"?Some%20data"添加到當前的URL,然后URL被送到服務器(如果服務器沒有采取動作,當前文檔就用這個改變的search字符串重新加載)。
操縱瀏覽器歷史
DOM window 對象通過 history 對象提供了對瀏覽器歷史的訪問。
window.history.back();:和用戶點擊瀏覽器回退按鈕的效果相同。類似的還有window.history.forward();和window.history.go(Num);。另外,window.history.length; 可以獲得歷史堆棧中頁面的數量。執行history.back()或history.forward()后會觸發 window.onpopstate事件。
pushState()方法絕不會導致hashchange 事件被激活,就算新的URL和舊的只在hash上有區別。
history.pushState() 方法和history.replaceState()方法分別可以添加和改變歷史實體。這兩個方法要和window.onpopstate事件一起用。push和pop說明它們的行為就像棧。popstate事件會被觸發當一個文檔的激活的歷史實體改變。如果激活的歷史實體是由history.pushState()創建的,或者是被history.replaceState()改變過,那popstate事件的state屬性(即e.state)會包含一個該激活的歷史實體的sate對象的副本;如果不是,那e.state的值為null。調用history.pushState()方法和history.replaceState()方法不會觸發popstate事件,the popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript)。現在的瀏覽器加載頁面時都不會觸發popstate事件了(以前有的瀏覽器會)。
舉個例子。history.pushState({ foo: "bar" }, "", "bar.html");會使地址欄的http://mozilla.org/foo.html變成http://mozilla.org/bar.html,但是不會使瀏覽器加載http://mozilla.org/bar.html,甚至都不會使瀏覽器檢查http://mozilla.org/bar.html是否存在。假設用戶在這個頁面導航到了http://google.com,然后點擊瀏覽器的回退按鈕。這時,地址欄變成http://mozilla.org/bar.html,而且瀏覽器會加載http://mozilla.org/bar.html。如果這時你讀取history.state,你會得到{ foo: "bar" }(即history.pushState()的第一個參數)。這時popstate事件不將被觸發(?),因為頁面是通過被加載而顯示的(頁面加載時觸發的是load事件而不是popstate事件)。如果我們再點擊回退按鈕,才會回到http://mozilla.org/foo.html,這時文檔會獲得一個popstate事件(可以通過這個事件改變文檔內容),且history.state是null。總之,history.pushState()會添加一個歷史實體並激活它(也就是現在的頁面變成新添加的歷史實體了),但暫時還是顯示原來的歷史實體,只有當再次回到這個頁面時(包括刷新,但是要注意刷新不會觸發popstate事件哦,只有前進和后退會觸發)才會顯示新添加的歷史實體。
history.pushState()接收三個參數,一個對象(用作state),一個字符串(用作標題,但是目前都被瀏覽器忽略),一個字符串(用作URL,是可選的):
·state會成為history.pushState()添加的歷史實體的屬性,可以用history.state獲得。傳入state的參數要能字符串化,比如能用JSON字符串化,因為火狐瀏覽器會把state這個對象存在用戶的磁盤,以便當用戶重啟瀏覽器時state這個對象能恢復實際傷並不能恢復。state這個對象字符串化后的大小有限制(火狐是640K),如果需要更多的空間,你應該使用sessionStorage 和/或localStorage。
·第二個參數傳入空的字符串即可。
·傳入的URL可以是相對的也可以是絕對的。如果是相對的,它將相對於當前的URL被解析。傳入的URL必須和當前的URL同源。如果不傳入URL,則會被設置為當前的URL。
總之,調用pushState()和設置window.location = "#foo"(設置hash,與window.location.hash = "#foo"等效,且window.可省略)很像,兩者都創建了並激活了一個新的歷史實體。但是前者由以下優點:
1. 新的URL可以是任何和當前URL同源的URL,而設置window.location讓你智能呆在同一個文檔里,因為你只改變了hash。
2. 你可以不改變URL而創建新的歷史實體,而通過設置window.location = "#foo";創建一個新的歷史實體則必須要求當前的hash不是#foo。
3. 你可以在state中放入任何數據,而用hash就只能在URL末尾放入短字符串形式的數據。
history.replaceState()和history.pushState()很像,除了是改變當前的歷史實體而不是創建一個新的。當你像更新當前的歷史實體的state對象或URL來響應用戶的行為時,replaceState()相當有用。