路由
1. 路由传参
1.1 环境准备
1.1.1 定义数据
我们在 src 目录创建 list.json 文件,在这文件中数据如下:
{
"data": [
{
"id": 1,
"name": "奥迪 Q7",
"price": 650000
},
{
"id": 2,
"name": "宝马 X6",
"price": 700000
},
{
"id": 3,
"name": "奔驰 S350",
"price": 1200000
}
]
}
1.1.2 定义类型
我们可以通过 json2ts 的网站来帮我们把 JSON 格式的数据转换为相应的类型。
export interface Datum {
id: number;
name: string;
price: number;
}
export interface RootObject {
data: Datum[];
}
1.1.3 定义列表组件
在 components 目录中创建 Product.vue 组件。代码如下:
<template>
<table cellspacing="0" class="table" border="1">
<thead>
<tr>
<th>编号</th>
<th>品牌</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
</table>
</template>
<script setup lang="ts">
import {ref} from 'vue'
// 引入数据
import { data } from '../list.json'
// 引入类型
import { Item, RootObject } from '../datatype'
</script>
<style scoped>
</style>
1.1.4 配置路由
在 src 目录下创建 router 目录,然后创建 index.ts 文件。
import {createRouter, createWebHashHistory, RouteRecordRaw} from "vue-router";
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: () => import('../components/Product.vue'),
},
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
1.1.5 挂载路由
在 main.ts 入口文件中挂载路由。
import { createApp } from 'vue'
import App from './App.vue'
// 引入路由配置
import router from './router'
createApp(App).use(router).mount('#app')
1.1.6 在根组件中使用路由
<template>
<router-link to="/">产品列表</router-link>
<router-view></router-view>
</template>
<script setup lang="ts">
</script>
<style>
</style>
1.2 完整列表组件
<template>
<table cellspacing="0" class="table" border="1">
<thead>
<tr>
<th>编号</th>
<th>品牌</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.price }}</td>
<td><button @click="toDetail(item)">详情</button></td>
</tr>
</tbody>
</table>
</template>
<script setup lang="ts">
import {ref} from 'vue'
// 引入数据
import { data } from '../list.json'
// 引入类型
import { Item, RootObject } from '../datatype'
// 点击事件
const toDetail = (item: Item) => {
}
</script>
<style lang="less" scoped>
.table {
width: 400px;
}
</style>
1.3 编写详情组件
在 components 目录下创建 Detail.vue 组件,用于显示产品的详情信息。
<template>
<h3>产品详情</h3>
<ul>
<li>编号:</li>
<li>名称:</li>
<li>价格:</li>
</ul>
</template>
<script setup lang="ts">
import {ref} from 'vue'
</script>
<style scoped>
</style>
1.4 配置详情组件的路由
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: () => import('../components/Product.vue'),
},
{
path: '/detail',
component: () => import('../components/Detail.vue')
}
]
1.5 query传参
1.5.1 完善产品列表的点击事件
// 引入路由对象
import { useRouter } from 'vue-router'
const router = useRouter()
// 点击事件
const toDetail = (item: Item) => {
router.push({
path: '/detail',
query: item,
})
}
此时数据就可以成功的进行传递了。但是是在浏览器的地址栏中显示的。
1.5.2 接收参数
在详情组件中来接收从产品列表中传递过来的数据。
<template>
<h3>产品详情</h3>
<ul>
<li>编号:{{ route.query.id }}</li>
<li>名称:{{ route.query.name }}</li>
<li>价格:{{ route.query.price }}</li>
</ul>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
1.6 params传参
1.6.1 传递参数
我们修改 Product.vue 组件中点击事件中编程导航。
// 点击事件
const toDetail = (item: Item | RouteParamValueRaw | string) => {
router.push({
//path: '/detail',
//query: item,
name: 'Detail',
params: item,
})
}
注意:要使用 params 来传参时,需要使用命令路由。
1.6.2 接收参数
修改详情组件,来接收参数
<template>
<h3>产品详情</h3>
<ul>
<li>编号:{{ route.params.id }}</li>
<li>名称:{{ route.params.name }}</li>
<li>价格:{{ route.params.price }}</li>
</ul>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
</script>
1.7 动态路由传参
很多时候,我们传递的参数不需要把整个对象都以参数的方式来进行传递,而只传递 id,因为 id 是唯一的,不会重复。
大部分情况下,地址栏的信息都是相同的,唯一不同的就可能是 id 这值了,这时我们就可以把这个 id 值设置为动态参数进行传递。
可以在路由中使用冒号(:)来表示,当一个路由被匹配时,它的 params 的值就会将在组件中显示。
1.7.1 修改路由
修改路由配置文件 router/index.ts
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: () => import('../components/Product.vue'),
},
{
path: '/detail/:id', // 动态路由
name: 'Detail',
component: () => import('../components/Detail.vue')
}
]
1.7.2 传递参数
修改 Product.vue 组件中点击情况的事件代码:
const toDetail = (item: Item) => {
router.push({
name: 'Detail',
params: {
id: item.id
}
})
}
1.7.3 接收参数
修改 Detail.vue 组件,代码如下:
<template>
<h3>产品详情</h3>
<ul>
<!-- 接收动态参数 -->
<li>编号:{{ item.id }}</li>
<li>名称:{{ item.name }}</li>
<li>价格:{{ item.price }}</li>
</ul>
<button @click="router.back()">返回</button>
</template>
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'
import { data } from '../list.json'
const route = useRoute()
const router = useRouter()
// 在 JSON 数据中查询传递过来的 id 对象的产品
const item = data.find(v => v.id === Number(route.params.id))
</script>
2. 嵌套路由
2.1 环境准备
我们在 components 目录下新建一个 Footer.vue 组件
<template>
<h3>我是父路由</h3>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>
2.2 定义路由
在 router/index.ts 文件中添加父路由配置。
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: () => import('../components/Product.vue'),
},
{
path: '/detail/:id', // 动态路由
name: 'Detail',
component: () => import('../components/Detail.vue')
},
{
path: '/footer',
component: () => import('../components/Footer.vue')
}
]
2.3 创建子组件
在 components 目录下创建 Login.vue 和 Register.vue 组件。
Login.vue
<template>
<div class="container">
<h3>Login</h3>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
background-color: #0a53be;
color: #FFFFFF;
}
</style>
Register.vue
<template>
<div class="container">
<h3>Register</h3>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.container {
width: 100%;
height: 100%;
background-color: #0f5132;
color: #FFFFFF;
}
</style>
2.4 配置子路由
我们给 Footer 父组件路由两个子路由。
{
path: '/footer',
component: () => import('../components/Footer.vue'),
// 配置子路由——嵌套路由
children: [
{
path: 'login',
component: () => import('../components/Login.vue'),
},
{
path: 'register',
component: () => import('../components/Register.vue'),
},
]
}
2.5 在根组件中添加链接
App.vue
<template>
<router-link to="/">产品列表</router-link> | <router-link to="/footer">有账号?</router-link>
<hr>
<router-view></router-view>
</template>
2.6 修改Footer.vue组件
在这个组件中添加 <router-view>
来显示子组件,同时指定链接地址。
<template>
<h3>我是父路由</h3>
<router-link to="/footer/login">登录</router-link> | <router-link to="/footer/register">注册</router-link>
<hr>
<router-view></router-view>
</template>
<script setup lang="ts">
</script>
3. 命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。
3.1 环境准备
我们在 components 目录下新建 A.vue、B.vue、C.vue 和 Root.vue 四个组件。
A.vue
<template>
<h3>A组件</h3>
</template>
<script setup lang="ts">
</script>
B.vue
<template>
<h3>B组件</h3>
</template>
<script setup lang="ts">
</script>
C.vue
<template>
<h3>C组件</h3>
</template>
<script setup lang="ts">
</script>
Root.vue
<template>
<h3>Root组件</h3>
</template>
<script setup lang="ts">
</script>
3.2 定义路由
在 router/index.ts 文件中添加上面四个组件的路由配置。
{
path: '/root',
component: () => import('../components/Root.vue'),
children: [
{
path: 'user1',
components: {
default: () => import('../components/A.vue'), // 默认视图
}
},{
path: 'user2',
components: {
bb: () => import('../components/B.vue'), // 命名视图
cc: () => import('../components/C.vue'), // 命名视图
},
}
]
},
3.3 使用视图
在 Root.vue 组件中来使用命名视图,需要给 <router-view></router-view>
标签指定 name
属性。
<template>
<h3>Root组件</h3>
<router-link to="/root/user1">user1</router-link> |
<router-link to="/root/user2">user2</router-link>
<hr>
<!-- 显示默认视图 -->
<router-view></router-view>
<router-view name="bb"></router-view>
<router-view name="cc"></router-view>
</template>
<script setup lang="ts">
</script>
4. 重定向和别名
4.1 重定向
重定向也是通过 routes
配置来完成,需要使用 redirect
来实现。可以通过以下三种方式来实现重定向。
4.1.1 字符串
{
path: '/root',
redirect: '/root/user1', // 重定向
component: () => import('../components/Root.vue'),
children: [
{
path: 'user1',
components: {
default: () => import('../components/A.vue'), // 默认视图
}
},{
path: 'user2',
components: {
bb: () => import('../components/B.vue'), // 命名视图
cc: () => import('../components/C.vue'), // 命名视图
},
}
]
},
通过指定
redirect
属性来进行重定向的配置。
4.1.2 对象
重定向还可以是一个对象,动态返回重定向目标:
{
path: '/root',
//redirect: '/root/user1', // 重定向——字符串
redirect: {
//path: '/root/user1',
name: 'user1',
},
component: () => import('../components/Root.vue'),
children: [
{
path: 'user1',
name: 'user1',
components: {
default: () => import('../components/A.vue'), // 默认视图
}
},{
path: 'user2',
components: {
bb: () => import('../components/B.vue'), // 命名视图
cc: () => import('../components/C.vue'), // 命名视图
},
}
]
},
使用对象时,可以使用
path
,也可以使用name
来进行命名路由重定向配置。
4.1.3 函数
{
path: '/root',
//redirect: '/root/user1', // 重定向——字符串
/*redirect: { // 重定向——对象
//path: '/root/user1',
name: 'user1',
},*/
redirect: () => { // 重定向——函数
return {
path: '/root/user1',
query: {
name: '天子'
}
}
},
component: () => import('../components/Root.vue'),
children: [
{
path: 'user1',
name: 'user1',
components: {
default: () => import('../components/A.vue'), // 默认视图
}
},{
path: 'user2',
components: {
bb: () => import('../components/B.vue'), // 命名视图
cc: () => import('../components/C.vue'), // 命名视图
},
}
]
},
使用函数配置的重定向,还可以传递参数。
4.2 别名
将 /
别名为 /root
,意味着当用户访问 /root
时,URL 仍然是 /user
,但会被匹配为用户正在访问 /
{
path: '/root',
//redirect: '/root/user1', // 重定向——字符串
/*redirect: { // 重定向——对象
//path: '/root/user1',
name: 'user1',
},*/
redirect: () => { // 重定向——函数
return {
path: '/root/user1',
query: {
name: '天子'
}
}
},
alias: ['/root', '/root2', '/root3'], // 别名
component: () => import('../components/Root.vue'),
children: [
{
path: 'user1',
name: 'user1',
components: {
default: () => import('../components/A.vue'), // 默认视图
}
},{
path: 'user2',
components: {
bb: () => import('../components/B.vue'), // 命名视图
cc: () => import('../components/C.vue'), // 命名视图
},
}
]
},
使用别名:
<template>
<router-link to="/">产品列表</router-link> | <router-link to="/footer">嵌套路由</router-link> |
<router-link to="/root">命名视图</router-link> | <router-link to="/root2">命名视图——别名</router-link>
<hr>
<router-view></router-view>
</template>
使用别名和使用 path 路由的效果一样。