history.pushState無刷新改變url

經過history.pushState無刷新改變urljavascript

背景

在瀏覽器中改變地址欄url,將會觸發頁面資源的從新加載,這使得咱們能夠在不一樣的頁面間進行跳轉,得以瀏覽不一樣的內容。但隨着單頁應用的增多,愈來愈多的網站採用ajax來加載資源。由於異步加載的特性,地址欄上的資源路徑沒有被改變,隨之而來的問題就是頁面的狀態沒法被保存。這致使咱們難以經過熟悉的方式(點擊瀏覽器前進/後退按鈕),在先後的頁面狀態間進行切換。 
爲了解決ajax頁面狀態不能返回的問題,人們想出了一些曲線救國的方法,好比利用瀏覽器hash的特性,將新的資源路徑假裝成錨點,經過onhashchange事件來改變狀態,同時又避免了瀏覽器刷新。但這樣始終顯得有些hack。 
如今HTML5規範爲 window.history引入了兩個新api,pushState 和 replaceState,咱們可使用它很方便的達到改變url不重載頁面的目的。css


表現

改變地址欄url,不刷新頁面。 
這裏寫圖片描述
觀察地址欄,能夠看到當url路徑由」join」改變爲」login」時,頁面狀態也隨之改變,但並無形成背景圖片等資源的從新加載。 
此時」join」被壓入歷史棧,當點擊瀏覽器後退按鈕時仍然可以回到」join」狀態。html


使用

pushStatereplaceState方法相似,都有改變當前地址欄URL的做用。主要區別在於pushState會在瀏覽器中建立一條新的歷史紀錄,而replaceState僅僅替換將當前地址爲指定URL。 
下面以pushState接口爲例:java

API

history.pushState(state, title[, url]);web

事件

執行history.back()history.forward()後觸發 window.onpopstate事件ajax

參數

state: 對象,能夠存存放一些數據表示當前狀態。當瀏覽器執行前進後退操做時觸發onpopstate事件,state將成爲event的子對象,可經過event.state獲取先前狀態。可是注意state中的屬性值不能爲引用類型對象,會報ObjectCloneError(對象克隆異常),例如容許{data:」test」},不容許{data:document.querySelector(‘#testId’)}。 
title:目前無特殊意義,通常能夠傳入 document.title 或 」(空字符串)。 
url:要替換的url,若是是pushState則會添加一條歷史記錄,不容許跨域。chrome

示例

history.pushState({title:"login"}, "login", "fish/login"); window.addEventListener("popstate", function(event){ if(event.state) { var state = event.state.title; switch(state) { case "login":.............;break; case "join" :.............;break; case "home" :.............;break; } } }, false);

實例

下面以一個具體實例來展現pushState的做用,注意地址欄的變化。api

效果

pushState

兩次點擊白色圓環改變頁面背景,將在當前瀏覽器歷史會話(window.history)中寫入兩條新記錄:「/pushState.html?state=blue」和「/pushState.html?state=org」。 
點擊瀏覽器後退/前進按鈕至關於執行history.back()history.forward()方法,將觸發onpopstate事件,經過監聽onpopstate事件改變相應狀態。跨域

源代碼

本實例在Chrome下編寫調試,請在Chrome、Firefox、IE10+等現代瀏覽器中運行。瀏覽器

<!DOCTYPE html> <html> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <meta name="renderer" content="webkit" /> <head> <title>pushState demo</title> <style> body { font-family: "Microsoft YaHei"; transition: background-color .3s; } .bg-org { color: #383c3c; background-color: #FF6633; } .bg-blue { color: #fbfaf5; background-color: #6699FF; } .time { margin-top: 20%; text-align: center; font-size: 4em; font-weight: 100; } .switch { margin: auto; width: 30px; height: 30px; position:absolute; bottom:25%; left:0; right:0; cursor:pointer; box-shadow: 0 0 0 5px rgba(255,255,255,.6); border-radius: 50%; transition: box-shadow .1s; } .switch:hover { box-shadow: 0 0 0 5px rgba(255,255,255,.75); } .switch:active { box-shadow: 0 0 0 30px rgba(255,255,255,.4); } </style> </head> <body class="bg-org"> <h1 id="time" class="time">Loading...</h1> <div id="switch" class="switch"></div> <script> var time = $('#time'); function $(selector) {return document.querySelector(selector);} // 顯示當前時間 setInterval(function(){ var date = new Date(), format = function(n) {return n<10?'0'+n:n}; time.innerHTML = format(date.getHours()) + ' : ' + format(date.getMinutes()) + ' : ' + format(date.getSeconds()); }, 500); $('#switch').addEventListener('click', toggleState, false); // 監聽popstate事件 history.pushState && window.addEventListener("popstate", function(e) { // 獲取history.state對象中的狀態信息 // 在這裏state將自動成爲event的子對象,可直接經過event.state訪問 var flag = e.state && e.state.title; $('body').className = flag || ($('body').className=='bg-org'?'bg-blue':'bg-org'); }, false); function toggleState(e) { var flag = $('body').className=='bg-org'?'bg-blue':'bg-org'; // 新建歷史記錄,將當前狀態信息保存至history.state中 history.pushState && history.pushState({ title: flag }, flag, 'pushState.html?state='+flag.split('-')[1]); $('body').className = flag; } </script> </body> </html>

更多

replaceState

前面曾說過,pushStatereplaceState的區別在於pushState會在瀏覽器中建立一條新歷史紀錄,而replaceState僅替換當前地址。 
咱們將上面的實例稍做修改,觀察replaceState的具體表現。 
替換history.pushStatehistory.replaceState: 
這裏寫圖片描述

history.pushState && history.replaceState({ title: flag }, flag, 'pushState.html?state='+flag.split('-')[1]);

對比運行結果能夠看到,屢次執行replaceState僅改變當前地址欄的URL,而沒有建立新的歷史記錄。

window.location

下面咱們再使用傳統的window.location跳轉,對比與pushState的區別。 
傳統location方式

<script> if(urlParam('state')=='blue') { $('body').className = 'bg-blue'; } else { $('body').className = 'bg-org'; } var time = $('#time'); function $(selector) {return document.querySelector(selector);} // 顯示當前時間 setInterval(function(){ var date = new Date(), format = function(n) {return n<10?'0'+n:n}; time.innerHTML = format(date.getHours()) + ' : ' + format(date.getMinutes()) + ' : ' + format(date.getSeconds()); }, 500); $('#switch').addEventListener('click', toggleState, false); function toggleState(e) { var flag = $('body').className=='bg-org'?'bg-blue':'bg-org'; window.location = location.pathname + '?state=' + flag.split('-')[1]; $('body').className = flag; } /** * 獲取url參數 * @param {String} name 參數名 * @return {String} 參數值 */ function getUrlParam(name){ var reg, value; reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); value = window.location.search.substr(1).match(reg); return value==null?null:decodeURI(value[2]); } </script>

能夠看到使用傳統location跳轉將會形成頁面重載,雖然在功能上可以達到一樣的目的,但在用戶體驗上將大打折扣。

#hash

最後再來介紹另外一種無刷新技巧window.location.hash的使用。 
URL中#稱爲位置的標識符,表明網頁中的一個位置,當瀏覽器讀取這個URL後,會自動將可視區域滾動至所定義的錨點處。HTTP請求中不包括#,也就是說改變#後的內容不會向服務器發起請求,所以也就不會引發頁面重載。 
window.location.hash這個屬性可讀可寫。讀取時,能夠用來判斷網頁狀態是否改變;寫入時,則會在不重載網頁的前提下,創造一條訪問歷史記錄。 
當#值發生變化時,就會觸發onhashchange事件。 
hash

<script> var time = $('#time'); function $(selector) {return document.querySelector(selector);} // 顯示當前時間 setInterval(function(){ var date = new Date(), format = function(n) {return n<10?'0'+n:n}; time.innerHTML = format(date.getHours()) + ' : ' + format(date.getMinutes()) + ' : ' + format(date.getSeconds()); }, 500); // 監聽onhashchange事件 window.addEventListener("hashchange", function(e) { // 獲取hash值判斷頁面狀態 var flag = location.hash && location.hash.substring(1); $('body').className = 'bg-'+flag || ($('body').className=='bg-org'?'bg-blue':'bg-org'); }, false); $('#switch').addEventListener('click', toggleState, false); function toggleState(e) { var flag = $('body').className=='bg-org'?'bg-blue':'bg-org'; // 在url中寫入新的hash值 location.hash = flag.split('-')[1]; $('body').className = flag; } </script>



須要注意的是每次hash發生變化時都會觸發onhashchange事件。而onpopstate事件只會在歷史記錄(history entry)變化時觸發,好比執行瀏覽器的前進/後退操做。

最後

本文介紹了pushStatereplaceState誕生的背景以及API的使用。又經過一個簡單的實例與location,hash等技術進行了對比。相信你們已經能夠很直觀的看到pushState 和 replaceState的做用及區別。若有錯誤的地方歡迎交流指正。 
隨着HTML5標準的肯定,pushState與ajax技術,將會在單頁應用中發揮愈來愈重要的做用,國外也有一個叫pjax(pushState+ajax)的庫將它們予以封裝。

 https://blog.csdn.net/helloxiaoliang/article/details/73850428
相關文章
相關標籤/搜索