vue-router异步路由咋个办?源码分析找方案!

3,040 阅读4分钟

其他部分分析暂时不放了,你只需要知道关于路由加载,管理,匹配都是VueRouter创建的实例上的一个属性router.matcher做的,接下来我们主要是针对这个matcher相关代码分析。

router.matcher

VueRouter实例的两个方法matchaddRoutes都是内部matcher对象暴露的方法。

只不过VueRouter的实例的addRoutes方法在调用了对应matcher对象方法后,还执行了以下代码。

if (this.history.current !== START) {
	this.history.transitionTo(this.history.getCurrentLocation())
}

由于在init的时候已经执行过一次transitionTo了,所以这里减少不必要的操作,其实transitionTo里有做相同路由不跳转的处理。

其中match方法的作用为解析页面路径,获取匹配到的路由对象。这在VueRouter中起着重要作用,在每次跳转时,都需要去获取目标路径对应的路由对象,这里也实现到了动态路由匹配的功能。

addRoutes则是动态添加路由规则的方法,这对于异步权限获取并设置对应路由很有帮助。

1. createMatcher

首先,我们来看看matcher是怎么来的。

// VueRouter constructor

this.matcher = createMatcher(options.routes || [], this)

我们知道,matcherVueRouter实例上的一个属性,它是在构造器中用createMatcher方法创建的。

具体实现逻辑:

  1. 首先根据传入的options.routes构建了pathList, pathMap,nameMap
  2. 然后创建了matchaddRoutes方法并返回

可以猜得出来,我们配置的options.routes在这里之后就没啥用了。
因为它内部浅拷贝了options.routes并且生成了所有路由对象的map,就相当于是私有属性了。而之后对路由的处理都是基于这些路由对象来的,所以配置项options.routes到这里也就没啥用了。
说白了就你后面想通过配置项options的引用去修改或者删除某个路由是不太可能的了。

这里的options是指我们在 new VueRouter(options) 时传入的配置参数

2. addRoutes: (routes: Array) => void

matcher暴露出来的能够修改到内部的路由对象map的就只有这个方法了,其作用是扩充原来的路由对象map。

function addRoutes (routes) {
    createRouteMap(routes, pathList, pathMap, nameMap)
}

这里引用的pathList, pathMap,nameMap都是外部作用域(createMatcher方法内作用域)的变量,也就是说他在更新这些变量。 addRoutes为我们提供了异步增加路由的方法,这在某些场合下很有用。

这里提一点就是在方法createRouteMap的实现中,对于后来加的path相同的路由是不会覆盖掉原来的路由的(在pathMappathList中),同name的也一样(在nameMap中)。

3. match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route

其主要逻辑在于根据传入的第一个参数获取其路径path,并根据pathpathMap中取出匹配到的路由对象并返回。
另外还有根据name匹配的逻辑。

总结

// VueRouter

class VueRouter {
    // ...
    match(raw, current, redirectedFrom) {
        return this.matcher.match(raw, current, redirectedFrom)
    }
    push(location, onComplete, onAbort) {
        // ...
        this.history.push(location, onComplete, onAbort)
        // this.history.push 主要是由 history.transitionTo 实现
    }
}

matcher.match方法是比较核心的方法,在路由跳转的基本实现方法history.transitionTo中,就是用this.router.match来获取目标路由的。

function createMatcher (routes) {
    const { pathList, pathMap, nameMap } = createRouteMap(routes)
    // ...
    return {
        match,
        addRoutes
    }
}

这里我们可以很清晰的看出来,matcher并没有暴露出修改路由表的方法。

但实际上我们在运用的过程中,可能会出现以下情景:

未登录前,我们加载的是路由表1。
登录后,我们的路由表增加了,并且原来的路由可能组件也改变了,变成了路由表2。

我们是可以异步加载路由,用addRoutes。但是之前也提到了,相同pathname是不会进行替换的。
所以会出现同pathname的路由仍然没有进行更新。

那解决方法呢?

就在于这个matcher,既然匹配路由是靠matcher,并且matcher在运行时也没有依赖到其他模块。那么我们根据新的routes自己create一个matcher对原来的进行替换,那么不就相当于更新了路由表吗。
但是VueRouter并没有将该方法export出来或挂在VueRouter上,我们也不能直接引用vue-router/src/create-matcher.js对其进行加载,因为他是用flow写的。

import VueRouter from 'vue-router'
import router form './router'

const newRouter = new Router({
    routes: newRoutes
})

router.matcher = newRouter.matcher

router.push('/changed-path')

我们只有新建一个VueRouter实例,来获取新的matcher

OVER! ( * ┒ * )