一:介绍
1. 什么是 Vue Router
是 Vue.js 的官方路由。功能包括:1. 路由映射。2. 动态路由。3. 路由配置。4. 路由参数。5. 过渡效果。6. 导航控制。7. CSS 类链接。8. HTML5 模式。9. 滚动行为。10. URL 编码
2. 安装
对于一个现有使用 JS 包管理的项目,添加 Vue Router 依赖:
npm install vue-router@4 # or yarn add vue-router@4 # or pnpm add vue-router@4
通过 create-vue 脚手架创建一个基于 Vite 的新项目,加入 Vue Router 的选项:
npm create vue@latest # or yarn create vue # or pnpm create vue
二:基础
1. 入门
router-view 显示与 URL 对应的组件。可以放在任何地方,以适应布局。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <script src="https://unpkg.com/vue@3"></script> <script src="https://unpkg.com/vue-router@4"></script> <div id="app"> <h1>Hello App!</h1> <p> <!--使用 router-link 组件进行导航 --> <!--通过传递 `to` 来指定链接 --> <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签--> <router-link to="/">Go to Home</router-link> <router-link to="/about">Go to About</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> </body> <script> // 1. 定义路由组件. const Home = { template: "<div>Home</div>" }; const About = { template: "<div>About</div>" }; // 2. 定义一些路由 // 每个路由映射一个组件。 const routes = [ { path: "/", component: Home }, { path: "/about", component: About }, ]; // 3. 创建路由实例并传递 `routes` 配置 // 可以在这里输入更多的配置 const router = VueRouter.createRouter({ // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。 history: VueRouter.createWebHashHistory(), routes, // `routes: routes` 的缩写 }); // 5. 创建并挂载根实例 const app = Vue.createApp({}); //确保 _use_ 路由实例使 //整个应用支持路由。 app.use(router); app.mount("#app"); </script> </html>
在 Vue3 中通过调用 useRouter 和 useRoute 函数创建实例来访问。
<script setup lang="ts"> import { useRouter, useRoute } from "vue-router"; const router = useRouter(); const route = useRoute(); router.push("/"); // 跳转 route.params; // 参数 </script>
2. 动态路由匹配
多个匹配路由映射到同一组件,通过一个动态字段来实现,称之为路径参数,路径参数用冒号 : 表示。如下 /users/johnny 和 /users/jolyne 会映射到同一个路由。
const User = { template: "<div>User</div>", }; // 这些都会传递给 `createRouter` const routes = [ // 动态字段以冒号开始 { path: "/users/:id", component: User }, ];
通过 this.$route.params 来访问 URL 上的参数:
const User = { template: "<div>User {{ $route.params.id }}</div>", };
路由可以设置多个参数,在 $route.params 上相对应:
const User = { template: "<div>User</div>", }; // 传递给 `createRouter` const routes = [ // 动态字段以冒号开始 { path: "/users/:username/posts/:postId", component: User }, ]; // 匹配路由:/users/eduardo/posts/123 // $route.params:{ username: 'eduardo', postId: '123' }
2.1 响应路由参数的变化
同一路由的不同参数跳转,因为映射的是相同组件,所以复用组件显得更高效,但生命周期函数就不会被调用,比如从 /users/johnny 导航到 /users/jolyne。要对同一路由的参数做出响应,需要用 watch 监听:
// vue3 import { watchEffect } from "vue"; watchEffect(() => { console.log(1, route.params, route.query); });
2.2 捕获所有路由或 404 Not found 路由
想匹配任意路径,使用路径参数正则表达式,在路径参数后面的括号中加入正则表达式 :
const routes = [ // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下 { path: "/:pathMatch(.*)*", name: "NotFound", component: NotFound }, // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下 { path: "/user-:afterUser(.*)", component: UserGeneric }, ];
3. 路由的匹配语法
大多数应用都会使用 /about 静态路由和 /users/:userId 动态路由,但是 Vue Router 提供了更多的方式!
3.1 在参数中自定义正则
两个路由 /:orderId 和 /:productName,会匹配完全相同的 URL,需要一种方法来区分他们,最简单的方法是添加一个静态部分来区分:
const routes = [ // 匹配 /o/3549 { path: "/o/:orderId" }, // 匹配 /p/books { path: "/p/:productName" }, ];
如果不想添加静态部分,也可以添加正则,orderId 总是一个数字,productName 可以是任何东西。以在括号中为参数指定正则,现在,/25 将匹配 /:orderId,其他情况匹配 /:productName。
const routes = [ // /:orderId -> 仅匹配数字 { path: "/:orderId(\\d+)" }, // /:productName -> 匹配其他任何内容 { path: "/:productName" }, ];
TIP:确保转义反斜杠( \ ),就像对 \d (变成\\d)所做的那样,在 JS 中实际传递字符串中的反斜杠字符。
3.2 可重复的参数
匹配 /first/second/third 路由,应该用 \*(0 个或多个)和 +(1 个或多个)将参数标记为可重复:
const routes = [ // /:chapters -> 匹配 /one, /one/two, /one/two/three, 等 { path: "/:chapters+" }, // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等 { path: "/:chapters*" }, ];
提供了参数数组,而不是字符串,并且在使用命名路由时也需要你传递一个数组:
// 给定 { path: '/:chapters*', name: 'chapters' }, router.resolve({ name: "chapters", params: { chapters: [] } }).href; // 产生 / router.resolve({ name: "chapters", params: { chapters: ["a", "b"] } }).href; // 产生 /a/b // 给定 { path: '/:chapters+', name: 'chapters' }, router.resolve({ name: "chapters", params: { chapters: [] } }).href; // 抛出错误,因为 `chapters` 为空
可以与正则结合使用:
const routes = [ // 仅匹配数字 // 匹配 /1, /1/2, 等 { path: "/:chapters(\\d+)+" }, // 匹配 /, /1, /1/2, 等 { path: "/:chapters(\\d+)*" }, ];
3.3 Sensitive 与 strict 路由配置
默认情况下,所有路由是不区分大小写和带尾部斜线的路由的。例如,路由 /users 将匹配 /users、/users/、/Users/。通过 strict 和 sensitive 选项来修改。
const router = createRouter({ history: createWebHistory(), routes: [ // 将匹配 /users/posva 而非: // - /users/posva/ 当 strict: true,尾部不能有斜线 // - /Users/posva 当 sensitive: true,路由区分大小写 { path: "/users/:id", sensitive: true }, // 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/ { path: "/users/:id?" }, ], strict: true, // applies to all routes });
3.4 可选参数
通过使用 ? 修饰符(0 个或 1 个)将一个参数标为可选:
const routes = [ // 匹配 /users 和 /users/posva { path: "/users/:userId?" }, // 匹配 /users 和 /users/42 { path: "/users/:userId(\\d+)?" }, ];
4. 嵌套路由
顶层的 router-view 渲染顶层路由匹配的组件。一个被渲染的组件也可以包含嵌套的 :
const User = { template: ` <div class="user"> <h2>User {{ $route.params.id }}</h2> <router-view></router-view> </div> `, };
要将组件渲染到嵌套的 router-view 中,需要在路由中配置 children,以 / 开头的嵌套路径将被视为根路径。不必使用嵌套的 URL:
const routes = [ { path: "/user/:id", component: User, children: [ { // 当 /user/:id/profile 匹配成功 // UserProfile 将被渲染到 User 的 <router-view> 内部 path: "profile", component: UserProfile, }, { // 当 /posts 匹配成功 // UserPosts 将被渲染到 User 的 <router-view> 内部 path: "/posts", component: UserPosts, }, ], }, ];
如上配置,访问 /user/eduardo ,User 的 router-view 什么都不展示,因为没有匹配到嵌套路由。可以提供一个空的嵌套路径,来展示一些东西:
const routes = [ { path: "/user/:id", component: User, children: [ // 当 /user/:id 匹配成功 // UserHome 将被渲染到 User 的 <router-view> 内部 { path: "", component: UserHome }, // ...其他子路由 ], }, ];
5. 编程式导航
除了 定义导航链接,还可以使用 router 的实例方法来导航。router.push 返回的是 Promise。
5.1 router.push 导航
使用 router.push 方法,会向 history 栈添加一个新的记录,所以,当点击浏览器后退按钮时,会回到之前的 URL。当点击 时,内部会调用这个方法,所以点击 相当于调用 router.push(...) :
// 字符串路径 router.push("/users/eduardo"); // 带有路径的对象 router.push({ path: "/users/eduardo" }); // 如果路由没有命名username参数,会报错,path: /user/:username? 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 参数会无效,query 参数适用 path 或 name 跳转:
const username = "eduardo"; router.push(`/user/${username}`); // -> /user/eduardo router.push({ path: `/user/${username}` }); // -> /user/eduardo router.push({ name: "user", params: { username } }); // -> /user/eduardo // `params` 不能与 `path` 一起使用 router.push({ path: "/user", params: { username } }); // -> /user
5.2 替换当前位置
不会向 history 添加新记录,直接取代了当前的路由。用法:
<script setup lang="ts"> import { useRouter } from "vue-router"; const router = useRouter(); router.push({ path: "/home", replace: true }); // 相当于 router.replace({ path: "/home" }); </script> <template> <router-link :to="..." replace> </template>
5.3 横跨历史
router.go(n) 用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 window.history.go(n)。
// 向前移动一条记录,与 router.forward() 相同 router.go(1); // 返回一条记录,与 router.back() 相同 router.go(-1); // 前进 3 条记录 router.go(3); // 如果没有那么多记录,静默失败 router.go(-100); router.go(100);
6. 命名路由
使用 name 命名路由。优点:1. 没有硬编码的 URL。2. params 的自动编码/解码。3. 防止你在 url 中出现打字错误。4. 绕过路径排序(如显示一个)。
const routes = [ { path: "/user/:username", name: "user", component: User, }, ];
命名路由,通过 params 传递路由参数,如下路由将导航到 /user/erina。
<script setup lang="ts"> import { useRouter } from "vue-router"; const router = useRouter(); const handleGo = () => router.push({ name: "user", params: { username: "erina" } }); </script> <template> <router-link :to="{ name: 'user', params: { username: 'erina' } }"> User </router-link> <div class="home" @click="handleGo">home</div> </template>
7. 命名视图
设置多个同级,通过 name 属性确定渲染组件,默认 default:
<router-view class="view left-sidebar" name="LeftSidebar"></router-view> <router-view class="view main-content"></router-view> <router-view class="view right-sidebar" name="RightSidebar"></router-view>
多个视图就需要多个组件。正确配置 components (带上 s):
const router = createRouter({ history: createWebHistory(), routes: [ { path: "/", components: { default: Home, // LeftSidebar: LeftSidebar 的缩写 LeftSidebar, // 它们与 `<router-view>` 上的 `name` 属性匹配 RightSidebar, }, }, ], });
8. 重定向和别名
8.1 重定向
通过配置 redirect,实现路由重定向,当有 redirect 属性时,可以省略 component ,因为没有被访问,如下从 /home 重定向到 /:
const routes = [{ path: "/home", redirect: "/" }];
也可以使用 name 重定向:
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", // ... }, ];
8.2 别名
访问别名和访问路由一个效果,使用 alias 声明别名,以 / 开头,并可以使用数组提供多个别名:
const routes = [ { path: "/", component: Homepage, alias: "/home", }, { path: "/users", component: UsersLayout, children: [ // 为这 3 个 URL 呈现 UserList // - /users // - /users/list // - /people { path: "", component: UserList, alias: ["/people", "list"] }, ], }, ];
9. 路由组件传参
在组件中使用 $route 会与路由紧密耦合,限制了组件的灵活性,因为它只能用于特定的 URL。可以通过 props 配置来解除这种行为:
const User = { template: "<div>User {{ $route.params.id }}</div>", }; const routes = [{ path: "/user/:id", component: User }]; // 替换成 const User = { // 请确保添加一个与路由参数完全相同的 prop 名 props: ["id"], template: "<div>User {{ id }}</div>", }; const routes = [{ path: "/user/:id", component: User, props: true }];
9.1 命名视图
对于有命名视图的路由,必须为每个命名视图定义 props 配置:
const routes = [ { path: "/user/:id", components: { default: User, sidebar: Sidebar }, props: { default: true, sidebar: false }, }, ];
9.2 函数模式
创建一个返回 props 的函数。可以将参数转换为其他类型:
const routes = [ { path: "/demo/:id?", name: "demo", component: Demo, props: (route) => { return { id: +route.params.id }; }, }, ];
9.3 Via RouterView
<RouterView v-slot="{ Component }"> <component :is="Component" view-prop="value" /> </RouterView>
10. 不同的历史模式
在创建路由器实例时,history 配置可以选择不同的历史模式。
10.1 Hash 模式
hash 模式是用 createWebHashHistory() 创建的,会在 url 后添加哈希字符(#),对 SEO 不友好,不推荐:
import { createRouter, createWebHashHistory } from "vue-router"; const router = createRouter({ // /home#/ history: createWebHashHistory(), routes: [ //... ], });
10.2 Memory 模式
Memory 模式是用 createMemoryHistory() 创建的,适合 Node 环境和 SSR,不推荐:
import { createRouter, createMemoryHistory } from "vue-router"; const router = createRouter({ history: createMemoryHistory(), routes: [ //... ], });
10.3 HTML5 模式
用 createWebHistory() 创建 HTML5 模式,URL 会看起来很 "正常",推荐使用这个模式:
import { createRouter, createWebHistory } from "vue-router"; const router = createRouter({ history: createWebHistory(), routes: [ //... ], });
Vue-Router 官网速通(下)https://developer.aliyun.com/article/1513245?spm=a2c6h.13148508.setting.26.f8774f0euyBLtl