本文參考:http://m.itdecent.cn/p/bfffb4b8c9fa
https://www.cnblogs.com/GGbondLearn/p/12239651.html
http://shanhuxueyuan.com/news/detail/137.html
hash模式
hash 就是指 url 尾巴后的 # 號(hào)以及后面的字符。這里的 # 和 css 里的 # 是一個(gè)意思。hash 也 稱作 錨點(diǎn),本身是用來(lái)做頁(yè)面定位的,她可以使對(duì)應(yīng) id 的元素顯示在可視區(qū)域內(nèi)。
由于 hash 值變化不會(huì)導(dǎo)致瀏覽器向服務(wù)器發(fā)出請(qǐng)求,而且 hash 改變會(huì)觸發(fā) hashchange 事件,hashChange事件中獲取當(dāng)前的hash值,并根據(jù)hash值來(lái)修改頁(yè)面內(nèi)容,則達(dá)到了前端路由的目的。瀏覽器的進(jìn)后退也能對(duì)其進(jìn)行控制,所以人們?cè)?html5 的 history 出現(xiàn)前,基本都是使用 hash 來(lái)實(shí)現(xiàn)前端路由的。
hash模式背后的原理是onhashchange事件,可以在window對(duì)象上監(jiān)聽這個(gè)事件:
window.onhashchange = function(event){ // 點(diǎn)擊瀏覽器前進(jìn)后退按鈕時(shí)會(huì)觸發(fā)
console.log(event.oldURL, event.newURL);
let hash = location.hash.slice(1);
document.body.style.color = hash;
}
關(guān)鍵的一點(diǎn)是,因?yàn)閔ash發(fā)生變化的url都會(huì)被瀏覽器記錄下來(lái),從而你會(huì)發(fā)現(xiàn)瀏覽器的前進(jìn)后退都可以用了,同時(shí)點(diǎn)擊后退時(shí),頁(yè)面字體顏色也會(huì)發(fā)生變化。這樣一來(lái),盡管瀏覽器沒有請(qǐng)求服務(wù)器,但是頁(yè)面狀態(tài)和url一一關(guān)聯(lián)起來(lái)。
history模式
已經(jīng)有 hash 模式了,而且 hash 能兼容到IE8, history 只能兼容到 IE10,為什么還要搞個(gè) history 呢?
首先,hash 本來(lái)是拿來(lái)做頁(yè)面定位的,如果拿來(lái)做路由的話,原來(lái)的錨點(diǎn)功能就不能用了。其次,hash 的傳參是基于 url 的,如果要傳遞復(fù)雜的數(shù)據(jù),會(huì)有體積的限制,而 history 模式不僅可以在url里放參數(shù),還可以將數(shù)據(jù)存放在一個(gè)特定的對(duì)象中。
hash的hashchange,你只能改變#后面的url片段,而history api則給了前端完全的自由。
history的api
window.history.pushState(state, title, url)
// state:需要保存的數(shù)據(jù),這個(gè)數(shù)據(jù)在觸發(fā)popstate事件時(shí),可以在event.state里獲取
// title:標(biāo)題,基本沒用,一般傳 null
// url:設(shè)定新的歷史記錄的 url。新的 url 與當(dāng)前 url 的 origin 必須是一樣的,否則會(huì)拋出錯(cuò)誤。
// url可以是絕對(duì)路徑,也可以是相對(duì)路徑。
// 如 當(dāng)前url是 https://www.baidu.com/a/,執(zhí)行history.pushState(null, null, './qq/'),
// 則變成 https://www.baidu.com/a/qq/,
// 執(zhí)行history.pushState(null, null, '/qq/'),則變成 https://www.baidu.com/qq/
window.history.replaceState(state, title, url)
// 與 pushState 基本相同,但她是修改當(dāng)前歷史記錄,而 pushState 是創(chuàng)建新的歷史記錄
window.addEventListener("popstate", function() {
// 監(jiān)聽瀏覽器前進(jìn)后退事件,pushState 與 replaceState 方法不會(huì)觸發(fā)
console.log(event.state)
});
history.state;//是一個(gè)屬性,可以得到當(dāng)前頁(yè)的state信息。
// 通過window.history對(duì)象來(lái)控制頁(yè)面歷史記錄跳轉(zhuǎn)
window.history.back() // 后退
window.history.forward() // 前進(jìn)
window.history.go(1) // 前進(jìn)一步,-2為后退兩步,window.history.lengthk可以查看當(dāng)前歷史堆棧中頁(yè)面的數(shù)量
通過pushstate把頁(yè)面的狀態(tài)保存在state對(duì)象中,當(dāng)頁(yè)面的url再變回這個(gè)url時(shí),可以通過event.state取到這個(gè)state對(duì)象,從而可以對(duì)頁(yè)面狀態(tài)進(jìn)行還原,其實(shí)滾動(dòng)條的位置,閱讀進(jìn)度,組件的開關(guān)的這些頁(yè)面狀態(tài)都可以存儲(chǔ)到state的里面。
history模式的問題
在hash模式下,前端路由修改的是#中的信息,而瀏覽器請(qǐng)求時(shí)是不帶它玩的,所以沒有問題.但是在history下,你可以自由的修改path,當(dāng)刷新時(shí),如果服務(wù)器中沒有相應(yīng)的響應(yīng)或者資源,會(huì)分分鐘刷出一個(gè)404來(lái)。
history 模式改變 url 的方式會(huì)導(dǎo)致瀏覽器向服務(wù)器發(fā)送請(qǐng)求,這不是我們想看到的,我們需要在服務(wù)器端做處理:如果匹配不到任何靜態(tài)資源,則應(yīng)該始終返回同一個(gè) html 頁(yè)面。
總結(jié)
hash模式和history模式的區(qū)別
1、hash模式較丑,history模式較優(yōu)雅;
2、pushState設(shè)置的新URL可以是與當(dāng)前URL同源的任意URL;而hash只可修改#后面的部分,故只可設(shè)置與當(dāng)前同文檔的URL;
3、pushState設(shè)置的新URL可以與當(dāng)前URL一模一樣,這樣也會(huì)把記錄添加到棧中;而hash設(shè)置的新值必須與原來(lái)不一樣才會(huì)觸發(fā)記錄添加到棧中;
4、pushState通過stateObject可以添加任意類型的數(shù)據(jù)到記錄中;而hash只可添加短字符串;
5、pushState可額外設(shè)置title屬性供后續(xù)使用;
6、hash兼容IE8以上,history兼容IE10以上;
7、history模式需要后端配合將所有訪問都指向index.html,否則用戶刷新頁(yè)面,會(huì)導(dǎo)致404錯(cuò)誤。