寫在前面:通常 SPA 中前端路由有2種實(shí)現(xiàn)方式:
- window.history
- location.hash
下面就來介紹下這兩種方式具體怎么實(shí)現(xiàn)的
一.history
1.history基本介紹
window.history 對象包含瀏覽器的歷史,window.history 對象在編寫時可不使用 window 這個前綴然评。history是實(shí)現(xiàn)SPA前端路由是一種主流方法州疾,它有幾個原始方法:
- history.back() - 與在瀏覽器點(diǎn)擊后退按鈕相同
- history.forward() - 與在瀏覽器中點(diǎn)擊按鈕向前相同
- history.go(n) - 接受一個整數(shù)作為參數(shù)踢关,移動到該整數(shù)指定的頁面馋评,比如go(1)相當(dāng)于forward()嫩与,go(-1)相當(dāng)于back()唬涧,go(0)相當(dāng)于刷新當(dāng)前頁面
- 如果移動的位置超出了訪問歷史的邊界几晤,以上三個方法并不報錯业踏,而是靜默失敗
在HTML5禽炬,history對象提出了 pushState() 方法和 replaceState() 方法,這兩個方法可以用來向歷史棧中添加數(shù)據(jù)勤家,就好像 url 變化了一樣(過去只有 url 變化歷史棧才會變化)腹尖,這樣就可以很好的模擬瀏覽歷史和前進(jìn)后退了,現(xiàn)在的前端路由也是基于這個原理實(shí)現(xiàn)的伐脖。
2.history.pushState
pushState(stateObj, title, url) 方法向歷史棧中寫入數(shù)據(jù)热幔,其第一個參數(shù)是要寫入的數(shù)據(jù)對象(不大于640kB),第二個參數(shù)是頁面的 title, 第三個參數(shù)是 url (相對路徑)讼庇。
- stateObj :一個與指定網(wǎng)址相關(guān)的狀態(tài)對象绎巨,popstate事件觸發(fā)時,該對象會傳入回調(diào)函數(shù)巫俺。如果不需要這個對象认烁,此處可以填null。
- title:新頁面的標(biāo)題介汹,但是所有瀏覽器目前都忽略這個值却嗡,因此這里可以填null。
- url:新的網(wǎng)址嘹承,必須與當(dāng)前頁面處在同一個域窗价。瀏覽器的地址欄將顯示這個網(wǎng)址。
關(guān)于pushState叹卷,有幾個值得注意的地方:
- pushState方法不會觸發(fā)頁面刷新撼港,只是導(dǎo)致history對象發(fā)生變化坪它,地址欄會有反應(yīng),只有當(dāng)觸發(fā)前進(jìn)后退等事件(back()和forward()等)時瀏覽器才會刷新
- 這里的 url 是受到同源策略限制的,防止惡意腳本模仿其他網(wǎng)站 url 用來欺騙用戶帝牡,所以當(dāng)違背同源策略時將會報錯
3.history.replaceState
replaceState(stateObj, title, url) 和pushState的區(qū)別就在于它不是寫入而是替換修改瀏覽歷史中當(dāng)前紀(jì)錄往毡,其余和 pushState一模一樣
4.popstate事件
- 定義:每當(dāng)同一個文檔的瀏覽歷史(即history對象)出現(xiàn)變化時,就會觸發(fā)popstate事件靶溜。
- 注意:僅僅調(diào)用pushState方法或replaceState方法 开瞭,并不會觸發(fā)該事件,只有用戶點(diǎn)擊瀏覽器倒退按鈕和前進(jìn)按鈕罩息,或者使用JavaScript調(diào)用back嗤详、forward、go方法時才會觸發(fā)瓷炮。另外葱色,該事件只針對同一個文檔,如果瀏覽歷史的切換娘香,導(dǎo)致加載不同的文檔苍狰,該事件也不會觸發(fā)。
- 用法:使用的時候茅主,可以為popstate事件指定回調(diào)函數(shù)舞痰。這個回調(diào)函數(shù)的參數(shù)是一個event事件對象,它的state屬性指向pushState和replaceState方法為當(dāng)前URL所提供的狀態(tài)對象(即這兩個方法的第一個參數(shù))诀姚。
5.history實(shí)現(xiàn)spa前端路由代碼
<a class="api a">a.html</a>
<a class="api b">b.html</a>
// 注冊路由
document.querySelectorAll('.api').forEach(item => {
item.addEventListener('click', e => {
e.preventDefault();
let link = item.textContent;
if (!!(window.history && history.pushState)) {
// 支持History API
window.history.pushState({name: 'api'}, link, link);
} else {
// 不支持,可使用一些Polyfill庫來實(shí)現(xiàn)
}
}, false)
});
// 監(jiān)聽路由
window.addEventListener('popstate', e => {
console.log({
location: location.href,
state: e.state
})
}, false)
popstate監(jiān)聽函數(shù)里打印的e.state便是history.pushState()里傳入的第一個參數(shù),在這里即為
{name: 'api'}
二.Hash
1.Hash基本介紹
url 中可以帶有一個 hash
http://localhost:9000/#/a.html
window 對象中有一個事件是 onhashchange玷禽,以下幾種情況都會觸發(fā)這個事件:
- 直接更改瀏覽器地址赫段,在最后面增加或改變#hash;
- 通過改變location.href或location.hash的值矢赁;
- 通過觸發(fā)點(diǎn)擊帶錨點(diǎn)的鏈接糯笙;
- 瀏覽器前進(jìn)后退可能導(dǎo)致hash的變化,前提是兩個網(wǎng)頁地址中的hash值不同撩银。
2.Hash實(shí)現(xiàn)spa前端路由代碼
// 注冊路由
document.querySelectorAll('.api').forEach(item => {
item.addEventListener('click', e => {
e.preventDefault();
let link = item.textContent;
location.hash = link;
}, false)
});
// 監(jiān)聽路由
window.addEventListener('hashchange', e => {
console.log({
location: location.href,
hash: location.hash
})
}, false)