Vue-Router 笔记
目录
快速部署(TS)
// @/router/index.ts
// 导入页面
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 配置路由
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
alias: '/home',
component: HomeView
},
{
// 页面路径
path: "/square",
// 页面名
name: "square",
// 对于的vue代码,这样写页面会在打开时才加载
component: () => import('@/views/square.vue')
},
{
path: "/knowledgebase/:id*",
name: "knowledgebase",
component: () => import('@/views/KnowledgeBaseView.vue')
},
{
path: "/content/:id",
name: "new",
component: () => import('@/views/ContentView.vue')
},
{
// 匹配其他漏掉的页面
path: "/:pathMatch(.*)*",
name: "404",
component: () => import('@/views/404View.vue')
}
]
})
// 路由守卫
router.afterEach((to, from) => {
const hash = to.hash
if (hash) {
const targetElement = document.getElementById(hash.substring(1));
if (targetElement) {
targetElement.scrollIntoView();
}
}
})
// 导出路由
export default router
使用
// @/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
const app = createApp(App);
app.use(router);
app.mount("#app");
// @/App.vue
<template>
<!-- 使用路由 -->
<router-view></router-view>
</template>
// xxx.vue
<template>
<!-- 跳转到对应组件 -->
<router-link to="/home">home</router-link>
</template>
配置 Vue-Router
import components | 导入路由组件
2 种导入方法
// 首页加载时就会加载页面(不管首页是不是它)
import HomeView from '../views/HomeView.vue'
// 只有访问了对应的路径才会加载页面,可以加快首页的加载速度
{
path: '/',
name: 'home',
alias: '/home',
component: () => import('@/views/HomeView.vue')
}
// 补充一种奇怪的东西
// 路由组件对象只需要有component属性就行(怪)
const User = {
template: '<div>User</div>',
}
const routes = [
{ path: '/users', component: User },
]
createRouter | 路由配置
import { createRouter } from 'vue-router'
const router = createRouter({
history: // ...
routes: [
//...
],
})
history | 不同的历史模式
Hash 模式
hash 模式是用 createWebHashHistory()
创建的:
import { createRouter, createWebHashHistory } from "vue-router";
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
});
它在内部传递的实际 URL 之前使用了一个哈希字符(#
)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO(搜索引擎优化) 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。
并且使用了这个之后就不能用页面内跳转和跳转到页面指定部分了,因为页面内跳转就是基于这个的
HTML5 模式
用 createWebHistory()
创建 HTML5 模式,推荐使用这个模式:
import { createRouter, createWebHistory } from "vue-router";
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
});
当使用这种历史模式时,URL 会看起来很 "正常",例如 https://example.com/user/id
。漂亮!
不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id
,就会得到一个 404 错误。这就尴尬了。
不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html
相同的页面。漂亮依旧!
缺点:会增加访问服务器的次数
不同的历史模式 | HTML5 模式 | 服务器配置示例
不过要注意的是,服务器将不再会报 404 错误,你可以使用复杂的麻烦的配置在服务端实现所有 url 的匹配,然后将其余的返回 404
举例部分:
- NGINX
location / {
try_files $uri $uri/ /index.html;
}
单个页面配置的属性
const router = createRouter({
history: createWebHistory(),
routes: [
{
// 路径
path: "/home", // 默认匹配 /home /HomE /home/
// 路由名称,可以通过路由名称打开路由组件
name: "home",
// 路径别名,对应的路径也会打开该路由组件
alias: "/home",
// 对应的路由组件
component: () => import("@/views/HomeView.vue"),
// 严格检查后缀斜杠,默认false
strict: true,
// 严格检查大小写,默认false
sensitive: true,
},
],
// 全局使用严格检查
strict: true,
sensitive: true,
});
动态路由匹配
静态不讲了,有手就行
const routes = [
// 普通的匹配
{ path: "/home" },
// 匹配/home /HomE (不分大小写) /home/
];
动态路由匹配用于:
url 地址是动态的(比如各个用户的主页 url)
需要获取 url 里面的参数
基础用法
// router/index
const routes = [
// 动态匹配
{ path: "/users/:username" },
];
// xxxx.vue <js>
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
console.log(route.params);
// 动态路由中匹配到的字符串会解析到这个对象中,字符串数组
// eg:{
// user:"truraly"
// }
正则复杂用法
const routes = [
// 动态匹配
{ path: "/users/:username" },
// 匹配/users/truraly
// {username:"truraly"}
{ path: "/users/:username/blog/:bid" },
// 匹配/users/truraly/blog/123
// {username:"truraly",bid:"123"}
// /:chapters -> 匹配 /one, /one/two, /one/two/three, 等
{ path: "/:chapters+" },
// /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
{ path: "/:chapters*" },
// /:chapters -> 匹配 /, /one
{ path: "/:chapters?" },
// 使用正则规定路径
{ psth: "/users/:uid(\\d+)" }, // 注意双斜杠
// 匹配/users/114514
// 只匹配数字,且不能为空
// { uid: 114514 }
//
{ path: "/users/:uid(\\d+)*" },
// 匹配/users /users/123 /users/123/456
// 匹配多个数字串
// { uid: [123, 456] }
//
{ path: "/users/user-:username(\\w+)" },
// 匹配 /users/user-truraly /users/user-xiabeize
// { username: "truraly" }
// 常用于404页面
{ path: "/path404(*)*" },
// 匹配其他字符串匹配不到的
// /asda/asd
// { path404: [ "asda", "asd" ] }
// 其实这种也能用与404,会有一些区别
{ path: "/path404(*)" },
// 匹配其他字符串匹配不到的
// /asda/asd
// { path404: "asda/asd" }
];
使用 vue-router 对加载的影响
使用 vue 路由时需要注意,跳转页面时相同的组件不会重新加载
比如从一个人的博客跳转到另一个人的博客时,它可能不会按你想象得那样刷新
解决方法:
// plan 1
// xxxx.vue
// 使用watch监视url的变化
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
this.$watch(
() => route.params,
(toParams, previousParams) => {
// 对路由变化做出响应...
}
);
// plan 2
// router/index.ts
// 使用 beforeRouteUpdate 导航守卫
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/blog/:bid",
// ...
async beforeRouteUpdate(to, from) {
// 对路由变化做出响应...
let bid = await fetchUser(to.params.bid);
},
},
],
});
children | 嵌套路由
/user/johnny/profile /user/johnny/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
在 APP.vue 之外的组件上使用<router-view></router-view>
根据 url 的情况选择性加载部分页面
比如用户博客界面和用户信息界面,可以有部分一样的
// 父组件
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
// 该父组件的路由配置
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 <router-view> 内部
path: 'profile',
component: UserProfile,
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 将被渲染到 User 的 <router-view> 内部
path: 'posts',
component: UserPosts,
},
{
// 以/开头的嵌套路径将被视为根路径,即无视父路径匹配/users
// UserList 将被渲染到 User 的 <router-view> 内部
path: '/users',
compenent: UserList
},
{
// 当 /user/:id 匹配成功
path: '',
compenent: User-detail
}
]
}
- 当子路由被命名时,
/user/:id
将始终匹配到子路由
const routes = [
{
path: "/user/:id",
component: User,
// 请注意,只有子路由具有名称
children: [{ path: "", name: "user", component: UserHome }],
},
];
当父子路由都命名时,可以用父路由的名字来导航到父路由(不显示子路由)
但刷新页面时,会显示嵌套路由,因为这被视为访问了
url:/user/:id
而不是name:user-parent
const routes = [
{
path: "/user/:id",
name: "user-parent",
component: User,
children: [{ path: "", name: "user", component: UserHome }],
},
];
路由的声明式和编程式使用方法
// 获取RouterLink(声明式)
import { RouterLink } from "vue-router";
// 获取router(编程式)
import { useRouter } from "vue-router";
const router = useRouter();
编程式路由底层是对浏览器 windows.history 的一个封装?
差不多
RouterLink 和 router.push()是向 history 栈里添加记录,可以返回
router.replace()是替换栈顶,被替换的网页不能返回
RouterLink | 声明式
<RouterLink :to="...">
<!-- 跳转到一个命名路由 -->
<router-link :to="{ name: 'user', params: { username: 'erina' }}">
User
</router-link></RouterLink
>
router.push() | 编程式
// 跳转到对应的url
router.push();
// 字符串路径
router.push("/users/eduardo");
// 带有路径的对象
router.push({ path: "/users/eduardo" });
// 命名的路由,并加上参数,让路由建立 url
router.push({ name: "user", params: { username: "eduardo" } });
// 带查询参数,结果是 /register?plan=private
router.push({ path: "/register", query: { plan: "private" } });
// 带 hash,结果是 /about#team
router.push({ path: "/about", hash: "#team" });
当path
参数存在时,params
会被忽略
// `params` 不能与 `path` 一起使用
router.push({ path: "/user", params: { username } }); // -> /user
也可以使用字符串拼接出完整的path
路径
const username = "eduardo";
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`); // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }); // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: "user", params: { username } }); // -> /user/eduardo
当指定 params
时,可提供 string
或 number
参数(或者对于可重复的参数可提供一个数组)。任何其他类型(如 undefined
、false
等)都将被自动字符串化。对于可选参数,你可以提供一个空字符串(""
)来跳过它。
router.replace() | 编程式
用法和router.push()
一样,甚至可以用router.push()
代替(在传递给 router.push
的 routeLocation
中增加一个属性 replace: true
)
router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
router.go() | 编程式
类似window.history.go(n)
// 向前移动一条记录,与 router.forward() 相同
router.go(1);
// 返回一条记录,与 router.back() 相同
router.go(-1);
// 前进 3 条记录
router.go(3);
// 如果没有那么多记录,静默失败
router.go(-100);
router.go(100);
name | 命名路由
优点:
- 没有硬编码的 URL
params
的自动编码/解码。- 防止你在 url 中出现打字错误。
- 绕过路径排序(如显示一个)
const routes = [
{
path: "/user/:username",
name: "user",
component: User,
},
];
name | 命名视图
使用并列而不是嵌套的方法摆放几个路由
<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
<!-- name不声明则默认为defult -->
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" name="RightSidebar"></router-view>
路由配置(注意使用 components而不是 component)
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: "/",
components: {
default: Home,
// LeftSidebar: LeftSidebar 的缩写
LeftSidebar,
// 它们与 `<router-view>` 上的 `name` 属性匹配
RightSidebar,
},
},
],
});
案例 | 嵌套命名试图
/settings/emails /settings/profile
+-----------------------------------+ +------------------------------+
| UserSettings | | UserSettings |
| +-----+-------------------------+ | | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | | +------------> | | Nav | UserProfile | |
| | +-------------------------+ | | | +--------------------+ |
| | | | | | | | UserProfilePreview | |
| +-----+-------------------------+ | | +-----+--------------------+ |
+-----------------------------------+ +------------------------------+
Nav
只是一个常规组件。UserSettings
是一个视图组件。UserEmailsSubscriptions
、UserProfile
、UserProfilePreview
是嵌套的视图组件。
<!-- UserSettings.vue -->
<div>
<h1>User Settings</h1>
<NavBar />
<router-view />
<router-view name="helper" />
</div>
// router
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
redirect | 重定向
// 从 /home 重定向到 /
const routes = [{ path: "/home", redirect: "/" }];
// 重定向的目标也可以是一个命名的路由
const routes = [{ path: "/home", redirect: { name: "homepage" } }];
// 方法作为重定向的目标,实现动态重定向
const routes = [
{
// /search/screens -> /search?q=screens
path: "/search/:searchText",
redirect: (to) => {
// 方法接收目标路由作为参数
// return 重定向的字符串路径/路径对象
return { path: "/search", query: { q: to.params.searchText } };
},
},
{
path: "/search",
// ...
},
];
请注意,导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在上面的例子中,在 /home
路由中添加 beforeEnter
守卫不会有任何效果。
在写 redirect
的时候,可以省略 component
配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 children
和 redirect
属性,它也应该有 component
属性。
相对重定向
const routes = [
{
// 将总是把/users/123/posts重定向到/users/123/profile。
path: "/users/:id/posts",
redirect: (to) => {
// 该函数接收目标路由作为参数
// 相对位置不以`/`开头
// 或 { path: 'profile'}
return "profile";
},
},
];
alias | 别名
// 将/ 别名为 /home,意味着当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /。
const routes = [{ path: "/", component: Homepage, alias: "/home" }];
//
const routes = [
{
path: "/users",
component: UsersLayout,
children: [
// 为这 3 个 URL 呈现 UserList
// - /users
// - /users/list
// - /people
// 使用数组提供多个别名
{ path: "", component: UserList, alias: ["/people", "list"] },
],
},
];
如果你的路由有参数,请确保在任何绝对别名中包含它们:
const routes = [
{
path: "/users/:id",
component: UsersByIdLayout,
children: [
// 为这 3 个 URL 呈现 UserDetails
// - /users/24
// - /users/24/profile
// - /24
{ path: "profile", component: UserDetails, alias: ["/:id", ""] },
],
},
];
props | 路由组件传参
vue-router 中 props 有 3 种模式,分别将 3 种对象传入组件中
布尔模式
当 props
设置为 true
时,route.params
将被设置为组件的 props。
<!-- xxx.vue -->
<script setup lang="ts">
const props = defineProps(["uid"]);
</script>
<template>
<h1>{{ uid }}</h1>
</template>
// router
const routes = [
{
// 参数名称要相同
path: "/user/:uid",
component: User,
// 注意这个属性不能落下,不写template里面就不能用
props: true,
},
];
对于有命名视图的路由,你必须为每个命名视图定义 props
配置:
const routes = [
{
path: "/user/:id",
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false },
},
];
对象模式
当 props
是一个对象时,它将原样设置为组件 props。当 props 是静态的时候很有用。
const routes = [
{
path: "/promotion/from-newsletter",
component: Promotion,
props: { newsletterPopup: false },
},
];
函数模式
你可以创建一个返回 props 的函数。这允许你将参数转换为其他类型,将静态值与基于路由的值相结合等等。
const routes = [
{
path: "/search",
component: SearchUser,
props: (route) => ({ query: route.query.q }),
},
];
URL /search?q=vue
将传递 {query: 'vue'}
作为 props 传给 SearchUser
组件。