前端路由原理本質就是監聽 URL
的變化,然後匹配路由規則,顯示相應的頁面,並且無須刷新。目前單頁面使用的路由就只有兩種實現方式
- hash
- history
www.test.com/##/
就是 Hash URL
,當 ##
後面的哈希值發生變化時,不會向服務器請求數據,可以通過 hashchange
事件來監聽到 URL
的變化,從而進行跳轉頁面。
vue-router hash
實現源碼(完整源碼訪問https://github.com/vuejs/vue-...):
/**
* 添加 url hash 變化的監聽器
*/
setupListeners () {
const router = this.router
/**
* 每當 hash 變化時就解析路徑
* 匹配路由
*/
window.addEventListener('hashchange', () => {
const current = this.current
/**
* transitionTo:
* 匹配路由
* 並通過路由配置,把新的頁面 render 到 ui-view 的節點
*/
this.transitionTo(getHash(), route => {
replaceHash(route.fullPath)
})
})
}
檢測到 hash
的變化後,就可以通過替換 DOM
的方式來實現頁面的更換。
History
模式是 HTML5
新推出的功能,比之 Hash URL
更加美觀
兩個 API
,pushState
和replaceState
可以改變 url
地址且不會發送請求,還有onpopState
事件。但因爲沒有 #
號,所以當用戶刷新頁面之類的操作時,瀏覽器還是會給服務器發送請求。爲了避免出現這種情況,所以這個實現需要服務器的支持,需要把所有路由都重定向到根頁面。具體可以訪問官網:https://router.vuejs.org/zh/g...
vue-router history
實現源碼(完整源碼訪問https://github.com/vuejs/vue-...)
export class HTML5History extends History {
constructor (router, base) {
super(router, base)
/**
* 原理還是跟 hash 實現一樣
* 通過監聽 popstate 事件
* 匹配路由,然後更新頁面 DOM
*/
window.addEventListener('popstate', e => {
const current = this.current
// Avoiding first `popstate` event dispatched in some browsers but first
// history route not updated since async guard at the same time.
const location = getLocation(this.base)
if (this.current === START && location === initLocation) {
return
}
this.transitionTo(location, route => {
if (supportsScroll) {
handleScroll(router, route, current, true)
}
})
})
}
go (n) {
window.history.go(n)
}
push (location, onComplete, onAbort) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
// 使用 pushState 更新 url,不會導致瀏覽器發送請求,從而不會刷新頁面
pushState(cleanPath(this.base + route.fullPath))
onComplete && onComplete(route)
}, onAbort)
}
replace (location, onComplete, onAbort) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
// replaceState 跟 pushState 的區別在於,不會記錄到歷史棧
replaceState(cleanPath(this.base + route.fullPath))
onComplete && onComplete(route)
}, onAbort)
}
}