路由模式及降級處理
vue-router
默認是 hash
模式 , 即使用 URL
的 hash
來模擬一個完整的 URL
,於是當 URL
改變時,頁面不會重新加載。
vue-router
還支持 history
模式,這種模式充分利用了 history.pushState
來完成 URL
跳轉。
在不支持 history.pushState
的瀏覽器 , 會自動會退到 hash
模式。
是否回退可以通過 fallback
配置項來控制,默認值爲 true
const router = new VueRouter({
mode: 'history', // history 或 hash
routes: [...]
});
詳細使用可參看文檔: HTML5 History 模式
根據 mode 確定類型
首先看下 VueRouter 的構造方法 , 文件位置 src/index.js
import { HashHistory } from './history/hash'
import { HTML5History } from './history/html5'
import { AbstractHistory } from './history/abstract'
// ... more
constructor(options: RouterOptions = {}) {
// ... more
// 默認hash模式
let mode = options.mode || 'hash'
// 是否降級處理
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
// 進行降級處理
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract'
}
this.mode = mode
// 根據不同的mode進行不同的處理
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
我們可以看到,會判斷是否支持 history
, 然後根據 fallback
來確定是否要降級。然後,根據不同的 mode
, 分別實例化不同的 history
。 (HTML5History、HashHistory、AbstractHistory
)
history
我們看到 , HTML5History、HashHistory、AbstractHistory
都是來自 history 目錄。
├── history // 操作瀏覽器記錄的一系列內容
│ ├── abstract.js // 非瀏覽器的history
│ ├── base.js // 基本的history
│ ├── hash.js // hash模式的history
│ └── html5.js // html5模式的history
其中, base.js 裏面定義了 History 類
基本的關係如下圖:
base.js
裏面定義了一些列的方法, hash
、html5
模式,分別繼承了這些方法,並實現了自己特有的邏輯
從外部調用的時候,會直接調用到 this.history
, 然後,由於初始化對象的不同,而進行不同的操作。
接下來, 我們挑選其中一個我們最常用到的 push
方法來解釋一整個過程
push 方法
我們平時調用的時候, 一直都是用 this.$router.push('home')
, 這種形式調用。
首先,在 VueRouter
對象上有一個 push 方法 。
// 文件位置: src/index.js
export default class VueRouter {
// ... more
push(location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.push(location, onComplete, onAbort);
}
}
我們看到,其沒有做任何處理,直接轉發到 this.history.push(location, onComplete, onAbort)
。
上面我們講到,這個處理,會根據 history 的初始化對象不同而做不同處理。我們來分別看看細節
mode === hash
export class HashHistory extends History {
// ...more
// 跳轉到
push(location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this;
this.transitionTo(
location,
route => {
pushHash(route.fullPath);
handleScroll(this.router, route, fromRoute, false);
onComplete && onComplete(route);
},
onAbort
);
}
}
// 切換路由
// 會判斷是否支持pushState ,支持則使用pushState,否則切換hash
function pushHash(path) {
if (supportsPushState) {
pushState(getUrl(path));
} else {
window.location.hash = path;
}
}
mode === history
export class HTML5History extends History {
// ...more
// 增加 hash
push(location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this;
this.transitionTo(
location,
route => {
pushState(cleanPath(this.base + route.fullPath));
handleScroll(this.router, route, fromRoute, false);
onComplete && onComplete(route);
},
onAbort
);
}
}
兩種模式的 push
實現區別並不大,都是調用了 transitionTo
, 區別在於: 一個調用 pushHash
, 一個調用 pushState
.
其他的 go
、 replace
、getCurrentLocation
都是類似的實現方式。
transitionTo
的具體實現,這裏就先不詳聊了,後面聊到路由守護的時候,會細講這一塊內容。