前端路由
- 什么是路由:地址欄的變化、URL到函數(shù)的映射
后端路由:又稱服務(wù)器路由,服務(wù)器接收前端發(fā)來(lái)的http請(qǐng)求后,會(huì)根據(jù)url找到相應(yīng)的映射函數(shù),然后執(zhí)行函數(shù),并將返回值發(fā)回給客戶端。對(duì)于靜態(tài)資源來(lái)說(shuō),映射函數(shù)就是一個(gè)文件讀取操作;對(duì)于動(dòng)態(tài)資源,映射函數(shù)可能是一個(gè)數(shù)據(jù)庫(kù)讀取操作,或者是數(shù)據(jù)處理操作;根據(jù)讀取的數(shù)據(jù),在服務(wù)端使用相應(yīng)的模板來(lái)渲染頁(yè)面,再返回渲染完畢的頁(yè)面【??這是早期的做法】
如訪問(wèn)http://xxx/about,服務(wù)器收到后,解析出/about,尋找到對(duì)應(yīng)的邏輯,程序吧請(qǐng)求交給對(duì)應(yīng)處理邏輯,完成一次路由分發(fā)。
好處:安全性好、SEO好;
缺點(diǎn):加大服務(wù)器壓力,不利于用戶體驗(yàn)
前端路由:路由的映射函數(shù);當(dāng)訪問(wèn)不同的路徑,會(huì)顯示不同的頁(yè)面組件。控制頁(yè)面跳轉(zhuǎn)
單頁(yè)應(yīng)用路由的特點(diǎn):通過(guò)改變url,不會(huì)向后端發(fā)送新的請(qǐng)求,更新頁(yè)面視圖,所有頁(yè)面均在客戶端渲染
更新地址欄,但是不重新請(qǐng)求頁(yè)面
兩種模式都是使用瀏覽器接口實(shí)現(xiàn)的,除此之外,vue-router還為非瀏覽器提供了abstract模式,原理為用一個(gè)數(shù)組stack模擬出瀏覽器歷史記錄棧的功能。
缺點(diǎn):使用前進(jìn)后退時(shí)會(huì)重新發(fā)請(qǐng)求,沒(méi)有合理利用緩存。
-
hash:#本身以及后面的字符稱為hash,可以通過(guò)window.location來(lái)獲取
??a標(biāo)簽和router-link區(qū)別,hash模式只關(guān)心#后面的路徑-
早期路由實(shí)現(xiàn)基于
location.hash實(shí)現(xiàn),即#及后面的部分url中的hash只是客戶端的一種狀態(tài),當(dāng)向服務(wù)器發(fā)出請(qǐng)求,hash部分不會(huì)被發(fā)送;
hash值改變,會(huì)在瀏覽器訪問(wèn)歷史中增加記錄,可以通過(guò)瀏覽器的前進(jìn)后退控制hash切換
可以通過(guò)
hashchange事件來(lái)監(jiān)聽(tīng)hash的變化-
觸發(fā)的方式:
<a href="#xxx">、location.hash="#xxx",如
http://xxx/about,#后面會(huì)被服務(wù)器忽略,但是JavaScript通過(guò)window.location.hash讀取到,得到響應(yīng)邏輯
-
history API
history.back()、history.forward()、history.go(xx)
html5提供了history API,主要有
history.pushState(狀態(tài)對(duì)象,標(biāo)題,URL)、history.replaceState(),可以在不進(jìn)行刷新的情況下操作歷史記錄。不同的是,前者是增加歷史記錄,后者是替換當(dāng)前歷史記錄
-
對(duì)比:hash兼容性更好;history更正式,可以設(shè)置與當(dāng)前url同源的任意url,路徑更美觀;基于hash的路由不用對(duì)服務(wù)器做改動(dòng),history的路由要對(duì)服務(wù)器做相應(yīng)配置。
????hash會(huì)檢測(cè)#之后的路徑,如果發(fā)現(xiàn)一致,不會(huì)重新加載,只有但與之前是不一樣才會(huì)觸發(fā)記錄添加到棧中;history可以反復(fù)重新加載同一個(gè)頁(yè)面,pushState也會(huì)添加到棧中
pushState設(shè)置的新url可以與當(dāng)前url同源的任意url;hash只可修改#后面的部分,所以只可設(shè)置與當(dāng)前同文檔的url
pushState通過(guò)stateObject可以添加任意類型的數(shù)據(jù)到記錄中;hash只可以添加短字符
pushState可以額外設(shè)置title屬性供后續(xù)使用
abstract:非瀏覽器
-
關(guān)于history的一個(gè)問(wèn)題:?jiǎn)雾?yè)應(yīng)用的理想場(chǎng)景是,僅在進(jìn)入應(yīng)用時(shí)加載index.html,后續(xù)的網(wǎng)絡(luò)操作通過(guò)ajax完成,不會(huì)根據(jù)url重新請(qǐng)求頁(yè)面,但是難免遇到特殊情況:用戶在地址欄輸入地址并回車,瀏覽器重新加載應(yīng)用等;
hash模式僅改變hash部分的內(nèi)容,hash部分不會(huì)包含在http請(qǐng)求中:
http://oursite.com/#/user/id//如果重新請(qǐng)求只會(huì)發(fā)送http://oursite.com/,所以hash模式下遇到根據(jù)url請(qǐng)求頁(yè)面的情況不會(huì)有問(wèn)題history模式會(huì)將url修改得和正常請(qǐng)求后端的url一樣
http://oursite.com/user/id,在此情況下向后端發(fā)送請(qǐng)求,如果后端沒(méi)有配置對(duì)應(yīng)的user/id路由處理,就會(huì)返回404錯(cuò)誤??官方推薦的解決方法:在服務(wù)端增加一個(gè)覆蓋所有情況的候選資源:如果url匹配不到任何靜態(tài)資源,則應(yīng)該返回同一個(gè)index.html頁(yè)面,這個(gè)頁(yè)面就是App依賴的頁(yè)面。這樣服務(wù)器就不會(huì)返回404錯(cuò)誤頁(yè)面,因?yàn)閷?duì)所有頁(yè)面都返回index.html文件。為了應(yīng)對(duì)這種情況,在vue應(yīng)用里面覆蓋所有的路由情況,然后給出一個(gè)404頁(yè)面,或者,如果使用nodejs做后臺(tái),可以使用服務(wù)端的路由來(lái)匹配url,當(dāng)沒(méi)有匹配到路由時(shí)返回404,從而實(shí)現(xiàn)fallback。
- hash & history 前端路由實(shí)現(xiàn)方式
更新視圖但不重新請(qǐng)求頁(yè)面”是前端路由原理的核心
模式參數(shù):
vue-router中通過(guò)mode來(lái)控制路由的實(shí)現(xiàn)模式
mode只是一個(gè)標(biāo)記,
mode:history: HashHistory | HTML5History | AbstractHistory;根據(jù)history的類別執(zhí)行相應(yīng)的初始化操作和監(jiān)聽(tīng);VueRouter類暴露的以下方法實(shí)際是調(diào)用具體history對(duì)象的方法(push & replace)初始化history前,會(huì)進(jìn)行校驗(yàn),瀏覽器不支持HTML5History(通過(guò)
suportsPushState變量判斷)則強(qiáng)制為hash模式,非瀏覽器環(huán)境為abstract模式
hash模式:
改變hash不會(huì)重新加載頁(yè)面,不會(huì)被包含在http請(qǐng)求中,指導(dǎo)瀏覽器動(dòng)作的
為hash的改變添加監(jiān)聽(tīng)事件:
window.addEventListener("hashchange", funcRef, false)每一次改變hash(
window.location.hash),都會(huì)在瀏覽器的訪問(wèn)歷史中增加一個(gè)記錄$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()replace()方法與push()方法不同之處:不是直接對(duì)
window.location.hash進(jìn)行賦值,而是調(diào)用window.location.replace方法將路由進(jìn)行替換監(jiān)聽(tīng)地址欄:我們可以通過(guò)輸入U(xiǎn)RL訪問(wèn),因此要添加路由監(jiān)聽(tīng)并觸發(fā)響應(yīng)行為 通過(guò)
setupListeners實(shí)現(xiàn)
setupListeners () {
window.addEventListener('hashchange', () => {
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
replaceHash(route.fullPath) // 相當(dāng)于調(diào)用了replaceHash
})
})
}
history模式:
History interface是瀏覽器歷史記錄棧提供的接口,通過(guò)back(), forward(), go()等方法,我們可以讀取瀏覽器歷史記錄棧的信息,進(jìn)行各種跳轉(zhuǎn)操作。
新的方法:pushState(), replaceState()對(duì)瀏覽器歷史記錄棧進(jìn)行修改:
window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
代碼結(jié)構(gòu)以及更新視圖的邏輯與hash模式基本類似,只不過(guò)將對(duì)window.location.hash直接進(jìn)行賦值window.location.replace()改為了調(diào)用history.pushState()和history.replaceState()方法。
abstract模式:
原理為用一個(gè)數(shù)組stack模擬出瀏覽器歷史記錄棧的功能。
history VS hash
·
pushState設(shè)置的新URL可以是與當(dāng)前URL同源的任意URL;而hash只可修改#后面的部分,故只可設(shè)置與當(dāng)前同文檔的URLpushState設(shè)置的新URL可以與當(dāng)前URL一模一樣,這樣也會(huì)把記錄添加到棧中;而hash設(shè)置的新值必須與原來(lái)不一樣才會(huì)觸發(fā)記錄添加到棧中pushState通過(guò)stateObject可以添加任意類型的數(shù)據(jù)到記錄中;而hash只可添加短字符串pushState可額外設(shè)置title屬性供后續(xù)使用