pnpm add vue-router@4
创建路由文件
├── src | ├── App.vue | ├── router | | └── index.ts ...
VueRouter@4
版本中创建路由通过createRouter
创建,而vue-router3
中使用的是new Router
;
src/router/index.ts
tsimport { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => import('@views/IndexView.vue'),
},
],
});
export default router;
src/main.ts
tsimport { createApp } from 'vue';
import './style.css';
import router from './router/index.ts';
import App from './App.vue';
createApp(App).use(router).mount('#app');
VueRouter4
中同样是具有hash
模式和history
模式, 不同在于指定路由模式时的不同
hash
模式: createWebHashHistory
创建history
模式: createWebHistory
创建tsimport { createRouter, createWebHistory } from 'vue-router';
// import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => import('@views/IndexView.vue'),
},
],
});
export default router;
VueRouter3
中mode
直接传入history
或者hash
即可
tsimport Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: '登录',
component: () => import('@/views/Login.vue')
},
...
]
})
VueRouter4
中搭配组合式API使用根路由实例: 通过useRouter
获取
当前路由实例: 通过useRoute
获取
VueRouter3
中根路由实例: 通过this.$router
获取
当前路由实例: 通过this.$route
获取
会被渲染为带有href
属性的a
标签
vue<router-link to="/about"></router-link>
vue<RouterLink to="/register">跳转注册页面</RouterLink>
路由视图,用于显示与url
对应的组件
vue<template> <div> <el-button type="primary" plain @click="changeTest(1)">登录</el-button> <el-button type="primary" plain @click="changeTest(2)">注册</el-button> <RouterLink to="/register">跳转注册页面</RouterLink> </div> <RouterView></RouterView> </template> ...
tsimport { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const StaticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('@views/IndexView.vue'),
children: [
{
path: '/login/:name',
name: 'login',
props: true,
component: () => import('@views/Login/LoginView.vue'),
},
{
path: '/register',
name: 'register',
props: { name: String },
component: () => import('@views/Register/RegisterView.vue'),
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: StaticRoutes,
});
export default router;
命名路由视图EG
vue<template> <RouterView name="login"></RouterView> <RouterView name="register"></RouterView> </template> ...
tsimport { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const StaticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
components: {
login: () => import('@views/Login/LoginView.vue'),
register: () => import('@views/Register/RegisterView.vue'),
},
},
];
const router = createRouter({
history: createWebHistory(),
routes: StaticRoutes,
});
export default router;
和vue-router_v3
没区别,都是path: '/login/:name/:age/:hobby'
的形式
动态路由映射params
:
props: true
选项defineProps
规定接受的props
, 或者直接通过useRoute
获取当前路由然后使用.params
获取到传递的参数touter.ts
路由中的动态路由规定props:true
将动态路由映射到子组件内的props
上
ts{
path: '/',
name: 'home',
component: () => import('@views/IndexView.vue'),
children: [
{
path: '/login/:name/:age/:hobby',
name: 'login',
props: true,
component: () => import('@views/Login/LoginView.vue'),
},
{
path: '/register',
name: 'register',
props: { name: String },
component: () => import('@views/Register/RegisterView.vue'),
},
],
},
路由对应的组件: 需要通过defineProps
规定接受的props
参数
vue<template> <div>登录界面</div> </template> <script setup lang="ts"> const route = useRoute(); // 在动态路由规定`props:true`后可以通过`defineProps`获取params const propsmy = defineProps<{ name: string; age: string; hobby: string }>(); console.log('propsmy登录', propsmy); // 不使用defineProps在动态路由规定`props:true`后可以直接通过`route.params`获取 console.log('params登录', route, route.params); </script> <style></style>
在对应的动态路由中未规定props:true
规定props:true
后
动态路由对应的组件内不使用defineProps
, 直接使用route.params
获取
tsimport { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const StaticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('@views/IndexView.vue'),
children: [
{
path: '/login/:name/:age/:hobby',
name: 'login',
props: true,
component: () => import('@views/Login/LoginView.vue'),
},
{
path: '/register',
name: 'register',
props: { name: String },
component: () => import('@views/Register/RegisterView.vue'),
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: StaticRoutes,
});
export default router;
name
跳转与params
传参通过useRouter
获取根路由实例,然后调用.push
方法跳转
.push
跳转: 会在history
中添加一条记录,浏览器的返回会回到上一个记录.replace
跳转: 不会在history
中添加记录,浏览器的返回会回到上上条记录使用params
传参时只能搭配name
使用
params
的值必须为一个对象,对象的属性名必须为字符串,对象的属性值必须为string | number | null | undefined
或者(string | number)[]
; 所以如果属性值是一个对象则需要采用JSON.stringify()
转为字符串
vue<script setup lang="ts"> const userinfo = reactive({ name: 'xm', age: 19, hobby: { computer: true, want: ['a', 'b'] } }); const router = useRouter(); function changeTest(v: number) { console.log('v', v); router.push({ name: 'login', params: userinfo }); /* 不能将类型“{ name: string; age: number; hobby: { computer: boolean; want: string[]; }; }”分配给类型“RouteParamsRaw”。 属性“hobby”与索引签名不兼容。ts(2322) (property) LocationAsRelativeRaw.params?: RouteParamsRaw | undefined */ } </script>
node_modules\vue-router\dist\vue-router.d.ts
文件
看到RouteParamsRaw
的类型
ts/**
* @internal
*/
export declare interface LocationAsRelativeRaw {
name?: RouteRecordName;
params?: RouteParamsRaw;
}
继续查找,以下三行就是关键代码
tsexport declare type RouteParamsRaw = Record<string, RouteParamValueRaw | Exclude<RouteParamValueRaw, null | undefined>[]>;
/**
* @internal
*/
export declare type RouteParamValue = string;
/**
* @internal
*/
export declare type RouteParamValueRaw = RouteParamValue | number | null | undefined;
关键是第一行代码, Record
作为ts
的映射类型, Exclude
作为ts
的过滤类型过滤T
中可以赋值给K
的类型
export declare type RouteParamsRaw = Record<string, RouteParamValueRaw | Exclude<RouteParamValueRaw, null | undefined>[]>;
Record
解读tsinterface people {
name: string,
age: number
}
type RecordResult1 = Record<string, people>;
/*
interface people {
name: string,
age: number
}
type RecordResult1 = Record<string, people>;
*/
const a: RecordResult1 = { userinfo: { name: 'xm', age: 18 } }
Exclude
解读type RouteParamValueRaw = string | number | null | undefined; type ExcludeRes = Exclude<RouteParamValueRaw, null | undefined>[] // (string | number)[]
最终的类型为:
tsexport declare type RouteParamsRaw = Record<string, RouteParamValueRaw | Exclude<RouteParamValueRaw, null | undefined>[]>;
{
[X:string]: string | number | null | undefined
} | {
[X:string]: (string | number)[]
}
所以结论:params
的值必须为是一个对象,且对象的属性名必须为字符串,属性值可以是string | number | null | undefined
或者(string | number)[]
,所以对于对象需要采用JSON.stringify()
转为字符串
pinia
、vuex
状态管理器中,共多个页面共享query
传参,或者将数据放到路由参数上(即:动态路由传参)state
将其保存到history
上,通过window.history.state
获取vue<script setup lang="ts"> const userinfo = reactive({ name: 'xm', age: '19', hobby: { computer: 'true', want: ['a', 'b'] } }); const router = useRouter(); function changeTest() { router.push({ name: 'register', state: { name: userinfo.name, age: userinfo.age, hobby: JSON.stringify(userinfo.hobby) } }); } </script> <template> <router-link :to="{ name: '/login', state: { name: userinfo.name, age: userinfo.age, hobby: JSON.stringify(userinfo.hobby) } }">登录</router-link> <button @click="changeTest">注册</button> </template>
path
跳转与query
传参使用query
传的参数以?
形式拼接在地址后面
shhttp://192.168.100.106:3345/login/xm/19/%7B%22computer%22:%22true%22,%22want%22:[%22a%22,%22b%22]%7D
vue<script setup lang="ts"> const userinfo = reactive({ name: 'xm', age: '19', hobby: { computer: 'true', want: ['a', 'b'] } }); const router = useRouter(); function changeTest(v: number) { if (v === 1) { router.push({ name: 'login', params: { name: userinfo.name, age: userinfo.age, hobby: JSON.stringify(userinfo.hobby) } }); } else { router.push({ path: '/register', query: { name: userinfo.name, age: userinfo.age, hobby: JSON.stringify(userinfo.hobby) } }); } } </script> <template> <div> <el-button type="primary" plain @click="changeTest(1)">登录</el-button> <el-button type="primary" plain @click="changeTest(2)">注册</el-button> <RouterLink to="/register">跳转注册页面</RouterLink> </div> <RouterView></RouterView> </template> <style scoped></style>
通过
useRoute
的.query
方法获取
vue<template> <div>注册界面</div> </template> <script setup lang="ts"> const route = useRoute(); console.log('params注册', route.query, window.history.state); </script> <style></style>
tsrouter.push({ path: '/register', query: { name: true, age: { a: 1 } } });
// 不能将类型“true”分配给类型“LocationQueryValueRaw | LocationQueryValueRaw[]”
node_modules\vue-router\dist\vue-router.d.ts
文件
ts/**
* @internal
*/
export declare interface RouteQueryAndHash {
query?: LocationQueryRaw;
hash?: string;
}
查找LocationQueryRaw
,可以看到:
tsexport declare type LocationQueryRaw = Record<string | number, LocationQueryValueRaw | LocationQueryValueRaw[]>;
export declare type LocationQueryValue = string | null;
export declare type LocationQueryValueRaw = LocationQueryValue | number | undefined;
所以可以得知query
传参必须为一个对象,对象的属性名为字符串或数字,对象的属性值必须为string | number | null | undefined
或者(string | number | null | undefined)[]
; 所以如果属性值是一个对象则需要采用JSON.stringify()
转为字符串
同样和vue-routerv3
版本没多大区别,可以直接通过对象{path:'路由路径'}
或者{name:'路由名称'}
跳转
tsimport { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const StaticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
// redirect: { path: '/register' },
redirect: { name: 'register' }
component: () => import('@views/IndexView.vue'),
children: [
{
path: '/login/:name/:age/:hobby',
name: 'login',
props: true,
component: () => import('@views/Login/LoginView.vue'),
},
{
path: '/register',
name: 'register',
component: () => import('@views/Register/RegisterView.vue'),
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: StaticRoutes,
});
export default router;
也可以是一个方法,返回重定向目标
tsimport { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const StaticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
redirect: () => {
return { path: '/login/1/2/3' };
},
component: () => import('@views/IndexView.vue'),
children: [
{
path: '/login/:name/:age/:hobby',
name: 'login',
props: true,
component: () => import('@views/Login/LoginView.vue'),
},
{
path: '/register',
name: 'registers',
component: () => import('@views/Register/RegisterView.vue'),
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: StaticRoutes,
});
export default router;
和vue-router3
不同,由于官方解答的修复,根据官方文档得知如果路由中包含参数,则别名也必须含有对应的参数
tsimport { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const StaticRoutes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('@views/IndexView.vue'),
children: [
{
path: '/login/:name/:age/:hobby',
name: 'login',
props: true,
alias: '/logins/:name/:age/:hobby',
component: () => import('@views/Login/LoginView.vue'),
},
{
path: '/register',
name: 'registers',
component: () => import('@views/Register/RegisterView.vue'),
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: StaticRoutes,
});
export default router;
本文作者:RKLS
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!