vue-router之mode

724 阅读2分钟

但凡是使用过vue-router的前端开发工作者,都知道Vue-router包括两种实现模式:hash和history。为了对这两种模式有更直观的认识,我选择简略阅读源码并在此记录。本篇先从模式参数mode入手。

vue-router是Vue框架的路由插件,下面从源码入手,学习一下vue-router是如何实现两种模式的。

模式参数:mode

使用方式:

const router = new VueRouter({
    mode: 'history',
    routes: [...]
})

创建VueRouter实例的时候,直接将mode以构造函数参数的形式传入,在VueRouter类定义(src/index.js)中,使用如下:

export default class VueRouter {
    mode: string;//传入的string类型
    history: HashHistory | HTML5History | AbstractHistory; //实际调用的对象属性
    matcher: Matcher; // url正则匹配方法
    fallback: boolean; // 如果浏览器不支持,history需要回滚为hash模式
    ...
    let mode = options.mode || 'hash' //默认是hash模式
    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
    if (this.fallback) {
      mode = 'hash' // 传入模式为history,但是浏览器不支持,则回滚
    }
    if (!inBrowser) {
      mode = 'abstract' // 不在浏览器环境下,强制设置为‘abstract’
    }
    this.mode = mode

    // 根据mode的值,确定要使用的类,并实例化
    switch(this.mode) {
        case 'history':
            this.history = new HTML5History(this, option.base)
            break;
        case 'hash: 
            this.history = new HashHistory(this, option.base, this.fallback)
            break;
        case 'abstract':
            this.history = new AbstractHistory(this, option.base)
            break;
        default: 
            ...
    }
    // 通过mode确定好history实例后,进行实例的初始化和监听
    init (app: any /* Vue component instance */) {
        const history = this.history

        // 根据history的类别执行相应的初始化操作和监听
        if (history instanceof HTML5History) {
            // 'history'模式初始化
            history.transitionTo(history.getCurrentLocation())
        } else if (history instanceof HashHistory) {
            const setupHashListener = () => {
                history.setupListeners()
            }
            // 'hash'模式初始化
            history.transitionTo(
                history.getCurrentLocation(),
                setupHashListener,
                setupHashListener
            )
        }

        // 添加监听
        history.listen(route => {
            this.apps.forEach((app) => {
                app._route = route
            })
        })
    }
}

自此,基本完成了对mode字段的前期校验和后期使用,history的实例也已经初始化完成。接下来就是路由的一些基本操作,比如push(),replace(),onReady()等。接着以上源码往下看,以下代码只留存关键行

onReady (cb: Function, errorCb?: Function) {
    this.history.onReady(cb, errorCb)
}

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    // this.history会经过初始化操作,这里就会调用原生history的push方法
    this.history.push(location, resolve, reject)
    // this.history.push(location, onComplete, onAbort)
}

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    // replace也是原生方法的调用
    this.history.replace(location, resolve, reject)
    // this.history.replace(location, onComplete, onAbort)
}

从以上代码可看出,VueRouter类中的方法可以认为是一个代理,实际是调用的具体history对象的对应方法, 在init()方法中初始化时,会根据history对象具体的类别执行不同的操作。

说了这么多,什么时候调以及如何调用HTML5History和HashHistory有了大致的了解,后续再看这两个类是怎么实现的。