- 一般來說倦始,@angular/router斗遏、react-router、vue-router這些路由插件提供了兩種不同的路由方式:Hash和History鞋邑,有事也會提供費瀏覽器環(huán)境下的路由方式Abstract诵次,在vue-router中是使用了外觀模式將幾種不同的路由方式提供了一個一致的高層接口账蓉,讓我們可以更解耦的在不同路由方式中切換。
- 這兩種方式除了外觀上的不同之外逾一,還有一個區(qū)別是:Hash方式的狀態(tài)保存需要另行傳遞铸本,而HTML5 History原生提供了自定義狀態(tài)傳遞的能力,我們可以直接利用其來傳遞信息遵堵。
Hash
1箱玷、相關(guān)Api
Hash 方法是在路由中帶有一個 #,主要原理是通過監(jiān)聽 # 后的 URL 路徑標識符的更改而觸發(fā)的瀏覽器 hashchange 事件陌宿,然后通過獲取 location.hash 得到當前的路徑標識符锡足,再進行一些路由跳轉(zhuǎn)的操作。
-
location.href
:返回完整的URL -
location.hash
:返回URL的錨部分 -
location.pathname
:返回URL路徑名 -
hashchange
事件:當location.hash
發(fā)生改變時壳坪,將觸發(fā)這個事件
比如訪問一個路徑 http://lengfengshuang.cn/base/#/page1舶得,那么上面幾個值分別為:
# http://lengfengshuang.cn/base/#/page1
{
"href": "http://lengfengshuang.cn/base/#/page1",
"pathname": "/base/",
"hash": "#/page1"
}
注意: 因為 Hash 方法是利用了相當于頁面錨點的功能,所以與原來的通過錨點定位來進行頁面滾動定位的方式?jīng)_突爽蝴,導(dǎo)致定位到錯誤的路由路徑沐批,所以需要采用別的辦法,之前在寫 progress-catalog 這個插件碰到了這個情況蝎亚。
2珠插、實例
原理是把目標路由和對應(yīng)的回調(diào)記錄下來,點擊跳轉(zhuǎn)觸發(fā) hashchange 的時候獲取當前路徑并執(zhí)行對應(yīng)回調(diào)颖对,效果:
class RouterClass {
constructor() {
this.routes = {} // 記錄路徑標識符對應(yīng)的cb
this.currentUrl = '' // 記錄hash只為方便執(zhí)行cb
window.addEventListener('load', () => this.render())
window.addEventListener('hashchange', () => this.render())
}
/* 初始化 */
static init() {
window.Router = new RouterClass()
}
/* 注冊路由和回調(diào) */
route(path, cb) {
this.routes[path] = cb || function() {}
}
/* 記錄當前hash,執(zhí)行cb */
render() {
this.currentUrl = location.hash.slice(1) || '/'
this.routes[this.currentUrl]()
}
}
如果希望使用腳本來控制 Hash 路由的后退磨隘,可以將經(jīng)歷的路由記錄下來缤底,路由后退跳轉(zhuǎn)的實現(xiàn)是對 location.hash
進行賦值。但是這樣會引發(fā)重新引發(fā) hashchange 事件
番捂,第二次進入 render
个唧。所以我們需要增加一個標志位,來標明進入 render
方法是因為回退進入的還是用戶跳轉(zhuǎn)设预。
class RouterClass {
constructor() {
this.isBack = false
this.routes = {} // 記錄路徑標識符對應(yīng)的cb
this.currentUrl = '' // 記錄hash只為方便執(zhí)行cb
this.historyStack = [] // hash棧
window.addEventListener('load', () => this.render())
window.addEventListener('hashchange', () => this.render())
}
/* 初始化 */
static init() {
window.Router = new RouterClass()
}
/* 記錄path對應(yīng)cb */
route(path, cb) {
this.routes[path] = cb || function() {}
}
/* 入棧當前hash徙歼,執(zhí)行cb */
render() {
if (this.isBack) { // 如果是由backoff進入,則置false之后return
this.isBack = false // 其他操作在backoff方法中已經(jīng)做了
return
}
this.currentUrl = location.hash.slice(1) || '/'
this.historyStack.push(this.currentUrl)
this.routes[this.currentUrl]()
}
/* 路由后退 */
back() {
this.isBack = true
this.historyStack.pop() // 移除當前hash鳖枕,回退到上一個
const { length } = this.historyStack
if (!length) return
let prev = this.historyStack[length - 1] // 拿到要回退到的目標hash
location.hash = `#${ prev }`
this.currentUrl = prev
this.routes[prev]() // 執(zhí)行對應(yīng)cb
}
}
HTML5 History
1魄梯、相關(guān)Api
HTML5 提供了一些路由操作的 Api,關(guān)于使用可以參看 這篇 MDN 上的文章宾符,這里就列舉一下常用 Api 和他們的作用酿秸。
-
history.go(n)
:路由跳轉(zhuǎn)。比如n為 2 是往前移動2個頁面魏烫,n為 -2 是向后移動2個頁面辣苏,n為0是刷新頁面 -
history.back()
:路由后退肝箱,相當于history.go(-1)
-
history.forward()
:路由前進,相當于history.go(1)
-
history.pushState()
:添加一條路由歷史記錄稀蟋,如果設(shè)置跨域網(wǎng)址則報錯 -
history.replaceState()
:替換當前頁在路由歷史記錄的信息 -
popstate 事件
:當活動的歷史記錄發(fā)生變化煌张,就會觸發(fā) popstate 事件,在點擊瀏覽器的前進后退按鈕或者調(diào)用上面前三個方法的時候也會觸發(fā)
2退客、實例
將之前的例子改造一下骏融,在需要路由跳轉(zhuǎn)的地方使用 history.pushState
來入棧并記錄cb
,前進后退的時候監(jiān)聽 popstate
事件拿到之前傳給 pushState
的參數(shù)并執(zhí)行對應(yīng) cb
井辜,因為借用了瀏覽器自己的 Api绎谦,因此代碼看起來整潔不少。
class RouterClass {
constructor(path) {
this.routes = {} // 記錄路徑標識符對應(yīng)的cb
history.replaceState({ path }, null, path) // 進入狀態(tài)
this.routes[path] && this.routes[path]()
window.addEventListener('popstate', e => {
const path = e.state && e.state.path
this.routes[path] && this.routes[path]()
})
}
/* 初始化 */
static init() {
window.Router = new RouterClass(location.pathname)
}
/* 注冊路由和回調(diào) */
route(path, cb) {
this.routes[path] = cb || function() {}
}
/* 跳轉(zhuǎn)路由粥脚,并觸發(fā)路由對應(yīng)回調(diào) */
go(path) {
history.pushState({ path }, null, path)
this.routes[path] && this.routes[path]()
}
}
Hash 模式是使用 URL 的 Hash 來模擬一個完整的 URL窃肠,因此當 URL 改變的時候頁面并不會重載。History 模式則會直接改變 URL刷允,所以在路由跳轉(zhuǎn)的時候會丟失一些地址信息冤留,在刷新或直接訪問路由地址的時候會匹配不到靜態(tài)資源。因此需要在服務(wù)器上配置一些信息树灶,讓服務(wù)器增加一個覆蓋所有情況的候選資源纤怒,比如跳轉(zhuǎn) index.html 什么的,一般來說是你的 app 依賴的頁面天通,事實上 vue-router 等庫也是這么推介的泊窘,還提供了常見的服務(wù)器配置。