這篇文章主要是記錄下HTML5中history提供的pushState
, replaceState
API。最後經過這些API本身實現小型的路由。javascript
關於window.history提供的API請參見Mozilla文檔html
其中history
提供的pushState
和replaceState
2個API提供了操做瀏覽器歷史棧的方法。前端
其中pushState
:java
history.pushState(data, null, '#/page=1'); pushState接收3個參數,第一個參數爲一個obj,表示瀏覽器 第二個參數是document.title的值,通常設定爲`null` 第三個參數string,用以改變 當前url
pushState
方法在改變url
的同時向瀏覽器歷史棧中壓入新的歷史記錄。git
接收url
的參數爲string
類型,用以改變當前地址欄的url.須要注意的一點就是這個參數不能和跨域,即協議,域名,端口必須都是相同的,若是出現跨域的狀況,即會提示:github
Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'http://www.baidu.com/' cannot be created in a document with origin 'http://commanderXL.com' and URL
Example:web
打開www.baidu.com history.pushState(null, null, '?page=1') //地址欄變成 www.baidu.com/?page=1 history.pushState(null, null, '#page=2'); //地址欄變成 www.baidu.com/#page=2
其中replaceState
:ajax
history.replaceState(null, null, '#page=2');
replaceState
接收的參數pushState
相同,可是最終的效果是:地址欄url會根據接收的參數而變化,可是瀏覽器並未在當瀏覽歷史棧中增長瀏覽器的歷史記錄,而是替換當前的瀏覽器歷史記錄。跨域
經過pushState
和replaceState
雖然能改變URL,可是不會主動觸發瀏覽器reload
。瀏覽器
window
對象還提供popstate
方法:
window.addEventListener('popstate', function() { });
這個方法用以監聽瀏覽器在不一樣歷史記錄中進行切換,而觸發相應的事件。
在瀏覽器提供的history對象上還有go
, back
方法,用以模擬用戶點擊瀏覽器的前進後退按鈕。在某個web應用當中,好比點擊了<a>
標籤,發生了頁面的跳轉。這時調用history.back()
;方法後頁面回退,同時頁面發生刷新,這時window.onpopstate
沒法監聽這個事件。可是若是是經過pushState
或者replaceState
來改變URL且不發生瀏覽器刷新的話,再使用history.back()
或history.go()
,這樣popstate
事件會被觸發。
history.pushState({page: 1}, null, '?page=1'); history.pushState({page: 2}, null, '?page=2'); history.back(); //瀏覽器後退 window.addEventListener('popstate', function(e) { //在popstate事件觸發後,事件對象event保存了當前瀏覽器歷史記錄的狀態. //e.state保存了pushState添加的state的引用 console.log(e.state); //輸出 {page: 1} });
PS: 經過pushState
在url上添加?page=1
能夠經過location.search
去獲取search
的內容。不過若是經過location.search
去改變url
的話是會主動觸發瀏覽器reload
的。這個特性能夠和下面將的關於hash
的內容對比下。
API大體瞭解了,那麼這些方法能夠運用到哪些地方呢?一個比較經常使用的場景是就在單頁應用中,經過這些API完成前端的路由設計,利用pushState
, replaceState
能夠改變url
同時瀏覽器不刷新,而且經過popstate
監聽瀏覽器歷史記錄的方式,完成一系列的異步動做。
<a data-href="/post"></a> <a data-href="/login"></a> //路由 const Router = []; const addRoute = (path = '', handle = () => {}) => { let obj = { path, handle } Router.push(obj); } //添加路由定義 addRoute('/post', function() { //do something }); addRoute('/login', function() { //do something }) //路由處理 const routeHandle = (path) => { Router.forEach((item, index) => { if(item.path === path) { item.handle.apply(null, [path]); return true; } }) return false; } //攔截默認的a標籤行爲 document.addEventListener('click', function(e) { let dataset = e.target.dataset; if(dataset) { if(routeHandle(dataset.href)) { //阻止默認行爲 e.preventDefault(); } } })
大體的實現思路就是,經過<a>
添加路由信息,而後攔截<a>
標籤的默認行爲,並與註冊的路由信息進行匹配。若匹配成功調用對應的handle
方法.
不過pushState
和replaceState
方法在低版本的IE瀏覽器下兼容性不是很好。因此能夠進行降級使用hash
來進行路由設計。
hash
?請戳我。
能夠經過location.hash
獲取url
上第一個#(fragment)
及後面的內容。同時還能經過location.hash
改寫其內容,且不會主動觸發瀏覽器reload
。 有些功能是否是和pushState
和replaceState
同樣? 因此爲了兼容到低版本的瀏覽器,能夠經過監聽#
變化來進行路由設計。
那麼如何去監聽呢? 比較粗暴的一種方式就是polling
。
var oldHash = location.hash; setTimeInterval(function() { if(oldHash !== location.hash) { //do something oldHash = location.hash; } }, 100);
不過,H5還提供了一個API: hashchange
。它的就能夠直接代替上面的polling
方法,來監聽#
的變化。
window.addEventListener('hashchange', function() { routeHandle(locaiton.hash); });
這個小型的路由設計能夠參見個人github.
稍微總結下:
上面主要介紹了history提供的一些API,hash的相關知識。在平時能夠運用到SPA當中,Gmail就是經過hash來進行路由設計的。它相對於頁面跳轉來講:
頁面只須要加載一次。後面的頁面切換能夠經過ajax去請求數據。頁面體驗更加流暢;
能夠利用本地緩存,優化頁面體驗。在不一樣頁面切換的過程當中更加流暢;
可進行按需加載...
等等一些實用的好處吧。