vue-router源碼解析(三)路由模式

路由模式及降級處理

vue-router 默認是 hash 模式 , 即使用 URLhash 來模擬一個完整的 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 類

基本的關係如下圖:
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.

其他的 goreplace 、getCurrentLocation 都是類似的實現方式。

transitionTo的具體實現,這裏就先不詳聊了,後面聊到路由守護的時候,會細講這一塊內容。

其他

系列文章列表
個人博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章