2019獨角獸企業重金招聘Python工程師標準>>>
History API
這里不細說每一個 API 的用法,大家可以看 MDN 的文檔:https://developer.mozilla.org...
重點說其中的兩個新增的API?history.pushState
?和?history.replaceState
這兩個 API 都接收三個參數,分別是
-
狀態對象(state object)?— 一個JavaScript對象,與用pushState()方法創建的新歷史記錄條目關聯。無論何時用戶導航到新創建的狀態,popstate事件都會被觸發,并且事件對象的state屬性都包含歷史記錄條目的狀態對象的拷貝。
-
標題(title)?— FireFox瀏覽器目前會忽略該參數,雖然以后可能會用上。考慮到未來可能會對該方法進行修改,傳一個空字符串會比較安全。或者,你也可以傳入一個簡短的標題,標明將要進入的狀態。
-
地址(URL)?— 新的歷史記錄條目的地址。瀏覽器不會在調用pushState()方法后加載該地址,但之后,可能會試圖加載,例如用戶重啟瀏覽器。新的URL不一定是絕對路徑;如果是相對路徑,它將以當前URL為基準;傳入的URL與當前URL應該是同源的,否則,pushState()會拋出異常。該參數是可選的;不指定的話則為文檔當前URL。
相同之處是兩個 API 都會操作瀏覽器的歷史記錄,而不會引起頁面的刷新。
不同之處在于,pushState會增加一條新的歷史記錄,而replaceState則會替換當前的歷史記錄。
我們拿大百度的控制臺舉例子(具體說是我的瀏覽器在百度首頁打開控制臺。。。)
我們在控制臺輸入
window.history.pushState(null,?null,?"https://www.baidu.com/?name=orange");
好,我們觀察此時的 url 變成了這樣
我們這里不一一測試,直接給出其它用法,大家自行嘗試
window.history.pushState(null, null, "https://www.baidu.com/name/orange");
//url: https://www.baidu.com/name/orangewindow.history.pushState(null, null, "?name=orange");
//url: https://www.baidu.com?name=orangewindow.history.pushState(null, null, "name=orange");
//url: https://www.baidu.com/name=orangewindow.history.pushState(null, null, "/name/orange");
//url: https://www.baidu.com/name/orangewindow.history.pushState(null, null, "name/orange");
//url: https://www.baidu.com/name/orange
注意:這里的 url 不支持跨域,當我們把?
www.baidu.com
?換成?baidu.com
?時就會報錯。
Uncaught DOMException: Failed to execute?'pushState'?on?'History': A history state object?with?URL?'https://baidu.com/?name=orange'?cannot be created?in?a?document?with?origin?'https://www.baidu.com'?and URL?'https://www.baidu.com/?name=orange'.
回到上面例子中,每次改變 url 頁面并沒有刷新,同樣根據上文所述,瀏覽器會產生歷史記錄
這就是實現頁面無刷新情況下改變 url 的前提,下面我們說下第一個參數?狀態對象
如果運行?history.pushState()
?方法,歷史棧對應的紀錄就會存入?狀態對象,我們可以隨時主動調用歷史條目
此處引用 mozilla 的例子
<!DOCTYPE HTML>
<!-- this starts off as http://example.com/line?x=5 -->
<title>Line Game - 5</title>
<p>You are at coordinate <span id="coord">5</span> on the line.</p>
<p><a href="?x=6" onclick="go(1); return false;">Advance to 6</a> or<a href="?x=4" onclick="go(-1); return false;">retreat to 4</a>?
</p>
<script>var currentPage = 5; // prefilled by server!!!!function go(d) {setupPage(currentPage + d);history.pushState(currentPage, document.title, '?x=' + currentPage);}onpopstate = function(event) {setupPage(event.state);}function setupPage(page) {currentPage = page;document.title = 'Line Game - ' + currentPage;document.getElementById('coord').textContent = currentPage;document.links[0].href = '?x=' + (currentPage+1);document.links[0].textContent = 'Advance to ' + (currentPage+1);document.links[1].href = '?x=' + (currentPage-1);document.links[1].textContent = 'retreat to ' + (currentPage-1);}
</script>
我們點擊?Advance to ?
?對應的 url 與模版都會 +1,反之點擊?retreat to ?
?就會都 -1,這就滿足了 url 與模版視圖同時變化的需求
實際當中我們不需要去模擬 onpopstate 事件,官方文檔提供了 popstate 事件,當我們在歷史記錄中切換時就會產生 popstate 事件。對于觸發 popstate 事件的方式,各瀏覽器實現也有差異,我們可以根據不同瀏覽器做兼容處理。
hash
我們經常在 url 中看到 #,這個 # 有兩種情況,一個是我們所謂的錨點,比如典型的回到頂部按鈕原理、Github 上各個標題之間的跳轉等,路由里的 # 不叫錨點,我們稱之為 hash,大型框架的路由系統大多都是哈希實現的。
同樣我們需要一個根據監聽哈希變化觸發的事件 —— hashchange 事件
我們用?window.location
?處理哈希的改變時不會重新渲染頁面,而是當作新頁面加到歷史記錄中,這樣我們跳轉頁面就可以在 hashchange 事件中注冊 ajax 從而改變頁面內容。
這里我在 codepen 上模擬了一下原理:?http://codepen.io/orangexc/pe...點擊預覽
hashchange 在低版本 IE 需要通過輪詢監聽 url 變化來實現,我們可以模擬如下
(function(window) {// 如果瀏覽器不支持原生實現的事件,則開始模擬,否則退出。if ( "onhashchange" in window.document.body ) { return; }var location = window.location,oldURL = location.href,oldHash = location.hash;// 每隔100ms檢查hash是否發生變化setInterval(function() {var newURL = location.href,newHash = location.hash;// hash發生變化且全局注冊有onhashchange方法(這個名字是為了和模擬的事件名保持統一);if ( newHash != oldHash && typeof window.onhashchange === "function" ) {// 執行方法window.onhashchange({type: "hashchange",oldURL: oldURL,newURL: newURL});oldURL = newURL;oldHash = newHash;}}, 100);
})(window);
大型框架的路由當然不會這么簡單,angular 1.x 的路由對哈希、模版、處理器進行關聯,大致如下
app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {$routeProvider.when('/article', {templateUrl: '/article.html',controller: 'ArticleController'}).otherwise({redirectTo: '/index'});$locationProvider.html5Mode(true);
}])
這套路由方案默認是以 # 開頭的哈希方式,如果不考慮低版本瀏覽器,就可以直接調用?$locationProvider.html5Mode(true)
?利用 H5 的方案而不用哈希方案。
總結
兩種方案我推薦 hash 方案,因為照顧到低級瀏覽器,就是不美觀(多了一個 #),兩者兼顧也不是不可,只能判斷瀏覽器給出對應方案啦,不過也只支持 IE8+,更低版本兼容見上文!
這個鏈接的 demo 含有判斷方法:http://sandbox.runjs.cn/show/...?。同時給出 Github 倉庫地址:?minrouter,推薦大家讀下源碼,僅僅 117 行,精辟!
如果在上面鏈接測試時你的 url 里多了一個 #,說明你的瀏覽器該更新啦。
文章出自 orange 的 個人博客?http://orangexc.xyz/