资讯动态

Vue Router 2 的 `<router-view>` 组件:渲染机制、命名视图与 keep-alive 深度解析

发布时间:2026/9/21 15:04:35 来源:尧图企业网站定制
Vue Router 2 的router-view组件渲染机制、命名视图与 keep-alive 深度解析【免费下载链接】vue-router The official router for Vue 2项目地址: https://gitcode.com/gh_mirrors/vu/vue-routerrouter-view是 Vue Router 2 中负责把当前匹配到的路由组件渲染到页面的核心出口组件。本文以 docs-gitbook/fr/api/router-view.md 文档为主线结合本仓库src/components/view.js的实现源码、命名视图示例与官方 API 文档docs/api/README.md系统讲解它的功能定位、name属性、与transition/keep-alive的配合方式以及它在嵌套路由和命名视图场景下的底层渲染原理。读完本文你将能准确理解并熟练运用router-view完成单出口、多命名视图、嵌套布局与缓存动画等实战需求。一、router-view是什么功能定位与渲染职责按照官方文档的定义router-view是一个函数式组件functional component它负责渲染与当前路径path相匹配的那个组件。这句话可以拆解为两层含义它本身不产生任何真实 DOM只是一个渲染出口把匹配到的组件实例挂载到自己的位置它渲染的目标组件由**当前激活的路由记录matched route record**决定而不是由使用者显式指定。更重要的是文档强调了一个关键特性在router-view中渲染出来的组件其内部还可以再包含自己的router-view用来渲染嵌套路径nested paths对应的子组件。这正是嵌套路由能够逐层展开的根本原因父级路由组件里的router-view会继续渲染匹配到的子路由组件层层递进最终构成完整的页面嵌套结构。也就是说一个页面上可以同时存在多个处于不同层级的router-view它们分别对应路由树中的不同深度。在源码层面这一行为由 src/components/view.js 实现。该文件导出的组件定义如下export default { name: RouterView, functional: true, props: { name: { type: String, default: default } }, // ... }可以看到它在注册时就明确声明了functional: true与文档中函数式组件的描述完全一致。二、name属性默认视图与命名视图router-view只接收一个属性name类型string默认值default当一个router-view设置了name它就会渲染当前匹配到的路由记录route record中components选项注意带复数 s里对应名称的那个组件。而一个不带 name 的router-view默认名字就是default它渲染components.default或单数component选项中定义的组件。命名视图的典型应用是同一路径下同时渲染多个平级视图。官方示例docs-gitbook/fr/essentials/named-views.md给出了模板写法router-view classview one/router-view router-view classview two namea/router-view router-view classview three nameb/router-view对应的路由配置使用复数形式的components选项为每个名字分别指定组件const router new VueRouter({ routes: [ { path: /, components: { default: Foo, a: Bar, b: Baz } } ] })本仓库的 examples/named-views/app.js 完整演示了这一用法并且进一步展示了同一路由可以定义多组命名组件不同路径复用不同组合const router new VueRouter({ mode: history, base: __dirname, routes: [ { path: /, components: { default: Foo, a: Bar, b: Baz } }, { path: /other, components: { default: Baz, a: Bar, b: Foo } } ] })在源码 src/components/view.js 的第 58-59 行命名视图的匹配逻辑非常直观const matched route.matched[depth] const component matched matched.components[name]即先从当前$route的matched数组里按深度取出对应的路由记录再从该记录的components对象中按name取值。如果该深度没有匹配记录或没有对应名称的组件则渲染空节点if (!matched || !component) { cache[name] null return h() }三、深度depth计算嵌套路由如何逐层渲染router-view之所以能正确处理嵌套路由关键在于它会在渲染前从父组件链向上遍历计算自己处于第几层嵌套。源码中的核心逻辑src/components/view.js 第 27-39 行let depth 0 let inactive false while (parent parent._routerRoot ! parent) { const vnodeData parent.$vnode ? parent.$vnode.data : {} if (vnodeData.routerView) { depth } if (vnodeData.keepAlive parent._directInactive parent._inactive) { inactive true } parent parent.$parent } data.routerViewDepth depth这段代码的含义是从当前组件的父级开始向上回溯凡是遇到vnodeData.routerView为真的祖先即上层还有一个router-view深度就加一第 16 行渲染时会给当前 vnode 打上data.routerView true标记这正是上层遍历时用来识别这里曾经有一个 router-view的依据遍历过程中同时检测keepAlive与_inactive状态判断整棵组件树是否处于被 keep-alive 缓存且已失活的状态。最终得到的depth会被用作route.matched[depth]的下标。而matched数组正是由 src/util/route.js 的formatMatch函数生成的它从叶子路由记录一路向父级unshift得到从根到叶的有序记录列表function formatMatch (record: ?RouteRecord): ArrayRouteRecord { const res [] while (record) { res.unshift(record) record record.parent } return res }因此第一层router-view取matched[0]根路由第二层取matched[1]子路由以此类推——这正是嵌套路由外层父组件 内层子组件逐层渲染的底层机制。四、属性传递非 name 属性与路由数据官方文档指出除name以外的其他属性都会被原样传递给被渲染的组件不过在大多数情况下按路由变化的数据都包含在路由的params参数中。也就是说router-view上的其余 attribute 会被透传下去。例如router-view classview one/router-view其中的classview one最终会落到被渲染组件的最外层元素上在示例中它被用来定位视图的布局位置。而真正的业务数据Vue Router 更推荐通过路由参数来承载动态路径的params如/user/:id中的id、query、hash等都会体现在$route对象上被渲染的组件可以通过this.$route读取。路由对象由 src/util/route.js 的createRoute统一构造包含name、meta、path、hash、query、params、fullPath、matched等字段并整体Object.freeze冻结以保证不可变。此外如果路由记录配置了props选项object、function 或 boolean 形式router-view还会把解析出的 props 一并传给被渲染组件详见 src/components/view.js 第 105-133 行的fillPropsinData其中会把组件已声明为props的字段放入data.props其余字段则降级为attrs确保属性不会丢失也不会污染组件的 props 声明。五、与transition、keep-alive配合正确的嵌套顺序因为router-view本质上只是一个普通组件它天然可以与 Vue 的transition过渡动画和keep-alive组件缓存协同工作。文档特别强调了一个重要的嵌套顺序当两者同时使用时必须把keep-alive放在transition里面transition keep-alive router-view/router-view /keep-alive /transition这样组合的原因在于transition负责在视图切换时播放进出场动画而keep-alive负责在切换过程中缓存被替换掉的组件实例避免重新渲染和状态丢失。二者职责不同顺序不能颠倒。本仓库在 examples/transitions/app.js 和 examples/keepalive-view/app.js 中分别提供了过渡动画与 keep-alive 缓存的实际用法。从源码角度看keep-alive 缓存还与router-view内部的状态管理深度耦合。在 src/components/view.js 中可以看到每个视图有一个parent._routerViewCache缓存对象按name保存渲染过的组件当组件树因 keep-alive 而处于失活inactive状态时第 42-56 行router-view会直接从缓存中取出上一次渲染的组件继续渲染而不是渲染新的路由组件缓存同时保存了当时的route与configProps以便恢复渲染时仍能正确传参对应注释中提到的 issue #2301 的 props 传递修复。此外router-view还在 vnode 上挂载了registerRouteInstance、prepatch、init等钩子第 72-103 行用于维护matched.instances中各组件的实例注册。这在 keep-alive 组件被重新激活activated或同一组件实例被跨路由复用时能保证$route相关逻辑与handleRouteEntered见 src/util/route.js的进入回调仍然正确触发。六、小结router-view是 Vue Router 2 体系中连接路由状态与组件渲染的枢纽要点说明组件类型函数式组件functional本身不产生 DOM核心职责渲染与当前路径匹配的路由组件name属性string类型默认default对应components选项中的同名键嵌套渲染通过向上遍历父链计算深度按route.matched[depth]逐层取记录属性传递非 name 属性透传业务数据通常走$route的params/query配合使用可与transition、keep-alive组合且keep-alive必须放在transition内部理解这些机制后你便可以在单出口页面、多命名视图布局、嵌套路由页面以及需要缓存与动画的复杂场景中准确、高效地组织你的视图出口。进一步深入可阅读官方英文 API 文档docs/api/README.mdrouter-view一节组件实现源码src/components/view.js命名视图指南docs-gitbook/fr/essentials/named-views.md命名视图示例examples/named-views/app.js路由记录构造逻辑src/util/route.js【免费下载链接】vue-router The official router for Vue 2项目地址: https://gitcode.com/gh_mirrors/vu/vue-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价