路由實(shí)現(xiàn)原理,就是根據(jù)不同的 url ,在頁面上顯示相應(yīng)的內(nèi)容。而瀏覽器 url 變化時(shí),會(huì)造成頁面的刷新。前端路由要解決問題就是,在改變 url 的情況下,保證頁面的不刷新。
hash
url 中的 hash 改變時(shí),瀏覽器不會(huì)向服務(wù)器發(fā)送請求,頁面不會(huì)刷新。
hash 應(yīng)用的常見場景如,用 hash 實(shí)現(xiàn)頁面導(dǎo)航。如下代碼,點(diǎn)擊 a 標(biāo)簽 '聯(lián)系'(a 標(biāo)簽的 href 屬性值為 #contact),瀏覽器 url 會(huì)變成http://www.xxx.com/#contact,頁面會(huì)定位到元素 id 為 contact 的位置。
<!DOCTYPE html>
<html>
<head>
<style>
.nav li{
float: left;
}
article{
height: 600px;
text-align: center;
border: 1px solid black;
}
</style>
</head>
<body>
<ul class="nav">
<li><a href="#home">首頁</a></li>
<li><a href="#about">關(guān)于</a></li>
<li><a href="#work">作品</a></li>
<li><a href="#contact">聯(lián)系</a></li>
</ul>
<artice>
<section id="home">首頁</section>
<section id="about">關(guān)于</section>
<section id="work">作品</section>
<section id="contact">聯(lián)系</section>
</artice>
</body>
</html>
另外,url 的 hash 變化時(shí),會(huì)觸發(fā) hashchange 事件,該事件上有兩個(gè)屬性,oldURL 和 newURL,分別表示跳轉(zhuǎn)前的 url 和跳轉(zhuǎn)后的 url。通過分析 event.newURL 字段,配合 AJAX 發(fā)送請求,即可讓頁面展示相對應(yīng)的內(nèi)容。
window.addEventListener('hashchange', function(event) {
console.log('Hash changed from ' + event.oldURL);
console.log('Hash changed to ' + event.newURL);
});
history api
html5 的 history api 可以實(shí)現(xiàn)無刷新更改地址欄鏈接,再配合 AJAX 即可實(shí)現(xiàn)頁面無刷新跳轉(zhuǎn)。具體實(shí)現(xiàn):當(dāng)用戶點(diǎn)擊導(dǎo)航鏈接 a 標(biāo)簽時(shí),通過 replaceState 將瀏覽器地址欄的 url 更改為 a 標(biāo)簽 href 屬性的值,同時(shí)配合 AJAX 獲取數(shù)據(jù),替換頁面 DOM 中的內(nèi)容。
history 作為一個(gè)全局變量(即 window.history),不繼承任何屬性,在 html4 標(biāo)準(zhǔn)中,history 有如下屬性和方法:
- History.length 屬性: 返回一個(gè)整數(shù),該整數(shù)表示會(huì)話歷史中元素的數(shù)目,包括當(dāng)前加載的頁。例如,在一個(gè)新的選項(xiàng)卡加載的一個(gè)頁面中,這個(gè)屬性返回1。
- History.back() 方法:前往上一頁, 用戶可點(diǎn)擊瀏覽器左上角的返回按鈕模擬此方法。等價(jià)于history.go(-1).
- History.forward() 方法:在瀏覽器歷史記錄里前往下一頁,用戶可點(diǎn)擊瀏覽器左上角的前進(jìn)按鈕模擬此方法。等價(jià)于 history.go(1).
- History.go() 方法:通過當(dāng)前頁面的相對位置從瀏覽器歷史記錄( 會(huì)話記錄 )加載頁面。比如:參數(shù)為-1的時(shí)候?yàn)樯弦豁?,參?shù)為1的時(shí)候?yàn)橄乱豁? 當(dāng)整數(shù)參數(shù)超出界限時(shí),那么這個(gè)方法沒有任何效果也不會(huì)報(bào)錯(cuò)。調(diào)用沒有參數(shù)的 go() 方法或者不是整數(shù)的參數(shù)時(shí)也沒有效果。
html5 標(biāo)準(zhǔn)中,history新增了 history.pushState(state, title, url)、history.replaceState(state, title, url) 方法,和 popstate 事件。
History.pushState(state, title [, url]) 方法:往瀏覽歷史堆棧的頂部添加一個(gè)狀態(tài),不刷新頁面。pushState() 帶有三個(gè)參數(shù):一個(gè)狀態(tài)對象,一個(gè)標(biāo)題(現(xiàn)在被忽略了),以及一個(gè)可選的URL地址。
state 為一個(gè)對象或 null,它會(huì)在觸發(fā) window 的 popstate 事件(window.onpopstate)時(shí),作為參數(shù)的 state 屬性傳遞過去;如果你像 pushState() 方法傳遞了一個(gè)序列化表示大于 640k 的 state 對象,這個(gè)方法將扔出一個(gè)異常。如果你需要更多的空間,推薦使用 sessionStorage 或者 localStorage。title 為頁面的標(biāo)題,但當(dāng)前所有瀏覽器都忽略這個(gè)參數(shù)。URL 為頁面的 URL,不寫則為當(dāng)前頁;新 URL 必須和當(dāng)前URL在同一個(gè)源下;否則,pushState() 將丟出異常。
- History.replaceState(state, title [, url]):更改當(dāng)前頁面的歷史記錄值,不刷新頁面。參數(shù)同上。這種更改并不會(huì)去訪問該URL。
**popstate ** 事件:當(dāng)活動(dòng)歷史記錄條目更改時(shí),將觸發(fā) popstate 事件。如果被激活的歷史記錄條目是通過對 history.pushState() 的調(diào)用創(chuàng)建的,或者受到對 history.replaceState() 的調(diào)用的影響,popstate 事件的 state 屬性包含歷史條目的狀態(tài)對象的副本。
需要注意的是調(diào)用 history.pushState() 或history.replaceState() 不會(huì)觸發(fā) popstate 事件。只有在做出瀏覽器動(dòng)作時(shí),才會(huì)觸發(fā)該事件,如用戶點(diǎn)擊瀏覽器的回退按鈕。
當(dāng)我們需要監(jiān)聽該事件并作出相應(yīng)響應(yīng)時(shí),我們應(yīng)該這樣組織代碼:
window.addEventListener("popstate", function(e) {
var state = e.state;
// do something...
});
【1】https://juejin.im/post/5aebc4a26fb9a07acc11924d
【2】https://www.renfei.org/blog/html5-introduction-3-history-api.html
【3】https://hijiangtao.github.io/2017/08/20/History-API-and-Location-Object/