- 前端
- 路由
【免费下载链接】vue-router
🚦 The official router for Vue 2
导读
new VueRouter(options)是 Vue 2 应用接入路由的唯一入口,其构造选项直接决定了路由表如何构建、URL 以何种形态呈现、导航失败时如何降级、以及滚动位置如何恢复。本文以 vue-router 官方文档《Opciones del constructor de Router》(Router 构造选项)为核心骨架,逐项拆解routes、mode、base、linkActiveClass、linkExactActiveClass、scrollBehavior、parseQuery/stringifyQuery、fallback等全部构造选项的类型、默认值与使用场景,并结合本仓库的源码实现(src/router.js、src/history/base.js、src/util/query.js、src/util/scroll.js 等)讲解每个选项背后的真实执行逻辑。读完后,你将能够精确配置一个符合项目部署形态的 vue-router 实例,并理解各选项在运行时如何影响路由匹配、URL 生成与导航行为。
一、routes:路由配置表的唯一入口
类型:Array<RouteConfig>
routes是构造选项中唯一一个"数据性质"的选项,它声明了应用的全部路由。vue-router 在构造阶段会立即把它交给createMatcher处理(见 src/router.js),从而生成用于路径匹配的pathList、pathMap与nameMap三张表(见 src/create-route-map.js)。
官方文档给出的RouteConfig类型声明如下(字段注释已补充):
declare type RouteConfig = { path: string; component?: Component; name?: string; // 命名路由 components?: { [name: string]: Component }; // 命名视图 redirect?: string | Location | Function; props?: boolean | string | Function; alias?: string | Array<string>; children?: Array<RouteConfig>; // 嵌套路由 beforeEnter?: (to: Route, from: Route, next: Function) => void; meta?: any; // 2.6.0+ 新增 caseSensitive?: boolean; // 是否大小写敏感匹配(默认: false) pathToRegexpOptions?: Object; // 传给 path-to-regexp 的编译选项 }path是唯一必填字段。在开发环境下,如果缺少path,addRouteRecord会直接抛出断言错误"path" is required in a route configuration.(见 src/create-route-map.js);同时开发模式还会警告非嵌套路由必须以/开头、路径不应包含未编码字符、同一路径下不应出现重复的命名路由。component不能是字符串组件 id,必须是实际组件对象;components用于命名视图场景,源码在构建RouteRecord时会做归一化:components: route.components || { default: route.component }(见 src/create-route-map.js)。children会以父路由的path为前缀递归展开子路由记录,并在开发环境下对"带 name 且有默认子路由"的配置给出告警(对应 GH Issue #629)。alias支持字符串或数组,源码会为每个别名生成一条独立的匹配记录,并共享原路径的组件与children(见 src/create-route-map.js)。redirect在导航匹配阶段被捕获处理;beforeEnter作为"配置内进入守卫"进入导航守卫队列(见 src/history/base.js)。
caseSensitive 与 pathToRegexpOptions:如何影响匹配
这两个选项在 2.6.0+ 引入,直接作用于 vue-router 依赖的path-to-regexp正则编译环节。源码中的处理非常直观(见 src/create-route-map.js):
const pathToRegexpOptions: PathToRegexpOptions = route.pathToRegexpOptions || {} const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict) if (typeof route.caseSensitive === 'boolean') { pathToRegexpOptions.sensitive = route.caseSensitive }也就是说:
caseSensitive: true会被透传为path-to-regexp的sensitive选项,使/foo不再匹配/Foo;pathToRegexpOptions.strict影响路径归一化:非 strict(默认)时normalizePath会先去除末尾的/(见 src/create-route-map.js);- 其余
pathToRegexpOptions字段(如end、delimiter等)会原样传入Regexp(path, [], pathToRegexpOptions)参与正则编译(见 src/create-route-map.js)。
完整的类型声明可参考 types/router.d.ts 与 flow/declarations.js。
二、mode:三种路由模式的抉择
类型:string默认值:"hash"(浏览器中)|"abstract"(Node.js 中)可选值:"hash" | "history" | "abstract"
mode决定路由以何种方式驱动 URL 变化:
hash:使用 URL 中的#(hash)进行路由。它在所有 Vue 支持的浏览器中都能工作,包括不支持 HTML5 History API 的旧浏览器。hash 变化不会触发页面刷新,服务端无需任何额外配置。history:使用 HTML5 History API。URL 形态更美观(无#),但必须配合服务端配置(将所有路径回退到应用入口),否则直接访问深层链接会 404。详见官方文档 Modo historial HTML5。abstract:在任何 JavaScript 环境都能工作,例如 Node.js 服务端渲染场景。当检测不到浏览器 API 时,路由会被强制切换为abstract模式。
源码视角:mode 的真实决策链
构造函数的决策逻辑非常清晰(见 src/router.js):
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 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: // 非生产环境断言:invalid mode }几个容易被忽略的细节:
mode缺省时取'hash',但如果你在 Node.js 环境下构造路由,inBrowser为假,最终this.mode仍会是'abstract'——这就是文档中"自动强制"的含义。- 三种模式分别对应 src/history/hash.js、src/history/html5.js、src/history/abstract.js 三个历史实现类,它们共享 src/history/base.js 中的
History基类(导航队列、守卫执行、错误处理都在基类中完成)。 - hash 模式下,如果浏览器支持
pushState,pushHash/replaceHash内部其实走的是pushState(见 src/history/hash.js),监听的事件也相应从hashchange升级为popstate(见 src/history/hash.js)。
三、base:应用的基础路径
类型:string默认值:"/"
base声明整个单页应用被部署在哪个 URL 前缀之下。例如应用整体位于/app/下,则base应设为"/app/",此时所有路由解析出来的链接都会带上该前缀。
源码视角:base 的归一化处理
base最终在History基类构造函数中被normalizeBase归一化(见 src/history/base.js),处理规则如下:
- 未提供
base时:在浏览器中会读取页面<base>标签的href属性作为兜底(并剥离协议与域名部分),否则取"/";在 Node.js 环境直接取"/"。 - 确保以
/开头(缺失时自动补上)。 - 去除末尾的
/(如"/app/"会被归一化为"/app")。
此外,router.resolve()在生成href时会组合base与fullPath:hash 模式拼成base + '/#' + fullPath,history 模式拼成base + '/' + fullPath,再经cleanPath清理(见 src/router.js)。这也是文档中"整个应用位于 /app/ 下时 base 应设为 /app/"的底层由来。
四、linkActiveClass 与 linkExactActiveClass:全局活动链接类名
类型:string默认值:linkActiveClass为"router-link-active",linkExactActiveClass为"router-link-exact-active"
这两个选项用于全局配置<router-link>渲染出的链接在"处于活动状态"与"精确匹配状态"时的 CSS 类名,属于<router-link>组件active-class与exact-active-class属性在构造阶段的全局默认值。相关选项的类型声明见 types/router.d.ts,组件侧的完整用法见 router-link 文档。
需要理解两者的匹配语义差异:
linkActiveClass对应包含匹配(inclusive match):只要当前路径以目标路径开头(或等于目标路径),链接即为 active。典型副作用是<router-link to="/">在几乎所有路由下都处于 active 状态。linkExactActiveClass对应精确匹配(exact match):仅当路径完全一致时才激活。若需要"仅在首页激活",应依赖精确匹配语义。
在类型定义中还提到了该族的扩展选项linkExactPathActiveClass(默认"router-link-exact-path-active"),它只比较 URL 的path部分、忽略query与hash,这属于 Vue Router 3.5.0 之后的能力,本仓库对应文档见 docs/api/README.md。
五、scrollBehavior:自定义滚动行为
类型:Function
当浏览器支持history.pushState时,该选项允许你在路由切换后控制页面的滚动位置。官方签名如下:
( to: Route, from: Route, savedPosition?: { x: number, y: number } ) => { x: number, y: number } | { selector: string } | ?{}返回值可以是{ x, y }坐标、{ selector }选择器(滚动到指定元素),或返回空值/undefined以保持当前滚动位置;在 Vue Router 3 的类型声明中还支持返回 Promise(见 types/router.d.ts)。完整实战讲解见 comportamiento del scroll。
源码视角:handleScroll 的执行细节
滚动逻辑集中在 src/util/scroll.js 的handleScroll中,关键行为如下:
- 延迟执行:滚动发生在
router.app.$nextTick回调中,确保 DOM 完成重渲染后再滚动,避免滚动到错误位置。 savedPosition的来源:浏览器前进/后退(pop 导航)时,savedPosition是之前通过saveScrollPosition记录在positionStore中的坐标(见 src/util/scroll.js);普通导航则传入null。这就是"返回列表页时恢复原滚动位置"类需求的标准做法。- 支持 Promise:若
scrollBehavior返回一个 thenable,会等待其 resolve 后再执行scrollToPosition,期间异常会被断言输出(见 src/util/scroll.js)。 - selector 与 offset:返回
{ selector, offset }时,会通过document.querySelector(选择器以#数字开头时改用getElementById)计算元素位置并扣除 offset(见 src/util/scroll.js)。 - 平滑滚动:若浏览器支持
scrollBehaviorCSS 属性,会透传shouldScroll.behavior调用window.scrollTo({ behavior })(见 src/util/scroll.js)。
六、parseQuery / stringifyQuery:自定义查询串解析
类型:Function引入版本:2.4.0+
默认情况下,vue-router 使用内置的parseQuery与stringifyQuery处理 URL 查询串。这两个构造选项允许你提供自定义实现来覆盖默认行为——典型场景是项目需要特殊的参数编码规则或非标准的分隔符。
源码视角:默认实现与覆盖机制
默认解析逻辑在 src/util/query.js:按&拆分、+转空格、=分隔键值,重复键自动聚合为数组,并做严格的 RFC3986 兼容编码(额外转义[!'()*]、保留逗号,见 src/util/query.js)。
覆盖机制在resolveQuery中(见 src/util/query.js):
const parse = _parseQuery || parseQuery try { parsedQuery = parse(query || '') } catch (e) { // 非生产环境输出告警,回退为空对象 }自定义解析函数抛错时,会回退为空对象并给出告警;自定义 stringify 函数的约定是不要输出前导?(类型注释中明确说明,见 types/router.d.ts),因为默认stringifyQuery返回的字符串以?开头(见 src/util/query.js)。仓库中还提供了custom-query的单元测试用例 test/unit/specs/custom-query.spec.js 可供参考。
七、fallback:不支持 History API 时的降级开关
类型:boolean默认值:true引入版本:2.6.0+
fallback控制当mode设为"history"但浏览器不支持history.pushState(典型如 IE9)时,路由是否自动降级为hash模式。默认true表示自动降级,应用无需任何改动即可在旧浏览器工作。
将fallback设为false后,行为截然不同:在 IE9 中每一次通过router-link的导航都会变成整页刷新。这看似"退步",却是服务端渲染(SSR)场景的刻意设计——因为 hash 形态的 URL 无法与 SSR 配合(服务端拿不到 hash 中的路由信息),此时宁可牺牲 SPA 体验也要保证 URL 形态正确。
源码视角:fallback 如何进入决策链
回到 src/router.js,fallback 的实际判定条件比文档描述更精确:
let mode = options.mode || 'hash' this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false if (this.fallback) { mode = 'hash' }即:只有显式指定了mode: 'history'、浏览器不支持 pushState、且未把fallback显式设为false时,才会触发降级。降级后的HashHistory还会额外接收this.fallback参数,用于在构造时执行深链接的checkFallback重定向(见 src/history/hash.js 与 src/history/hash.js):若当前 URL 不是/#形态,会先把base拼入路径并执行window.location.replace,把旧地址规范化为 hash 形态。
八、构造选项速查表与组合实践
| 选项 | 类型 | 默认值 | 核心作用 | 关键源码位置 |
|---|---|---|---|---|
routes | Array<RouteConfig> | — | 声明路由表,构建匹配索引 | src/create-route-map.js |
mode | string | "hash"/"abstract" | 选择 hash / history / abstract 模式 | src/router.js |
base | string | "/" | 设置应用基础路径 | src/history/base.js |
linkActiveClass | string | "router-link-active" | 全局包含匹配活动类名 | types/router.d.ts |
linkExactActiveClass | string | "router-link-exact-active" | 全局精确匹配活动类名 | types/router.d.ts |
scrollBehavior | Function | — | 导航后自定义滚动位置 | src/util/scroll.js |
parseQuery | Function | 内置解析器 | 覆盖查询串解析 | src/util/query.js |
stringifyQuery | Function | 内置序列化器 | 覆盖查询串序列化 | src/util/query.js |
fallback | boolean | true | history 不可用时降级为 hash | src/router.js |
综合实践建议:
- 纯前端部署、无需 SEO:使用默认
hash模式即可,无需服务端配置; - 需要干净 URL 或 SEO:使用
mode: 'history'+ 配置base(如部署在/app/下),同时务必在服务端做好 history 回退,详见 Modo historial HTML5; - SSR / 测试环境:依赖
abstract模式的自动强制,无需显式配置,但也可显式声明以表明意图; - 需要滚动恢复:配置
scrollBehavior(to, from, savedPosition),返回savedPosition以恢复前进/后退时的位置,返回{ selector }滚动到锚点元素; - 自定义查询参数编码:通过
parseQuery/stringifyQuery成对覆盖,注意自定义 stringify 不要输出前导?。
以上所有选项的完整类型声明均可直接查阅 types/router.d.ts,运行时可观察的实例属性(如router.mode、router.currentRoute)与方法的进一步说明,可参考 API 参考文档。
- 前端
- 路由
【免费下载链接】vue-router
🚦 The official router for Vue 2
相关推荐
Vue Router 嵌套路由(Nested Routes)完全指南:children 配置、绝对路径与 router-view 渲染层级
Vue Router 嵌套路由(Nested Routes)完全指南:children 配置、绝对路径与 router view 渲染层级 嵌套路由是 vue
前端路由OneUptime DNSSEC 监控实战:从配置选项到 dig 底层实现的完整指南
OneUptime DNSSEC 监控实战:从配置选项到 dig 底层实现的完整指南 DNSSEC(Domain Name System Security Ex
可观测性后端运维前端云原生微服务AI AgentX6 画布(Graph)配置完全指南:从构造选项到源码级实现解析
X6 画布(Graph)配置完全指南:从构造选项到源码级实现解析 本篇指南以 X6 官方 API 文档 site/docs/api/graph/graph.zh
前端图形学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考