vue-router(vue 路由)基本使用指南(一)
概述
客户端 vs 服务端路由
-
服务端路由:
服务器根据用户访问的 URL 路径返回不同的响应结果。当在一个传统的服务端渲染的 web 应用中点击一个链接时,浏览器会从服务端获得全新的 HTML,然后重新加载整个页面。
-
客户端路由:
在单页面应用中,客户端的 JavaScript 可以拦截页面的跳转请求,动态获取新的数据,然后在无需重新加载的情况下更新当前页面。这样通常可以带来更顺滑的用户体验,尤其是在更偏向“应用”的场景下,因为这类场景下用户通常会在很长的一段时间中做出多次交互。
在这类单页应用中,“路由”是在客户端执行的。一个客户端路由器的职责就是利用诸如 History API 或是 hashchange 事件这样的浏览器 API 来管理应用当前应该渲染的视图。
客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。
Vue Router 介绍
Vue Router 是 Vue 官方的客户端路由解决方案,与 Vue.js 核心深度集成,用于构建单页面应用(SPA)。
Vue Router 基于 Vue 的组件系统构建,可以通过配置路由来告诉 Vue Router 为每个 URL 路径显示哪些组件。
功能包括:
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue.js 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
Vue Router 基础概念
-
路由器(router):管理所有路由的中央控制器
功能包括:
- 监听URL的变化
- 根据定义的 路由规则,映射到对应的组件。
- 提供导航方法,控制页面跳转。
-
路由(route):表示一个 URL 与组件的映射关系
一个典型的路由定义包括:
path
:URL当中的路径部分compontent
:当路径匹配时要渲染的组件
-
路由视图 (
<router-view>
):一个占位组件,告诉 Vue Router 在这里渲染匹配到的组件。 -
路由链接 (
<router-link>
):声明式的导航组件(替代传统的<a>
标签)用于创建导航链接,点击后会改变 URL,并触发路由跳转(不会触发页面刷新,只更新匹配的组件)
快速入门
安装 Vue Router
安装 Vue Router
-
对于一个现有的使用 JavaScript 包管理器的项目,可以从 npm registry 中安装 Vue Router:
# npm npm install vue-router@4# npm yarn add vue-router@4# pnpm pnpm add vue-router@4
以上命令三选一
-
如果打算启动一个新项目,使用 create-vue 这个脚手架工具更容易,它能创建一个基于 Vite 的项目,并包含加入 Vue Router 的选项:
# npm npm create vue@latest# yarn yarn create vue# pnpm pnpm create vue
以上命令三选一
创建路由器实例
路由器实例是通过调用 createRouter() 函数创建:
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'const router = createRouter({history: createWebHistory(),routes = [{ path: '/', component: HomeView },{ path: '/about', component: () => import('./AboutView.vue') },],
})
-
routes 选项:定义了一组路由,把 URL 路径映射到组件。
-
component 参数:指定组件,这些路由组件通常被称为视图,但本质上它们只是普通的 Vue 组件。
其他可以设置的路由选项后面介绍,基础的只需要
path
和component
。 -
history 选项:控制路由和 URL 路径是如何双向映射的。
常用
createWebHistory()
或createWebHashHistory()
注册路由器插件
一旦创建了路由器实例,就需要将其注册为插件,这一步骤可以通过调用 use()
来完成。
main.js
createApp(App).use(router).mount('#app')// 或等价地:const app = createApp(App)
app.use(router)
app.mount('#app')
- 和大多数的 Vue 插件一样,
use()
需要在mount()
之前调用。 use(router)
插件的职责包括:- 全局注册 RouterView 和 RouterLink 组件。
- 添加全局
$router
和$route
属性。 - 启用
useRouter()
和useRoute()
组合式函数。 - 触发路由器解析初始路由。
使用路由组件
App.vue
<template><h1>Hello App!</h1><p><strong>Current route path:</strong> {{ $route.fullPath }}</p><nav><RouterLink to="/">Go to Home</RouterLink><RouterLink to="/about">Go to About</RouterLink></nav><main><RouterView /></main>
</template>
在这个 template
中使用了两个由 Vue Router 提供的组件: RouterLink
和 RouterView
。
-
RouterLink 组件:不同于常规的
<a>
标签,使用组件RouterLink
创建链接使得 Vue Router 能够在不重新加载页面的情况下改变 URL,处理 URL 的生成、编码和其他功能。 -
RouterView 组件:使 Vue Router 知道在哪里渲染当前 URL 路径对应的路由组件。
它不一定要在
App.vue
中,可以把它放在任何地方,但它需要在某处被导入,否则 Vue Router 就不会渲染任何东西。 -
$route
:在组件模板中可使用的当前的路由对象。
访问路由器和当前路由
如果是从 ES 模块导出路由器实例的,则可以将路由器实例直接导入到需要它的地方。在一些情况下这是最好的方法,但如果在组件内部,那么还有其他选择。
-
在选项式 API 中,可以在 JavaScript 中如下访问这两个属性:
this.$router
和this.$route
。export default {methods: {goToAbout() {this.$router.push('/about')},}, }
-
在组合式 API 中,不能通过
this
访问组件实例,而是使用 Vue Router 提供的 useRouter() 和 useRoute() 来访问路由器实例和当前路由。<script setup> import { computed } from 'vue' import { useRoute, useRouter } from 'vue-router'const router = useRouter() const route = useRoute()const search = computed({get() {return route.query.search ?? ''},set(search) {router.replace({ query: { search } })}, }) </script>
基础
懒加载路由
通过动态导入实现路由组件的懒加载:
const routes = [{path: '/about',component: () => import('./components/About.vue'),},
];
命名路由
当创建一个路由时,我们可以选择给路由一个 name
,然后可以使用 name
而不是 path
来传递 to
属性。
所有路由的命名都必须是唯一的。如果为多条路由添加相同的命名,路由器只会保留最后那一条。
-
路由配置
const routes = [{path: '/user/:id',name: 'user', // 命名路由component: User,}, ];
-
导航到命名路由
this.$router.push({ name: 'user', params: { id: 123 } });
使用 name
的优点:
- 没有硬编码的 URL。
params
的自动编码/解码。- 防止你在 URL 中出现打字错误。
- 绕过路径排序,例如展示一个匹配相同路径但排序较低的路由。
命名视图
可以在界面中拥有多个单独命名的视图(同时 /同级展示多个视图,而不是嵌套展示),而不是只有一个单独的出口。
例如创建一个布局,有 sidebar
(侧导航) 和 main
(主内容) 两个视图,如果 router-view
没有设置名字,那么默认为 default
。
-
路由配置
const routes = [{path: '/views',components: {default: ViewMain, // 默认视图绑定的组件sidebar: ViewSidebar, // 命名视图绑定的组件。若视图名和组件名相同,可以缩写为:ViewSidebar,},}, ];
-
组件模板
<template><router-view></router-view> <!-- 默认视图 --><router-view name="sidebar"></router-view> <!-- 命名视图 --> </template>
嵌套命名视图
若想使用命名视图创建嵌套视图的复杂布局,也需要命名用到的嵌套 router-view
组件
-
路由配置
{path: '/settings',// 也可以在顶级路由就配置命名视图component: UserSettings,children: [{path: 'emails',component: UserEmailsSubscriptions}, {path: 'profile',components: {default: UserProfile,helper: UserProfilePreview}}] }
-
组件模板
<!-- UserSettings.vue --> <div><h1>User Settings</h1><NavBar /><router-view /><router-view name="helper" /> </div>
Nav
只是一个常规组件。UserSettings
是一个视图组件。UserEmailsSubscriptions
、UserProfile
、UserProfilePreview
是嵌套的视图组件。
编程式导航
除了使用 <router-link>
创建 a 标签来定义导航链接,还可以借助 router(路由器)的实例方法,通过编写代码来实现。
在组件内部,获取路由器实例:
- 选项式 API:使用
$router
属性访问路由器,例如this.$router.push(...)
。 - 组合式 API:通过调用
useRouter()
方法获取路由器实例。
router.push():导航到不同的 URL
该方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。
当点击 <router-link>
时,内部调用也是这个方法,即点击 <router-link :to="...">
相当于调用 router.push(...)
:
方法的参数:
-
可以是一个字符串路径,或者一个描述地址的对象。例如:
// 字符串路径 router.push('/users/eduardo') // 带有路径的对象 router.push({ path: '/users/eduardo' }) // 带查询参数,结果是 /register?plan=private router.push({ path: '/register', query: { plan: 'private' } }) // 带 hash,结果是 /about#team router.push({ path: '/about', hash: '#team' }) // 命名的路由,并加上参数,让路由建立 url router.push({ name: 'user', params: { username: 'eduardo' } })
注意:
-
params
不能与path
一起使用。如果提供了path
,则params
会被忽略。如果非要使用path
,则需要直接将params
的值填充进path
中:const username = 'eduardo' // 手动建立 url,但必须自己处理编码 router.push(`/user/${username}`) // -> /user/eduardo router.push({ path: `/user/${username}` }) // -> /user/eduardo
-
当指定 params 时,可提供 string 或 number 参数(或者对于可重复的参数可提供一个数组)。
任何其他类型(如对象、布尔等)都将被自动字符串化。
-
对于可选参数,可以提供一个空字符串(“”)或 null 来移除它。
-
由于属性 to 与 router.push() 接受的对象种类相同,所以两者的规则完全相同。
-
router.replace():替换当前位置
该方法的作用类似于 router.push()
,唯一不同的是,它在导航时不会向 history 添加新记录,它取代了当前的条目。
-
也可以直接在传递给
router.push()
的to
参数中增加一个属性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)
路由传参
在 Vue 3 应用中,路由传参是实现页面间数据传递的核心功能。Vue Router 4 提供了多种传参方式,适用于不同的场景。
params 传参(路径参数)
带参数的动态路由匹配,在 Vue Router 中,可以在路径中使用一个动态字段来实现,称之为 路径参数
路径参数 用冒号 :
表示。当一个路由被匹配时,它的 params
的值将在每个组件中以 route.params
的形式暴露出来。
使用示例:
-
路由配置
// 路由配置 const routes = [{path: '/user/:id', // 动态字段以冒号开始name: 'user', // 使用params时,必须搭配name使用。如果提供的path,则params会被忽略component: User} ]
-
同一个路由中可以设置多个路径参数,多个参数之间通过斜杠
/
分隔,它们会映射到$route.params
上的相应字段匹配模式 匹配路径 route.params /user/:id /user/123 { id: '123' }
/user/:id/:username /user/123/zhangsan { id: '123', username: 'zhangsan' }
可选参数:通过在参数后添加
?
标记,使其成为可选参数。 -
{ path: '/user/:age?', component: User }
-
-
传递参数
<template><!-- 通过路由链接 --><router-link :to="{ name: 'user', params: { id: 123 }}">用户</router-link> </template><script>// 选项式APIexport default {methods: {goUserDetail() {// 通过编程式导航this.$router.push({ name: 'UserDetail', params: { id: 123 } });},},}; </script><script setup>// 组合式APIimport { useRoute } from 'vue-router';const route = useRoute();function goUserDetail() {// 通过编程式导航route.push({ name: 'UserDetail', params: { id: 123 } });} </script>
-
获取参数
<script setup>import { useRoute } from 'vue-router';const route = useRoute();console.log(route.params.id); // 输出: 123// 可选参数获取const userAge = route.params.age || 'defaultId'; // 提供默认值console.log(userAge); // 输出: "12" 或 "defaultId" </script>
query 传参(查询参数)
查询参数通过 URL 的查询字符串传递,适合传递可选参数、过滤条件等。
查询参数不会影响路由匹配,适合频繁变化的参数。
-
传递参数
<template><!-- 通过路由链接 --><router-link :to="{ name: 'UserList', query: { page: 2, size: 10 } }">第二页</router-link> </template><script setup>// 组合式APIimport { useRoute } from 'vue-router';const route = useRoute();function goUserDetail() {// 通过编程式导航route.push({ name: 'UserList', query: { page: 2, size: 10 } });} </script>
获取参数
<script setup>import { useRoute } from 'vue-router';const route = useRoute();console.log(route.query.page); // 2console.log(route.query.size); // 10 </script>
props 传参
使用 props
选项将路由参数解耦为组件的普通 props
,提高组件的复用性。
-
路由配置
方式1:布尔模式
const routes = [{path: '/user/:id',name: 'user',component: User,props: true // 当props设置为true时,route.params将被设置为组件的props},// 有命名视图的路由,必须为每个命名视图定义 props 配置{path: '/user/:id',components: { default: User, sidebar: Sidebar },props: { default: true, sidebar: false }}, ]
方式2:函数模式
可以创建一个返回 props 的函数。这允许将参数转换为其他类型,将静态值与基于路由的值相结合等
const routes = [{path: '/user/:id/:age',name: 'user2',component: User,props: (route) => ({ id: route.params.id, age: route.params.age }) // params传参},{path: '/about',name: 'about',component: About,props: (route) => ({ age: route.query.age }) // query传参}, ]
方式3:对象模式
当
props
是一个对象时,它将原样设置为组件 props。当 props 是静态的时候很有用。const routes = [{path: '/profile',name: 'profile',component: Profile,props: { isStudent: false } // 对象形式} ]
-
获取参数
<script>// 选项式APIexport default {props: ['id', 'age', 'isStudent'],setup(props) {console.log(props.id)}} </script><script setup>// 组合式APIconst props = defineProps<{id: number;age: number;isStudent: boolean;}>(); </script>