完全指南:定义、跳转与底层匹配原理)
前端路由【免费下载链接】vue-router The official router for Vue 2项目地址https://gitcode.com/gh_mirrors/vu/vue-router点击查看免费下载命名路由Named Routes是 Vue RouterVue 2 官方路由库本仓库即其源码仓库中一种以字符串名称标识路由的方式相比直接用路径字符串它在编写router-link链接和调用router.push()等编程式导航时更加稳定、可读且易于维护。本指南以仓库文档 docs-gitbook/es/essentials/named-routes.md及英文版 docs/guide/essentials/named-routes.md为主线结合源码实现与 examples/named-routes/app.js 完整示例带你掌握命名路由的定义方式、声明式与编程式跳转用法并深入理解其内部解析与参数填充原理。为什么需要命名路由在开发中通过 URL 路径进行链接和导航存在明显的脆弱性一旦路径结构调整例如/user/:userId改为/users/:userId所有硬编码路径的router-link和router.push()调用都必须同步修改极易遗漏。命名路由正是为应对这一场景而设计用稳定的名称代替易变的路径。当链接或导航到某条路由时只需引用它的名字由路由器负责根据名称解析出最终路径。这在以下场景尤为便利链接到一条路由或通过代码执行导航路径中带有动态参数如:userId需要同时传参路由路径未来可能变更希望把路径集中收敛在路由配置表中维护。定义命名路由在routes选项中添加name要使用命名路由只需在创建 Router 实例的routes配置数组中为某条路由记录添加name字段const router new VueRouter({ routes: [ { path: /user/:userId, name: user, component: User } ] })name的类型为字符串且全局唯一。路由器在初始化时会把所有路由记录收集进一张以名称为键的索引表nameMap中供后续按名查找。源码层面的注册机制命名路由的注册发生在 src/create-route-map.js 的createRouteMap函数中。它会遍历routes数组为每条路由调用addRouteRecord最终维护三张表pathList路径列表用于控制路径匹配的优先级pathMap以路径为键的路由记录表nameMap以名称为键的路由记录表。相关核心代码src/create-route-map.jsfunction addRouteRecord (pathList, pathMap, nameMap, route, parent, matchAs) { const { path, name } route // ... 路径规范化、正则编译、children 递归处理等 if (name) { if (!nameMap[name]) { nameMap[name] record } else if (process.env.NODE_ENV ! production !matchAs) { warn( false, Duplicate named routes definition: { name: ${name}, path: ${record.path} } ) } } }可以看到两点关键行为首次注册名称不存在时直接写入nameMap[name] record重复名称警告若同名路由已存在且非别名场景开发环境下会输出Duplicate named routes definition警告后注册的记录会被忽略。这印证了「名称必须全局唯一」的约束。动态添加命名路由除了静态配置还可以在运行时用router.addRoute()动态注册命名路由对应 src/router.js 的实现其内部调用matcher.addRoute后者在 src/create-matcher.js 中把新路由并入同一份nameMaprouter.addRoute({ path: /about, name: about, component: About })使用router-link声明式链接到命名路由声明式导航时将to属性绑定为一个对象字面量其中用name指定目标路由用params提供动态路径参数router-link :to{ name: user, params: { userId: 123 }}User/router-link注意to前的冒号v-bind不能省略——只有绑定对象时 Vue 才能把它作为 JavaScript 对象解析而不是当作字符串路径。router-link内部如何处理torouter-link组件的实现位于 src/components/link.js。其render函数在渲染时调用router.resolve(this.to, current, this.append)完成解析点击时则执行router.push(location)或router.replace(location)取决于replace属性见 src/components/link.jsconst { location, route, href } router.resolve( this.to, current, this.append ) // ... const handler e { if (guardEvent(e)) { if (this.replace) { router.replace(location, noop) } else { router.push(location, noop) } } }router.resolve定义于 src/router.js依次完成normalizeLocation标准化目标 →match匹配路由 → 生成href。这意味着声明式router-link与编程式导航最终走的是同一套解析管道行为完全一致。关于 params 的补充说明上例中params的键名userId必须与路由配置path: /user/:userId中的动态段名完全一致否则参数无法填充params中多余的键会被忽略缺失的必填参数会导致开发环境警告详见下文fillParams一节若只需在 URL 上附加查询参数应使用query而非params见下文「组合 query 与 hash」。使用router.push()编程式导航到命名路由代码中导航到命名路由时传入的正是与router-link的to完全相同的对象router.push({ name: user, params: { userId: 123 } })两种方式最终都会导航到/user/123这个路径。router.push定义于 src/router.js它还支持 Promise 与回调两种调用形态// 回调风格 router.push({ name: user, params: { userId: 123 } }, onComplete, onAbort) // Promise 风格浏览器支持 Promise 且未传回调时自动返回 Promise router.push({ name: user, params: { userId: 123 } }) .then(() { /* 导航成功 */ }) .catch(err { /* 导航失败 */ })类似地若希望替换当前历史记录而不是新增一条可使用router.replace()它接受与push相同的目标对象。命名路由的解析与匹配原理从传入{ name: user, params: { userId: 123 } }到最终导航至/user/123底层经历了两个关键步骤。第一步normalizeLocation识别命名目标src/util/location.js 中的normalizeLocation负责把各种形态的原始目标字符串路径、location 对象标准化。当检测到next.name存在时它会保留名称与参数走「命名路由」分支而不再走基于路径的解析逻辑let next typeof raw string ? { path: raw } : raw // named target if (next._normalized) { return next } else if (next.name) { next extend({}, raw) const params next.params if (params typeof params object) { next.params extend({}, params) } return next }值得注意的还有它的「相对 params」分支当目标只提供params而没有name和path时会基于当前路由补齐参数——若当前路由本身有名称则沿用当前路由的name并把参数合并进去见 src/util/location.js。这一行为允许你在导航到同一路由时只更新部分参数。第二步matcher.match按名称查表并填充路径标准化后的 location 传入匹配器matchsrc/create-matcher.js。如果存在name匹配器直接到nameMap中查表if (name) { const record nameMap[name] if (process.env.NODE_ENV ! production) { warn(record, Route with name ${name} does not exist) } if (!record) return _createRoute(null, location) // 取出该路由正则中的参数名排除可选参数 const paramNames record.regex.keys .filter(key !key.optional) .map(key key.name) if (typeof location.params ! object) { location.params {} } // 若当前路由同名参数存在且目标未提供则继承 if (currentRoute typeof currentRoute.params object) { for (const key in currentRoute.params) { if (!(key in location.params) paramNames.indexOf(key) -1) { location.params[key] currentRoute.params[key] } } } location.path fillParams(record.path, location.params, named route ${name}) return _createRoute(record, location, redirectedFrom) }这里有三点值得展开不存在则告警若nameMap中没有对应名称开发环境会输出Route with name xxx does not exist警告并返回一个未匹配的空路由_createRoute(null, location)对应页面会渲染为空参数继承从当前路由继承同名参数使得「从/user/1导航到/user/2」这类场景只需传入{ name: user, params: { userId: 2 } }最终路径生成fillParams(record.path, location.params, ...)把record.path如/user/:userId中的动态段替换为实际参数值产出/user/123。fillParams参数填充与缺失校验fillParams实现在 src/util/params.js。它基于path-to-regexp的compile预编译路径模板带缓存再以pretty: true模式填充参数export function fillParams (path, params, routeMsg) { params params || {} try { const filler regexpCompileCache[path] || (regexpCompileCache[path] Regexp.compile(path)) // 兼容星号通配路由的 pathMatch 参数issue #2505、#3106 if (typeof params.pathMatch string) params[0] params.pathMatch return filler(params, { pretty: true }) } catch (e) { if (process.env.NODE_ENV ! production) { warn(typeof params.pathMatch string, missing param for ${routeMsg}: ${e.message}) } return } finally { delete params[0] } }因此当必填参数缺失或参数与路径模板不匹配时开发环境会收到形如missing param for named route user的警告同时路径填充失败。这也提醒开发者使用命名路由时务必保证params键与路径动态段一一对应。命名路由与 query、hash 的组合命名路由的 location 对象同样支持query与hash字段二者会被保留并拼接进最终 URLrouter.push({ name: user, params: { userId: 123 }, query: { plan: private }, hash: #about }) // 导航至 /user/123?planprivate#about这些字段经_createRoute交由 src/util/route.js 的createRoute组装fullPath由path query hash拼接而成route.query会被深拷贝以避免外部篡改返回的路由对象还被Object.freeze冻结为只读。命名路由与重定向、别名的协作命名路由常与重定向配合实现「用名称引用重定向目标」。仓库测试 test/unit/specs/create-map.spec.js 中就有这种用法{ name: bar-redirect, redirect: { name: bar-redirect.baz } }在 src/create-matcher.js 的redirect逻辑中如果重定向目标声明为{ name: ... }会再次走按名称匹配的分支if (name) { // resolved named direct const targetRecord nameMap[name] if (process.env.NODE_ENV ! production) { assert(targetRecord, redirect failed: named route ${name} not found.) } return match({ _normalized: true, name, query, hash, params }, undefined, location) }注意此处重定向对象同样支持携带query、hash、params且重定向目标不存在时会抛出断言错误。关于重定向与别名的更多细节可参考 docs/guide/essentials/redirect-and-alias.md。命名路由与嵌套路由的注意事项当命名路由带有children子路由时有一个容易踩坑的约束如果父路由有name、没有redirect且存在一个默认子路由子路径为/或空字符串那么通过名称导航到父路由时默认子路由不会被渲染。源码在 src/create-route-map.js 中对此明确给出了开发警告if ( route.name !route.redirect route.children.some(child /^\/?$/.test(child.path)) ) { warn( false, Named Route ${route.name} has a default child route. When navigating to this named route (:to{name: ${route.name}}), the default child route will not be rendered. Remove the name from this route and use the name of the default child route for named links instead. ) }正确的做法是移除父路由的name改用默认子路由的名称来编写命名链接。嵌套路由的配置方式可参考 docs/guide/essentials/nested-routes.md。另外当按名称解析到的路由命中redirect或alias配置时_createRoutesrc/create-matcher.js会分别转入重定向或别名处理流程这与按路径匹配的行为保持一致。完整示例读取$route.name仓库提供了开箱即用的完整示例 examples/named-routes/app.js其中定义了三条命名路由并展示了通过$route.name读取当前路由名称const router new VueRouter({ mode: history, base: __dirname, routes: [ { path: /, name: home, component: Home }, { path: /foo, name: foo, component: Foo }, { path: /bar/:id, name: bar, component: Bar } ] }) new Vue({ router, template: div idapp h1Named Routes/h1 pCurrent route name: {{ $route.name }}/p ul lirouter-link :to{ name: home }home/router-link/li lirouter-link :to{ name: foo }foo/router-link/li lirouter-link :to{ name: bar, params: { id: 123 }}bar/router-link/li /ul router-view classview/router-view /div }).$mount(#app)要点回顾无参数的命名路由home、foo直接写:to{ name: foo }即可带参数的命名路由bar路径为/bar/:id必须通过params传参当前激活的路由名称可通过$route.name获取——它由 src/util/route.js 的createRoute从匹配记录中继承name: location.name || (record record.name)。该示例对应配套的示例页面骨架 examples/named-routes/index.html可结合本地示例服务器examples/server.js直接运行查看效果。测试验证命名路由的行为被单元测试锁定仓库的单元测试覆盖了命名路由的关键行为可作为行为契约参考test/unit/specs/api.spec.js验证router.resolve({ name: b })能按名称解析出正确的route并能正确继承当前路由的参数resolve({ name: b }, { params: { id: 2 }, path: /a/2 })test/unit/specs/create-map.spec.js验证含名称的嵌套路由bar.baz与基于名称的重定向redirect: { name: bar-redirect.baz }的注册与解析。这些测试与nameMap的构建、match的按名查表逻辑相互印证确认了本文前述源码分析的结论。小结命名路由是 Vue Router 中「以名称代替路径」的核心抽象贯穿声明式router-link :to与编程式router.push/router.replace两种导航方式。其完整链路为路由注册时名称写入nameMap索引表src/create-route-map.js重复名称触发警告导航时normalizeLocation识别name字段src/util/location.jsmatcher.match从nameMap查到路由记录通过fillParams把params填充进路径模板src/create-matcher.js、src/util/params.js最终生成的 Route 对象携带name、path、params、query、hash等信息交由页面渲染。掌握命名路由能让你的路由链接与导航代码摆脱对路径字面量的强依赖在路径重构时只需修改路由配置表一处配合参数继承、重定向引用和动态addRoute可显著提升大型应用中路由管理的可维护性。相关的扩展阅读还包括 docs/guide/essentials/dynamic-matching.md动态路由匹配与参数和 docs/guide/advanced/lazy-loading.md按需加载路由组件。赞分享前端路由【免费下载链接】vue-router The official router for Vue 2项目地址https://gitcode.com/gh_mirrors/vu/vue-router点击查看免费下载相关推荐Vue Router 命名路由Named Routes完全指南定义、链接与底层实现Vue Router 命名路由Named Routes完全指南定义、链接与底层实现 命名路由Named Routes是 Vue Router 为每条路前端路由Vue Router 2 命名路由Named Routes完全指南配置、跳转与源码级原理Vue Router 2 命名路由Named Routes完全指南配置、跳转与源码级原理 命名路由是 Vue Router本项目为 Vue 2 官方路由前端路由Vue RouterVue 2命名路由Named Routes完整指南配置、跳转与源码级原理Vue RouterVue 2命名路由Named Routes完整指南配置、跳转与源码级原理 命名路由Named Routes 是 vue rou前端路由创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考