- 开始时间:2020-01-27
- 目标主要版本:Vue 3 Router 4
- 引用 issue:issues
- 实现的 PR:
摘要
引入一个 API,允许在路由工作时添加和删除路由记录。
router.addRoute(route: RouteRecord)添加一个新路由router.removeRoute(name: string | symbol)删除一个现有的路由router.hasRoute(name: string | symbol): boolean坚持路由是否存在router.getRoutes(): RouteRecord[]获取当前的路由列表
基本范例
给出一个正在运行的 app 中的 router 实例:
router.addRoute({path: '/new-route',name: 'NewRoute',component: NewRoute})// add to the children of an existing routerouter.addRoute('ParentRoute', {path: 'new-route',name: 'NewRoute',component: NewRoute})router.removeRoute('NewRoute')// normalized version of the records addedconst routeRecords = router.getRoutes()
动机
动态路由是一种功能,它使应用程序能够建立自己的路由系统。这方面的一个案例时 vue-cli ui,它允许添加徒刑插件,这些插件可以有自己的借口。
当前版本的 Vue Router 只支持添加一个新的绝对(absolute)路由。这个 RFC 的想法是添加缺失的功能以允许动态路由。思考这个 API 的不同塑造方式,以及什么对 Vue Router 的未来是最好的。目前,如果不进行黑客方式或者创建一个全新的 Router 实例,就不可能实现上面描述的例子。
从理论上讲,这也可以通过 HMR(Hot Module Replacement,热模块替换)的方式,在原地改变 routes 来改善开发者的体验。
注意:动态路由只有在实现了路径排名(自动路由顺序,允许任何顺序添加路由记录,但仍然能正确匹配)才有意义。添加这个功能的大部分成本是在给路径评分上面。
具体设计
addRoute
- 允许添加一个绝对路由
- 允许添加一个子路由
- 应该如何处理冲突?
- 函数应该返回什么?
代码范例是用 TS 写的,以提供更多关于预期对象的信息:
RouteRecord 是在给实例化 Router 时在 routes 选项中使用的同一类对象。为了给你一个概念,它看起来像(https://router.vuejs.org/api/#routes)。需要注意的是,这个对象与 routes 选项是一致的,因为 RouteRecord 类型在未来的 Vue Router 4 版本中可能会有小的变化。
const routeRecord: RouteRecord = {path: '/new-route',name: 'NewRoute',component: NewRoute}
对于从 addRoute 返回的内容有不同的选项。
返回一个允许删除路由的函数,对未命名的路由很有用。
const removeRoute = router.addRoute(routeRecord)removeRoute() // removes the route record// orrouter.removeRoute('NewRoute') // because names are unique
如果我们在 /new-route 上,添加记录将不会触发替换导航。用户负责通过调用 router.push 或 router.replace 与当前 location router.replace(router.currentRoute.value.fullPath) 来强制进行导航(navigation)。使用 location 的字符串版本可以确保解决一个新的 location 而不是旧的 location。如果路由被添加到一个导航守卫(navigation guard)内,要确保使用 to 参数的值:
router.beforeEach((to, from, next) => {// ...router.addRoute(newRoute)next(to.fullPath)// ...})
因为动态路由 API 是一个高级的 API,大多数用户不会直接使用,有了这种分割,可以使行为更加灵活和优化。建议添加路由的方法仍然是基于配置的方法。
替代品,请查看备选方案。
冲突
当添加一个与现有路由同名的路由时,它应该替换现有的路由。这是最方便的版本,因为它允许替换新的 routes,而不必在使用相同的名称时明确地删除旧的路由。
备选方案:
- 失败并抛出一个错误,要求用户先替换它:不太方便
- 不警告用户,但仍要替换它:可能会出现难以调试的错误
嵌套 routes
根据 addRoute 返回的内容,可以通过引用现有路由的名称来添加一个嵌套路由。这强制父路由被命名。
router.addRoute('ParentRoute', routeRecord)
签名
interface addRoute {(parentName: string | symbol, route: RouteRecord): () => void(route: RouteRecord): () => void}
removeRoute
移除一个路由也会移除它的所有子路由。和 addRoute 一样,有必要触发一个新的导航,以便通过 RouterView 更新当前显示的视图。
interface removeRoute {(name: string | symbol): void}
hasRoute
检查路由是否存在:
interface hasRoute {(name: string | symbol): boolean}
getRoutes
允许读取规范化路由记录的列表:
interface getRoutes {(): RouteRecordNormalized[]}
RouteRecordNormalized 中的内容有待决定,但至少包含了 RouteRecord 的所有现有属性,其中一些已经被规范化(比如,是 components 而不是 component 和 undefined 名称)。
缺点
这个 API 增加了 vue-router 的大小。要让它成为 treeshakable 的就需要允许 matcher(负责解析 path 和进行 path 排名)扩展出更简单的版本,这一点写起来相当复杂,但我们可以转而输出不同版本的 matcher,并允许用户在更精简的 router 中指定 matcher。
备选方案
addRoutes
- 一个 promise,根据添加记录可能触发的导航来 resolve 或 reject(例如,在添加路由前在 /new-route 上)。
``typescript window.location.pathname // "/new-route" // 1. The promise of a function that allows removing the route record const removeRoute = await router.addRoute(routeRecord) // 2. The same kind of promise returned byrouter.push`. const route = await router.addRoute(routeRecord)
removeRoute() // removes the route record // or router.removeRoute(‘NewRoute’) // names are unique
- 对所添加记录的引用(原始记录的规范化副本)```typescript// js object (same reference hold by the router)const normalizedRouteRecord = router.addRoute(routeRecord)router.removeRoute(normalizedRouteRecord)
getRoutes
路由也可能有一个响应式的属性,但这将允许在模版中使用它们,在大多数情况下,这是一个应用程序层面的功能,并且应该以另一种方式来处理事情,有一个响应式的路由记录来源,与 router 同步。在应用层面这样做,可以避免为每个用户增加成本。
采纳策略
- 废弃
addRoutes,在 Vue Router 3 中添加addRoute。其他方法是新的。
没有解决的问题
N/A
