
tanstack/vue-router 1.167 → 1.170 演进解读匹配调度重构、错误边界与响应式订阅模型【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router导读本文基于packages/vue-router/CHANGELOG.md中记录的tanstack/vue-router从 1.167.0 到 1.170.32 的完整发布历史系统梳理这一阶段该 Vue 路由库在匹配与数据加载调度、错误/未找到not-found边界、Link 预加载、路径参数解析、SSR 与构建产物等方面的关键演进并结合本仓库packages/vue-router/src下的源码实现逐条佐证。读完本文你将理解tanstack/vue-router当前的内部状态模型signal 化的createAtom与useSelector订阅、1.170.18那次大规模调度器重构的动机与迁移要点以及如何在升级到 1.170.x 时规避破坏性变更。版本事实均以当前仓库为准packages/vue-router/package.json中记录当前版本为1.170.32engines.node要求20.19peerDependencies.vue为^3.3.0。一、版本脉络总览这一阶段的演进主线从 CHANGELOG.md 可以看到1.167.0 至 1.170.32 期间tanstack/vue-router的变更可分为四条主线响应式状态模型重构从createStore迁移到createAtom#7150随后升级 TanStack Store 至 0.11 并全面改用useSelector订阅1.170.32同时移除了pendingMatches/cachedMatches并转向 signal 化响应式1.168.0。匹配与加载调度重构1.170.18将匹配加载重写为基于 lane 的调度器统一跟踪导航、预加载与后台刷新是这一阶段最核心的破坏性变更。错误与 not-found 边界治理类型收窄到unknown、保留 falsy 抛错值、从生命周期回调中排除错误/not-found 边界的结构后代、保留已成功的 not-found 匹配作为终端共享边界。链接、预加载与体积优化preloadDelay应用于视口预加载、取消离开视口的预加载、移除非文档化的isTransitioning状态、内联isCtrlKey判断做字节压缩、清理 IntersectionObserver 选项以及持续为 Vue/Solid 链接共享规范化 pathname 比较以缩减包体积。此外还有大量Updated dependencies条目说明tanstack/vue-router绝大部分能力由底层tanstack/router-core1.167.x → 1.171.x与tanstack/history1.161.x → 1.162.x承载Vue 适配层负责把核心逻辑接入 Vue 的响应式系统。二、响应式状态模型演进createStore→createAtom→useSelector2.1 两次关键迁移1.168.13迁移createStore至createAtomAPI 更简单#7150。1.168.0移除pendingMatches与cachedMatches全面转向 signal 化响应式solid uses its own native signalsSolid 使用自身原生 signalVue 侧则依赖tanstack/vue-store的响应式原语。1.168.4 / 1.168.1跟进tanstack/store 0.9.3、0.9.2。1.170.32升级 TanStack Store 至 0.11并将路由器订阅迁移到useSelector在保留 selector 比较的同时补齐 Vue 订阅清理逻辑#7824。2.2 源码佐证Vue 适配层的 store 工厂routerStores.ts 是整个 Vue 适配层的响应式桥接点它把核心库的GetStoreConfig契约映射到tanstack/vue-storeimport { batch, createAtom } from tanstack/vue-store import type { GetStoreConfig } from tanstack/router-core import type { Readable } from tanstack/vue-store export const getStoreFactory: GetStoreConfig (_opts) { return { createMutableStore: createAtom, createReadonlyStore: createAtom, batch, } }可见可变与只读 store 均由createAtom实现即当前所有路由状态都走 atom信号原语batch用于批量提交状态保证一次导航过程中的多次状态写入只触发一次渲染。2.3 源码佐证Router 类的 Vue 扩展router.ts 中Router类继承自tanstack/router-core的RouterCore构造时传入getStoreFactory并通过declare module tanstack/router-core扩展了 Vue 特有的路由选项defaultComponent默认Outlet、defaultErrorComponent默认ErrorComponent、defaultPendingComponent、defaultNotFoundComponent默认NotFoundWrap/InnerWrap分别包裹整个路由器与路由器内部内容可用于注入全局 ContextdefaultOnCatch路由器 ErrorBoundary 捕获错误的默认处理器。这些选项在 1.170.x 中均可直接用于createRouter()的配置对象。2.4 源码佐证useMatch的订阅实现useMatch.tsx 是 Vue 侧所有“读取路由状态”钩子的地基useSearch、useParams、useLoaderData均基于它。其客户端分支使用useSelector订阅按路由 key 划分的 presentation storeif (opts.from) { // routeId case: subscribe to the stable per-route presentation atom. const matchStore router.stores.getMatchStore(opts.from) match useSelector(matchStore) } else { // Nearest-match case: use the routeId from context for stable lookup. const nearestRouteId Vue.inject(routeIdContext) if (nearestRouteId) { match useSelector(router.stores.getMatchStore(nearestRouteId)) } else { match Vue.ref(undefined) } }随后用Vue.computed包装opts.select并保留lastOwnedMatch在 match 暂时消失时回退配合 1.170.27 的 “preserve pending UI across retained routes” 与 1.170.25 的 “Keep active route components mounted by default when route params change”。shouldThrow默认true找不到 match 时抛出 invariant设为false时返回undefined——这正是 1.170.30 中“route-scopeduseMatch/useSearch/useParams转发shouldThrow选项并保留可选返回类型”#8169的落地位置。useSearch.tsx、useParams.tsx、useLoaderData.tsx 均以useMatchselect组合实现例如useSearch内部return useMatch({ from: opts.from!, strict: opts.strict, shouldThrow: opts.shouldThrow, select: (match) (opts.select ? opts.select(match.search) : match.search), })三、核心重构基于 lane 的匹配加载调度器1.170.183.1 动机与改动1.170.18#7805是这一阶段最具分量的内部重构将匹配加载重写为lane-based scheduler每次导航、预加载、后台刷新都被视为一个有序工作单元。它解决的问题包括重叠导航之间 pending/redirect/retry 状态互相泄漏SSR 对 redirect、error、not-found 响应的状态码不正确客户端在服务端已完成工作后仍重复执行hydration 间隙。配套行为变化失效invalidation会退役匹配中的活跃预加载使旧的推测性 loader 结果在失效后无法变成新鲜缓存数据路由headers()只在服务端执行不再在客户端资产投影asset projection期间调用与既有文档行为一致文档化的默认gcTime与preloadGcTime与运行时默认一致即 5 分钟300_000。3.2 移除/变更的导出内部 API升级重点该版本移除或调整了大量内部导出升级到 1.170.18 的开发者需要对照迁移原 API替代方案RouterState.loadedAtmatch.updatedAtRouterState.isTransitioning订阅router.state.status/router.state.isLoadingRouterState.statusCode、RouterState.redirect服务端 loader 内部处理不再暴露在router.stateRouteMatch.fetchCount已移除无替代纯信息性RouteMatch.status redirected被重定向的 match 直接从 match 列表中移除不再渲染RouteMatch.globalNotFound私有化_notFound改用match.status notFoundReact/Solid/VueMatch组件的matchIdproprouteIdpropRouterStores的matchesId/matchStores/getRouteMatchStore()ids/byRoute/getMatchStore()route-keyed presentation storeRouterCore.getMatch()/updateMatch()/cancelMatch()/cancelMatches()从router.state.matches读取如router.state.matches.find((m) m.id id)不再支持从外部变更/取消单个进行中的 matchRouterCore.hasNotFoundMatch()router.state.matches.some((m) m.status notFound)RouterCore.looseRoutesByIdroutesByIdRouterCore.isPrerendering()、isViewTransitionTypesSupported、viewTransitionPromise移除无替代RouterCore.getParsedLocationHref()、clearExpiredCache()移除过期缓存条目在 match 提交时自动对账RouterCore.latestLoadPromise、beforeLoad()移除无替代commitLocationPromise、pendingBuiltLocation内部字段_commitPromise、_pendingLocationGetMatchFn、UpdateMatchFn移除tanstack/router-core独立导出的getMatchedRoutes()改用实例方法router.getMatchedRoutes()RouterCore.loadRouteChunk()第二参数数组第二参数改为errorComponent、notFoundComponent或false单参数用法不变Redirect.redirectHandled移除内部 redirect 记账MatchRoutesOpts.preload、MatchRoutesOpts.dest移除StartTransitionFn(fn, expected) Promiseboolean原为(fn) void仅影响自定义框架适配器3.3 1.170.18 之后的调度细节修复1.170.29按需构建客户端预加载 location移除框架链接使用的预构建 location 参数#8132重载时保留 context#8130。1.170.30从路由生命周期回调中排除错误/not-found 边界之下的结构后代并在失效、hydration、后台刷新、被取代的导航发布过程中保留生命周期成员资格#8165保留客户端导航期间已成功的 not-found 匹配作为终端共享边界在目标加载时保留路由 context#8161。1.168.11修复被重定向的 pending 路由过渡使懒加载目标路由可完成加载而不被过期的重定向 match 引发渲染错误#7137。四、错误与 not-found 边界处理这一阶段对错误边界做了多轮打磨1.170.30#8209React 与 Vue 错误边界保留 falsy 抛出的值如null、undefined、0、falseReact/Vue 的边界 error 组件与onCatch回调类型收窄为unknown。升级后应先做窄化如error instanceof Error再读取message/stackErrorComponentPropsTError仍可用于已收窄类型的值路由onError类型不变。SSR 侧则把非Error的 loader 错误包装为Error以匹配 Solid 原生边界行为原始值保留在cause中。1.168.9#7077保留组件抛出的notFound()错误穿过框架错误边界使路由notFoundComponent无需显式routeId即可渲染。1.170.24#8045当Outlet被渲染在 pending、error 或 not-found 组件内部时给出警告——这是对错误/not-found 边界语义的显式约束。五、Link 组件、预加载与体积优化5.1 预加载行为细化1.170.24#8044将preloadDelay应用于视口链接预加载并在链接离开视口时取消进行中的预加载。1.170.20#7971清理 Link 组件中的 IntersectionObserver 选项。link.tsx 中可以看到 Vue 版 Link 的完整实现骨架useLinkProps在 SSR 下只渲染一次、不建立 store 订阅与 observer客户端则用useIntersectionObserver见 utils.ts触发预加载并通过timeoutMapWeakMap管理预加载超时。Link 事件处理器同时兼容 Vue 原生小写事件onMouseenter等与 camelCase 版本并支持asChild与disabled。5.2 属性与体积清理1.170.24#8043移除未文档化的 LinkisTransitioning状态与data-transitioning属性。1.168.10#7138修复Link将内部路由属性preloadIntentProximity、from、unsafeRelative等泄漏到渲染出的 DOM 元素的问题React/Solid/Vue 三端同步修复。1.170.26#8073在 Link 组件内联isCtrlKey判断以缩减字节。1.170.31#8308共享规范化 pathname 比较减少 Vue/Solid 链接包体积同时让 Vue 链接在目标变为内部地址时刷新链接状态refresh Vue link state when destinations become internal。1.168.2#7007用自研实现替换tiny-invariant与tiny-warning以缩减包体积。1.168.14#7152缩短内部不可压缩的 store 名称进一步做字节压缩。5.3 导航与 URL 校验1.170.31#8308校验导航与重定向目标将模糊相对 URL 保持在当前 origin约束 prerender 请求与输出路径避免重定向 header 出现在序列化的 server function 响应体中保留原生表单 HTTP 重定向、文档重定向的路由错误处理与 mask、共享 loader 重定向的按导航目标显式重定向Locationheader 优先于路由选项检查复用协议相对 URL 检查解析重定向 scheme。1.170.30#8251链接、导航、重定向与构建配置中的绝对 URL 检查改用URL.canParse并为旧浏览器保留URL构造器回退。六、路径参数解析与 search 中间件6.1params.parse的两处增强1.169.0#7263允许params.parse实验性地返回false以在路径匹配期间跳过某个候选路由抛出的解析错误仍浮出到选中的 match 上不会静默落入其他路由类型化模板链接的出站 URL 生成仍走“精确路由查找 params.stringify”。1.170.20#7967匹配路由时保留路径参数的原始字符串形式使params.parse产生的结构化值能够生成稳定的 match ID避免复用过期 loader 数据同时RouterCore.getMatchedRoutes()的返回从对象改为元组[matchedRoutes, rawParams, foundRoute]。6.2 search 中间件组合修复1.170.13#7555 的导出清单。七、SSR 与构建产物演进1.170.9#7497修复流式渲染streaming问题。1.168.20#7253为 TanStack Start 增加内联 CSS manifest 的 SSR 支持路由样式可嵌入 HTML 响应并在 hydration 时不产生重复 stylesheet 链接。1.170.8#7477支持 Rsbuild 客户端输出格式——默认 module 输出经典 script 环境用 IIFE客户端入口脚本与预加载表示为根路由 manifest 资产脚本预加载跟随 manifest 脚本格式跨域配置使用script键transformAssets脚本回调上下文仅暴露{ kind: script, url }。1.168.3#7023新增transformAssets能力。1.168.5#7042修复滚动恢复——不再节流。1.170.6#7447修复 hash 导航被过期的滚动恢复条目覆盖的问题。1.168.18#7167修复路由文件转换对 route ID 引号、更多导出Route模式的支持并避免边缘情况下的错误 import 重写对路由调用检测、import 移除安全性、引号保留、构造器替换与不支持的 route 定义补充了测试覆盖。SSR 相关源码可继续参考 src/ssr/renderRouterToString、renderRouterToStream、defaultRenderHandler、defaultStreamHandler等Vue 端 SSR 入口通过tanstack/vue-router/ssr/server与/ssr/client子路径导出见 package.json。八、类型系统与 DX 改进1.168.12#7139修复MatchRoute子回调参数推断——从目标to路由解析 params而非路由路径 keyReact/Solid/Vue 三端同步修复。1.167.4#6866 目录。1.167.0#6921新增staleReloadMode。1.170.0#7395干净的 minor 版本重新出发fresh start。1.170.20#7970createFileRoute不再依赖FileRoute类。构建链方面1.167.1修复使用tanstack/vite-config 0.4.3构建#69231.167.2升级到 vite-config 5.xrolldown#6926。九、升级建议与注意事项版本与依赖当前tanstack/vue-router1.170.32依赖tanstack/router-core1.171.x、tanstack/history1.162.x、tanstack/vue-store^0.11.0、vue/runtime-dom^3.5.25与isbot要求 Node20.19、Vue^3.3.0。1.170.18 是分水岭若你曾使用RouterState.loadedAt、isTransitioning、RouteMatch.fetchCount、router.beforeLoad()、hasNotFoundMatch()、looseRoutesById或独立导出的getMatchedRoutes()请按上文表格迁移Match组件改用routeIdprop从router.state.matches读取 match 列表。错误边界类型升级 React/Vue 适配层后读取边界 error 的message/stack前先做instanceof Error收窄注意 falsy 抛错值null/undefined/0/false现在会被保留。预加载与链接preloadDelay现在同样作用于视口预加载离开视口的链接预加载会被取消data-transitioning属性已移除勿在样式/测试中依赖。SSR 行为路由headers()仅在服务端执行服务器响应状态码与 redirect 处理已移入服务端 loader 内部不再暴露在router.state。十、进一步阅读版本记录全文packages/vue-router/CHANGELOG.md包配置与入口packages/vue-router/package.json、src/index.tsx状态桥接与订阅src/routerStores.ts、src/useMatch.tsx、src/useRouterState.tsx路由实例与选项src/router.ts、src/route.ts链接与导航src/link.tsx、src/useNavigate.tsxSSR 实现src/ssr/测试与工具链tests/、vitest.config.ts底层核心匹配/加载/错误边界逻辑本体packages/router-core/src、packages/history/src【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考