vue-router 命名视图(Named Views)完全指南:同一路由渲染多个组件的布局方案
2026/9/21 1:37:26 网站建设 项目流程

vue-router 命名视图(Named Views)完全指南:同一路由渲染多个组件的布局方案

【免费下载链接】vue-router🚦 The official router for Vue 2项目地址: https://gitcode.com/gh_mirrors/vu/vue-router

导读

在 Vue 2 单页应用中,vue-router默认只提供一个视图出口(<router-view>),一条路由对应渲染一个组件。但在真实项目中,我们常常需要"同屏多视图"的布局——例如一个同时包含主内容区(main)与侧边栏(sidebar)的页面。命名视图(Named Views)正是为此设计的:它允许在一条路由下同时渲染多个组件到多个具名出口中。本文基于本仓库官方文档与源码,系统讲解命名视图的定义方式、底层实现原理、嵌套命名视图的进阶布局,以及如何用仓库内示例与端到端测试验证效果。

什么是命名视图:从"嵌套"到"平铺"的布局思路

什么时候需要命名视图

常规做法是把视图一层层嵌套:父路由渲染父组件,父组件内部再放一个<router-view>渲染子路由组件(可参考仓库文档 嵌套路由)。但有些布局无法用嵌套表达——例如"一个页面同时显示主内容 + 侧边栏 + 底部栏",这几个区域是平级的,而不是父子包含关系。

此时就轮到命名视图登场。正如仓库官方文档(docs-gitbook/de/essentials/named-views.md)所述:与其只有一个视图出口,不如提供多个,并分别给它们命名。未命名的<router-view>会被自动赋予默认名default

基本写法:三个具名出口

在模板中放置多个<router-view>,用name属性区分:

<router-view class="view one"></router-view> <router-view class="view two" name="a"></router-view> <router-view class="view three" name="b"></router-view>
  • 第一个没有name属性,等价于name="default"
  • 第二、三个分别名为ab

路由配置:必须使用components(复数)

视图由组件渲染,多个视图自然需要多个组件。因此同一条路由下配置多个组件时,必须使用components(带 s 的复数形式)选项,而不是component

const router = new VueRouter({ routes: [ { path: '/', components: { default: Foo, a: Bar, b: Baz } } ] })

components是一个对象,键对应<router-view>name,值是对应的组件。上例中:default出口渲染Fooa出口渲染Barb出口渲染Baz

底层原理:RouterView 如何按名称取组件

name属性的默认值

命名视图的核心实现位于 src/components/view.js。它是一个函数式组件(functional),其props定义如下(src/components/view.js):

export default { name: 'RouterView', functional: true, props: { name: { type: String, default: 'default' } }, ... }

从这里可以确认:不带name<router-view>的默认名称就是字符串default,与官方文档描述完全一致。

匹配过程:depth 与 matched.components[name]

RouterView的渲染逻辑(src/components/view.js)按以下步骤工作:

  1. 计算视图深度(depth):遍历父级链,统计出现了多少个带routerView标记的祖先节点,从而知道当前出口处于路由记录的哪一层(用于配合嵌套路由)。
  2. 按深度取出匹配的路由记录const matched = route.matched[depth],即当前路由在该深度上匹配到的记录。
  3. 按名称取组件const component = matched && matched.components[name]——这正是命名视图的"命根子":组件来源于路由记录(RouteRecord)上的components字典,用当前出口的名称作为键去取。
  4. 取不到就渲染空节点:如果当前没有匹配记录,或该记录上没有对应名称的组件(!matched || !component),则渲染一个空节点h(),对应的出口位置什么都不显示。

路由记录如何归一化 components

无论你写的是单数component还是复数components,最终都会在构建路由表时被归一化为字典。源码 src/create-route-map.js 中的addRouteRecord是这样处理的:

const record: RouteRecord = { path: normalizedPath, regex: compileRouteRegex(normalizedPath, pathToRegexpOptions), components: route.components || { default: route.component }, ... props: route.props == null ? {} : route.components ? route.props : { default: route.props } }

关键结论:

  • 只写component: Foo时,等价于components: { default: Foo }——所以"单视图"本质是"默认名视图"的特例;
  • 当使用components复数形式时,props也应改为字典形式(按视图名称分别传 props),源码对此做了显式区分(详见下文"命名视图与 props"一节)。

类型定义也能佐证这一点:types/router.d.ts 中定义了RouteConfigSingleView(含component)与RouteConfigMultipleViews(含components?: Dictionary<Component>),两者共同组成RouteConfig联合类型。

仓库内的完整可运行示例

官方文档提到的工作 Demo 对应的就是仓库中的 examples/named-views/app.js(页面骨架见 examples/named-views/index.html)。它比文档示例更进一步,演示了同一条路由配置的两套具名组件映射

import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } const Baz = { template: '<div>baz</div>' } const router = new VueRouter({ mode: 'history', base: __dirname, routes: [ { path: '/', // a single route can define multiple named components // which will be rendered into <router-view>s with corresponding names. components: { default: Foo, a: Bar, b: Baz } }, { path: '/other', components: { default: Baz, a: Bar, b: Foo } } ] }) new Vue({ router, template: ` <div id="app"> <h1>Named Views</h1> <ul> <li><router-link to="/">/</router-link></li> <li><router-link to="/other">/other</router-link></li> </ul> <router-view class="view one"></router-view> <router-view class="view two" name="a"></router-view> <router-view class="view three" name="b"></router-view> </div> ` }).$mount('#app')

可以这样运行验证(仓库只读,仅介绍运行方式):

yarn install yarn run dev # 浏览器访问 http://localhost:8080/named-views/

导航到/other时,同一组三个出口会切换到另一套组件映射(defaultBazbFoo),直观体现了"一条路由 ↔ 多组件"的能力。

端到端测试中的行为断言

仓库的端到端测试 test/e2e/specs/named-views.js 对上述行为做了完整断言,可作为行为规范参考:

  • 访问/named-views/时:.view.one文本为foo.view.twobar.view.threebaz
  • 点击链接跳转到/named-views/other后:.view.one变为baz.view.two仍为bar.view.three变为foo
  • 直接刷新访问/named-views/other(模拟首次进入),断言结果同样成立。

这证明命名视图的组件解析与 URL 完全联动,且能正确处理"直接访问 + 应用内跳转"两种场景。

进阶:嵌套命名视图构建复杂布局

命名视图可以与嵌套路由组合,构建类似"设置中心"的多面板布局。英文版官方文档(docs/guide/essentials/named-views.md)给出了一个经典的 Settings 示例:外层是UserSettings页面,内部同时存在普通导航组件Nav、默认出口以及一个名为helper的具名出口,不同子路由(/settings/emails/settings/profile)在默认出口渲染不同内容,同时helper出口可选择性渲染预览组件。

UserSettings 组件的模板

<!-- UserSettings.vue --> <div> <h1>User Settings</h1> <NavBar/> <router-view/> <router-view name="helper"/> </div>

其中:

  • NavBar是普通组件,与路由无关;
  • name<router-view/>渲染默认组件;
  • <router-view name="helper"/>渲染名为helper的组件——如果当前路由没有配置helper,该出口渲染为空节点(对应上文源码中!componentreturn h()的行为)。

嵌套命名视图的路由配置

{ path: '/settings', // You could also have named views at the top component: UserSettings, children: [{ path: 'emails', component: UserEmailsSubscriptions }, { path: 'profile', components: { default: UserProfile, helper: UserProfilePreview } }] }

要点:

  • 顶层/settings用单数component: UserSettings(渲染外层壳);
  • 子路由emails使用单数component,此时helper出口为空;
  • 子路由profile使用复数components,同时填充默认出口与helper出口。

结合 src/components/view.js 的深度计算逻辑可知:子路由中的<router-view>会因父级UserSettings中存在 routerView 标记而使depth递增,从而正确匹配route.matched[depth]这一层记录——这正是嵌套命名视图在源码层面的支撑机制。

命名视图与 props:字典形式的传参

使用命名视图时,如果还需要向各视图组件传 props,props同样要用字典形式(每个名称一个配置),与components一一对应。这一点由类型定义明确约束(types/router.d.ts):

interface RouteConfigMultipleViews extends _RouteConfigBase { components?: Dictionary<Component> props?: Dictionary<boolean | Object | RoutePropsFunction> }

底层解析位于 src/components/view.js:渲染时会取matched.props && matched.props[name],即当前视图名称对应的 props 配置,随后通过fillPropsinData将解析出的 props 传给组件;未被组件声明为 props 的字段会被降级为普通 attrs。resolveProps支持三种形态(src/components/view.js):

  • object:直接作为 props 传入;
  • function:接收route参数、返回 props 对象(常用于动态传参);
  • booleantrue时把route.params作为 props 传入。

常见误区与注意事项

  1. 单复数混淆:多视图必须用components,用了单数component则只有默认出口生效。源码route.components || { default: route.component }也解释了为何混用容易困惑——单数写法会被归一化为default键。
  2. 默认名记忆:不带name的出口叫default,配置时写default: Foo即可覆盖它,不必额外加name="default"
  3. 未配置即空白:某条路由若没给某个具名出口提供组件,该出口渲染为空节点,不会报错(源码 src/components/view.js)。这在"同一布局、部分区域按路由条件显示"的场景中非常实用。
  4. 嵌套时注意层级:子路由中要使用命名视图,需确保父组件模板里已放置对应名称的<router-view>,且通过父链上的routerView标记让深度计算正确(源码 src/components/view.js)。
  5. 命名路由与默认子路由的坑:源码 src/create-route-map.js 会警告:带name且含有默认子路由(path: '')的父路由,使用:to="{ name: ... }"导航时默认子路由不会渲染,应改用子路由自己的名字。

小结

命名视图是 vue-router 构建"同屏多区"布局的标准方案:模板侧用多个具名<router-view>声明出口,路由配置侧用components字典(键为出口名)映射组件,源码侧由RouterView按名称从route.matched[depth].components取值渲染。本文结合 src/components/view.js、src/create-route-map.js、examples/named-views/app.js 与 test/e2e/specs/named-views.js 展示了从"能用"到"懂原理"的完整链路。更多相关概念可继续阅读仓库文档中的 嵌套路由 与 命名路由。

【免费下载链接】vue-router🚦 The official router for Vue 2项目地址: https://gitcode.com/gh_mirrors/vu/vue-router

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询