1、本文接着之前文章的案例继续:
Vue系列入门教程(10)——vue-router路由之入门案例、命名路由和动态路由
Vue系列入门教程(11)——vue-router路由之编程式导航
2、实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
借助 vue-router
,使用嵌套路由配置,就可以很简单地表达这种关系。
3、案例实现:
1)在之前的项目中,App.vue
中的router-view
是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 router-view
。例如,在 User.vue
组件的模板添加一个 router-view
,修改User.vue
的template
如下:
<template> <div> <h1>欢迎 {{ $route.params.username }} 用户回来!</h1> <router-view></router-view> </div> </template>
2)在views
中新建UserProfile.vue
和UserPosts.vue
组件:
a)UserProfile.vue
代码:
<template> <div> UserProfile-用户资料 </div> </template> <script> export default { } </script> <style scoped> </style>
b)UserPosts.vue
代码:
<template> <div> UserPosts-用户文章 </div> </template> <script> export default { } </script> <style scoped> </style>
3)要在嵌套的出口中渲染组件,需要在 VueRouter
的参数中使用 children
配置,修改config.js
中name
为User
的路由如下:
{ path: '/user/:username', name:'User', component:()=>import('@/views/User.vue'), // 配置User下嵌套路由 children:[ { // 当 /user/:username/profile 匹配成功, // UserProfile 会被渲染在 User 的 <router-view> 中 path: 'profile', component:() =>import('@/views/UserProfile.vue') }, { // 当 /user/:username/posts 匹配成功, // UserPosts 会被渲染在 User 的 <router-view> 中 path: 'posts', component:() =>import('@/views/UserPosts.vue') } ] }
/
开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。4)浏览器直接访问地址http://localhost:8080/user/xiaoming/profile
:
浏览器直接访问地址http://localhost:8080/user/xiaoming/posts
:
5)你会发现,children
配置就是像 routes
配置一样的路由配置数组,所以呢,你可以嵌套多层路由。
此时,基于上面的配置,当你访问 /user/xiaoming
时,User.vue
中的router-view
出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个 空的子路由:
{ path: '/user/:username', name:'User', component:()=>import('@/views/User.vue'), // 配置User下嵌套路由 children:[ // 当 /user/:username 匹配成功, // Blog 会被渲染在 User 的 <router-view> 中 { path: '', component:()=>import('@/views/Blog.vue') } // 其他子路由 ] }
浏览器直接访问http://localhost:8080/user/xiaoming
:
1、有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar
(侧导航) 和 main
(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view
没有设置名字,那么默认为 default
。
2、修改App.vue
的template
如下:
<template> <div id="app"> <!-- 头部 --> <Header/> <!-- 中间内容区域 --> <div class="container"> <!-- 该组件会根据不同的访问路径,渲染不同的组件 --> <!-- 没有命名,默认default --> <router-view></router-view> <!-- 命名为a --> <router-view name="a"></router-view> <!-- 命名为b --> <router-view name="b"></router-view> </div> <!-- 底部 --> <Footer/> </div> </template>
3、一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。我们修改config.js
中name
为Home
的路由配置如下:
component
,而是 components
{ path:"/", name:"Home", components:{ default:()=>import('@/views/Home.vue'), a:()=>import('@/views/Blog.vue'), b:()=>import('@/views/About.vue') } }
1、我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 router-view
组件。我们以一个设置面板为例:
Nav
只是一个常规组件。UserSettings
是一个视图组件。UserEmailsSubscriptions
、UserProfile
、UserProfilePreview
是嵌套的视图组件。
2、在views中新建Nav.vue
、UserSettings.vue
、UserEmailsSubscriptions.vue
、UserProfile.vue
(已有)、UserProfilePreview.vue
1)Nav.vue
<template> <div> Nav组件 </div> </template> <script> export default { } </script> <style scoped> </style>
2)UserEmailsSubscriptions.vue
<template> <div> UserEmailsSubscriptions组件 </div> </template> <script> export default { } </script> <style scoped> </style>
3)UserProfile.vue
(已有则修改)
<template> <div> UserProfile组件 </div> </template> <script> export default { } </script> <style scoped> </style>
4)UserProfilePreview.vue
<template> <div> UserProfilePreview组件 </div> </template> <script> export default { } </script> <style scoped> </style>
5)UserSettings.vue
<template> <div> <h1>UserSettings组件</h1> <Nav/> <router-view/> <router-view name="helper"/> </div> </template> <script> // 同级目录 import Nav from './Nav.vue' export default { } </script> <style scoped> </style>
3、在configs.js
下新增路由配置如下:
{ path: '/settings', name:'Settings', //在App.vue的默认视图下 component:()=>import('@/views/UserSettings.vue'), children:[ { path:"emails", component:()=>import('@/views/UserEmailsSubscriptions.vue') }, { path:"profile", components:{ default:()=>import('@/views/UserProfile.vue'), helper:()=>import('@/views/UserProfilePreview.vue') } } ] }
4、浏览器直接访问http://localhost:8080/settings/emails
:
浏览器直接访问http://localhost:8080/settings/profile
:
1、重定向也是通过 routes
配置来完成
2、案例:从 /about
重定向到 /blog
1)修改config.js
中name
为About
的路由配置如下:
{ path:"/about", name:"About", // 重定向指定路由地址 redirect:'/blog' }
2)测试:
3、重定向的目标也可以是一个命名的路由
案例:从 /about
重定向到 /blog
1)修改config.js
中name
为About
的路由配置如下:
{ path:"/about", name:"About", // 重定向指定命名路由 redirect:{name:'Blog'} }
2)测试效果一样
4、甚至是一个方法,动态返回重定向目标
案例:从 /about
重定向到 /blog
1)修改config.js
中name
为About
的路由配置如下:
{ path:"/about", name:"About", // 使用方法重定向 redirect:to=>{ // 方法接收 目标路由 作为参数 // return 重定向的 字符串路径/路径对象 return '/blog'; // 或路径对象 // return {name:'Blog'}; } }
2)测试效果一样
1、“重定向”的意思是,当用户访问 /a
时,URL 将会被替换成 /b
,然后匹配路由为 /b
,那么“别名”又是什么呢?
/a
的别名是 /b
,意味着,当用户访问 /b
时,URL 会保持为 /b
,但是路由匹配则为 /a
,就像用户访问 /a
一样。
上面对应的路由配置为:
{ path: '/a', component: A, alias: '/b' }
2、我们修改config.js,给name
为Blog
的路由配置起个别名为/b
,修改配置如下:
{ path:"/blog", name:"Blog", component:()=>import('@/views/Blog.vue'), // 使用alias起别名 alias:'/b' }
测试浏览器访问http://localhost:8080/b
:
1、在组件中使用 $route
获取参数会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。我们可以使用 props
将组件和路由解耦,在讲组件时我们已经讲过了组件的props
属性,不过当时props
中属性的数据是从组件标签中通过属性绑定传过来,而现在我们是通过路由传过来对应的数据,我们称之为路由组件传参。
2、路由组件传参的3种方式:
- 布尔模式
- 对象模式
- 函数模式
a)布尔模式-多用于动态路由
如果路由配置中props
被设置为 true
,route.params
将会被设置为组件属性。
1)修改User.vue
组件如下:
<template> <div> <h1>欢迎 {{ username }} 用户回来!</h1> </div> </template> <script> export default { // 接受参数属性 props:["username"] } </script> <style scoped> </style>
2)修改config.js
中name
为User
的路由配置如下:
{ path: '/user/:username', name:'User', component:()=>import('@/views/User.vue'), // props被设置为true,route.params将会被设置为组件属性 props:true }
3)运行测试:
对于包含命名视图的路由,你必须分别为每个命名视图添加props
选项:
1)修改App.vue
的template
如下:
<template> <div id="app"> <!-- 头部 --> <Header/> <!-- 中间内容区域 --> <div class="container"> <!-- 该组件会根据不同的访问路径,渲染不同的组件 --> <!-- 默认 --> <router-view></router-view> <!-- 命名为a --> <router-view name="a"></router-view> </div> <!-- 底部 --> <Footer/> </div> </template>
2)修改config.js
中name
为User
的路由配置如下:
{ path: '/user/:username', name:'User', components:{ default:()=>import('@/views/User.vue'), a:()=>import('@/views/Login.vue') }, // 包含命名视图的路由,必须分别为每个命名视图添加props props:{default:true,a:true} }
3)修改Login.vue
如下:
<template> <div> 欢迎 {{username}} 登录 </div> </template> <script> export default { props:["username"] } </script> <style scoped> </style>
4)运行测试:
b)对象模式——多用于静态路由
如果路由配置 props
是一个对象,它会被按原样设置为组件属性。当 props
是静态的时候有用。
1)修改config.js
中name
为User
的路由配置如下:
{ path: '/user', name:'User', component:()=>import('@/views/User.vue'), // // 路由配置 props 是一个对象,它会被按原样设置为组件属性,即这里传的是什么,组件上面接收的就会是什么 props:{username:"小明",password:"123456"} }
2)修改User.vue
组件如下:
<template> <div> <h1>欢迎 {{ username }} 用户回来,您的密码是: {{password}}</h1> </div> </template> <script> export default { // 接受参数属性 props:["username","password"] } </script> <style scoped> </style>
c)函数模式
你可以创建一个函数返回 props
。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。
1)修改config.js
中name
为User
的路由配置如下:
{ path: '/user/:username', name:'User', component:()=>import('@/views/User.vue'), /* URL /user/小明?p=123456 会将 {username:'小明'} 和{password: '123456'} 作为属性传递给 User.vue 组件 */ props:(route)=>({ // 第1个username匹配动态路中的参数,第2个username匹配User.vue中props属性名 username:route.params.username, // password匹配User.vue中props属性名,p匹配请求地址?后参数名 password:route.query.p }) }
2)User.vue
仍然为:
<template> <div> <h1>欢迎 {{ username }} 用户回来,您的密码是: {{password}}</h1> </div> </template> <script> export default { // 接受参数属性 props:["username","password"] } </script> <style scoped> </style>