Vue3简单快速基础入门 (四)

Vue Router 快速掌握

1. Vue Router

Vue Router 是 Vue 官方的客户端路由解决方案。
客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。
Vue Router 基于 Vue 的组件系统构建,你可以通过配置路由来告诉 Vue Router 为每个 URL 路径显示哪些组件。
Vue-Router官方网址

1.1 安装

新建一个Vue项目,创建views文件夹,并创建显示页面(自定义即可,方便路由显示),在项目目录终端下输入命令,安装Vue Router:

npm install vue-router@4

安装完成后,package.json 文件中会自动添加 vue-router 依赖。这里版本为"vue-router": "^4.5.0"

1.2 配置使用

首先创建router文件夹,在router文件夹下新建一个index.js文件,内容如下:

// 导入createRouter和createWebHistory方法
// createRouter用于创建路由实例,createWebHistory用于使用HTML5 History API进行路由管理
import { createRouter, createWebHistory } from 'vue-router'

// 设置路由规则
// routes是一个数组,包含多个路由对象,每个对象定义了一个路由的路径和对应的组件
const routes = [
    {
        // 访问路由路径为根路径 '/'
        path: '/',
        // 对应组件为懒加载的index.vue组件
        // 懒加载:只有在组件被访问时才会加载该组件,有助于提高应用的初始加载速度
        component: () => import('../views/index.vue')
    },
    {
        // 访问路由路径为'/content'
        path: '/content',
        // 对应组件为懒加载的content.vue组件
        component: () => import('../views/content.vue')
    }
]

// 创建路由实例
// 通过createRouter方法创建router实例,并传入配置对象
const router = createRouter({
    // 使用createWebHistory方法创建一个history模式的导航实例
    // history模式:利用HTML5 History API使得URL看起来像标准的网站URL
    history: createWebHistory(),
    // 将之前定义好的路由规则数组传入
    routes
})


// 将router实例导出
export default router

main.js文件中导入router实例,并挂载到Vue实例上:

import { createApp } from 'vue'
import App from './App.vue'
import router from '../router/index.js'


// 将router实例注册到Vue实例上
createApp(App).use(router).mount('#app')

最后,在App.vue文件中使用<router-view>组件来渲染路由匹配到的组件:

<script setup>

</script>

<template>
  <!-- 根据路由规则,渲染对应的组件 -->
  <router-view />
</template>

<style scoped>

</style>

注意:在 Vue Router 中,history 选项支持三种不同的模式,分别是 createWebHistorycreateWebHashHistorycreateMemoryHistory,下面详细介绍它们的特点和作用:

  • createWebHistory:传统模式,在这种模式下,URL 看起来就像标准的网站 URL,没有 # 符号,更符合用户对传统网站 URL 的认知,提升了 URL 的可读性和美观度。
  • createWebHashHistory:哈希模式,该模式使用 URL 中的哈希值(#)来实现路由导航。当 URL 中的哈希值发生变化时,浏览器不会向服务器发送新的请求,而是触发 Vue Router 的路由变化,从而实现单页面应用的导航。
  • createMemoryHistory:内存模式,该模式将路由的历史记录保存在内存中,不与浏览器的历史记录进行交互。这种模式通常用于非浏览器环境,如 Node.js 服务器端渲染(SSR)、测试环境等。

2. 配置路径别名@和VSCode路径提示

router/index.js文件中,我们使用了import '../views/index.vue'来导入index.vue组件,但是经常会看到别的vue项目中,使用的是@,也就是路径别名。
接下来,我们通过配置,从而使用@

2.1 配置路径别名

配置路径别名,在vite.config.js文件中,代码如下:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// 引入 path 模块
import path from 'path'

export default defineConfig({
    plugins: [vue()],
    // 解析配置,主要用于设置路径别名等
    resolve: {
        // 别名配置对象,允许我们使用简化的路径来引用文件
        alias: {
           // `path.resolve(__dirname, './src')` 是 Node.js 的 `path` 模块中的方法
            // `__dirname` 表示当前文件所在的目录,`path.resolve` 会将其和 `./src` 拼接成一个绝对路径
            // 设置路径别名 '@' 指向 '/src' 目录,这样在引入文件时可以使用 '@' 来代替 '/src'
            '@': path.resolve(__dirname, './src')
        }
    }
})

接着就可以使用@去导入组件了,比如在router/index.js文件中,我们可以这样写:

const routes = [
    {
        // 访问路由路径为根路径 '/'
        path: '/',
        // 对应组件为懒加载的index.vue组件
        // 懒加载:只有在组件被访问时才会加载该组件,有助于提高应用的初始加载速度
        component: () => import('@/views/index.vue')
    },
    {
        // 访问路由路径为'/content'
        path: '/content',
        // 对应组件为懒加载的content.vue组件
        component: () => import('@/views/content.vue')
    }
]

接下来终端输入npm run dev启动项目,访问对应路由路径,可以看到正常访问。

2.2 VSCode路径提示

上面使用了路径别名@来替换原来的路径方式,但是发现在VSCode中,使用@方式,并没有路径提示,所以接下来就对路径提示进行配置。


因为创建vue项目时,选择的语言为js,所以创建文件jsconfig.json文件,内容如下:

{
    "compilerOptions":{
        "baseUrl": ".",
            "paths": {
            "@/*": ["src/*"] // 配置 @ 符号指向 src 目录及其子目录
        }
    }
}

这样,VSCode中,使用@符号,就会有路径提示功能。

3. 使用查询字符串或路径传递参数

在Vue Router中,可以通过query属性来传递查询字符串参数,通过params属性来传递路径参数。

3.1 传递查询字符串参数

查询字符串,也就是路径上的?后面的参数,可以通过query属性来传递。例如:http://localhost:5173/content?id=100&name=十一月
然后,可以在content路由对应的组件content.vue中,通过$route.query来获取查询字符串参数:

<script setup>

</script>
<template>
    <h1>这里是内容页-content.vue</h1>
    <h1>id: {{ $route.query.id }}</h1>
    <h1>name: {{ $route.query.name }}</h1>

</template>

<style>

</style>

注意$route是一个全局属性,所以不需要引入,直接使用即可。

3.2 传递路径参数

传递路径参数,也就是路径中的参数,可以通过params属性来传递。例如:http://localhost:5173/content/100/name/十一月
需要先修改路由规则,确保路由规则和路径参数匹配,在router/index.js中修改:

//...
const routes = [
    {
        // 修改路由规则,设置路径参数
        path: '/content/:id/name/:name',
        component: () => import('@/views/content.vue')
    }
]
//...

然后,可以在content路由对应的组件content.vue中,通过$route.params来获取路径参数:


但是路径参数有一个问题,就是参数必须和路径规则匹配,如果我们不传id或name,那么就会提示没有匹配的路由,但有些时候,参数是可选的,我们希望可以不传参数,那么怎么办呢?
其实也很简单,在路由参数后使用?即可,表示该路由参数可选:

//...
const routes = [
    {
        // 修改路由规则,设置路径参数   并且 name 参数可选
        path: '/content/:id/name/:name?',
        component: () => import('@/views/content.vue')
    }
]
//...

router/index.js文件中,我们定义了路由规则,规则中,使用了path去指定了路由路径,component属性来指定了路由对应的组件。

4.1 定义别名

接下来,学习使用alies去指定路由别名,可以允许有多个别名。通过访问别名,也能够正常去访问对应的组件。

//...
const routes = [
    {
        path: '/',
        // 设置别名为/index
        // alias: '/index',
        // 设置多个别名 使用数组
        alias:["/index","/home"],
        component: () => import('@/views/index.vue')
    }
]
    // ...

router-link 组件是 Vue Router 提供的组件,可以用来创建路由链接。它可以绑定到路由的路径,当点击时,会导航到对应的路由。其实也就是相当于一个a标签,但是可以绑定路由。

<!-- index.vue中编写 -->
<script setup>

</script>
<template>
    <h1>这里是首页-index.vue</h1>
    <router-link to="/content/100/name/十一月">Router-Link路径传递参数</router-link>
    <br>
    <router-link to="/user?id=200&url=https://blog.xy21lin.cn">Router-Link传递查询参数</router-link>
</template>

<style>

</style>

4.3 路由名称

index.js文件定义的路由规则中,添加name属性,可以给路由规则定义一个名称,以此来解决动态绑定router-link路径传递参数并方便后续编程式导航。

const routes = [
    {
        path: '/content/:id/name/:name',
        // 设置路由名称,解决动态绑定router-link路径传递参数  路由不匹配问题
        name: 'content',
        component: () => import('@/views/content.vue')
    },
    {
        path:'/user',
        component: () => import('@/views/user.vue')
    }
]
<script setup>

</script>
<template>
    <h1>这里是首页-index.vue</h1>
    <!-- 这样子动态绑定传递,发现没有匹配的content路由,是因为index.js中,设置的路由规则是完全匹配了参数的,所以需要定义路由名称,也就是去设置一个name属性 -->
    <!-- <router-link :to="{path:'/content',params:{id:100,name:'十一月'}}">Router-Link路径传递参数-动态绑定</router-link> -->
    <router-link :to="{name:'content',params:{id:100,name:'十一月'}}">Router-Link路径传递参数-动态绑定</router-link>
    <br>
    <router-link :to="{path:'/user',query:{id:200,url:'https://blog.xy21lin.cn'}}">Router-Link传递查询参数-动态绑定</router-link>
    
</template>

<style>

</style>

4.4 编程式导航

编程式导航,就是通过代码来控制路由的跳转,而不是通过浏览器的前进、后退按钮。通过router.push()方法可以实现编程式导航。

<script setup>
    import {useRouter} from 'vue-router'

    const router = useRouter()

    let id = 888
    let name = "编程式导航"
    let url = "https://blog.xy21lin.cn"

    //定义跳转方法
    const gotoContent = ()=>{
        // router.push("/content/999/name/编程式导航")
        router.push({name:"content",params:{id:id,name:name}})
    }

    const gotoUser = () =>{
        // router.push("/user?id=200&url=https://blog.xy21lin.cn")
        router.push({path:"/user",query:{id:id,url:url}})
    }

</script>
<template>
    <h1>这里是首页-index.vue</h1>
    <router-link to="/content/100/name/十一月">Router-Link路径传递参数</router-link>
    <br>
    <router-link to="/user?id=200&url=https://blog.xy21lin.cn">Router-Link传递查询参数</router-link>
    <hr>
    <!-- 这样子动态绑定传递,发现没有匹配的content路由,是因为index.js中,设置的路由规则是完全匹配了参数的,所以需要定义路由名称,也就是去设置一个name属性 -->
    <!-- <router-link :to="{path:'/content',params:{id:100,name:'十一月'}}">Router-Link路径传递参数-动态绑定</router-link> -->
    <router-link :to="{name:'content',params:{id:100,name:'十一月'}}">Router-Link路径传递参数-动态绑定</router-link>
    <br>
    <router-link :to="{path:'/user',query:{id:200,url:'https://blog.xy21lin.cn'}}">Router-Link传递查询参数-动态绑定</router-link>
    <hr>
    <button @click="gotoContent()">跳转到内容页</button>
    <button @click="gotoUser()">跳转到用户页</button>
</template>

<style>

</style>

5. 嵌套路由结合共享组件

嵌套路由,就是在一个路由下,再定义子路由,这样子,就可以实现多级路由。例如定义了一个路由vip,再在vip下定义order,此时order路由也就是/vip/order
共享组件,就是在多个路由下,共用一个组件,这样子,就可以实现组件的复用。


接下来,我们来实现嵌套路由和共享组件。首先创建一个vip.vue,以及vip文件夹,并在内创建order.vueinfo.vuedefault.vue三个组件。以及在components文件夹下创建header.vuefooter.vue两个组件,作为公共组件。

<!-- header.vue -->
<script setup>

</script>
<template>
    <h1>这里是Header-header.vue</h1>
</template>

<style>

</style>

<!-- footer.vue -->
 <script setup>

</script>
<template>
    <h1>这里是Footer-footer.vue</h1>
</template>

<style>

</style>

<!-- default.vue -->
 <script setup>

</script>
<template>
    <h1>这里是会员默认页</h1>
</template>

<style>

</style>

<!-- order.vue -->
 <script setup>

</script>
<template>
    <h1>这里是会员订单页</h1>
</template>

<style>

</style>

<!-- info.vue -->
 <script setup>

</script>
<template>
    <h1>这里是会员信息页</h1>
</template>

<style>

</style>

<!-- vip.vue -->
<script setup>
    import Header from '@/components/header.vue'
    import Footer from '@/components/footer.vue'
    
</script>
<template>
    <!-- 使用公共组件Header和Footer -->
    <Header />
    <!-- 渲染子路由,不渲染就无法显示子路由页面内容 -->
    <router-view />
    <Footer />
</template>

<style>

</style>

然后在router/index.js文件中,定义vip路由,并在children属性中定义子路由:

//...
const routes = [
    {
        // 访问路由路径为根路径 '/'
        path: '/',
        // alias: '/index',
        alias:["/index","/home"],
        component: () => import('@/views/index.vue')
    },
    {
        path: '/content/:id/name/:name',
        name: 'content',
        component: () => import('@/views/content.vue')
    },
    {
        path:'/user',
        component: () => import('@/views/user.vue')
    },
    {
        path:"/vip",
        component: () => import('@/views/vip.vue'),
        // 定义子路由
        children:[
            {
                path:'',       // http://localhost:5173/vip
                component: () => import('@/views/vip/default.vue')
            },
            {
                path:'order',  // http://localhost:5173/vip/order
                component: () => import('@/views/vip/order.vue')
            },
            {
                path:'info',   // http://localhost:5173/vip/info
                component: () => import('@/views/vip/info.vue')
            }
        ]
    }
]
//...

6. 重定向

重定向,就是当访问一个路由时,根据设定的重定向规则,就自动跳转到另一个路由。
例如,我们定义了一个路由/svip,但是我们希望用户访问/svip时,自动重定向跳转到/vip

const routes = [
    {
        // 访问路由路径为根路径 '/'
        path: '/',
        alias:["/index","/home"],
        component: () => import('@/views/index.vue')
    },
    {
        path: '/content/:id/name/:name',
        name: 'content',
        component: () => import('@/views/content.vue')
    },
    {
        path:'/user',
        component: () => import('@/views/user.vue')
    },
    {
        path:"/vip",
        component: () => import('@/views/vip.vue'),
        // 定义子路由
        children:[
            {
                path:'',
                component: () => import('@/views/vip/default.vue')
            },
            {
                path:'order',
                component: () => import('@/views/vip/order.vue')
            },
            {
                path:'info',
                component: () => import('@/views/vip/info.vue')
            }
        ]
    },
    {
        path:'/svip',
        // 实现重定向
        redirect:'/vip'
    }
]

当访问/svip时,会自动跳转到/vip,接下来试一试,可不可以实现编程式导航一样传递到项目

const routes = [
    {
        // 访问路由路径为根路径 '/'
        path: '/',
        alias:["/index","/home"],
        component: () => import('@/views/index.vue')
    },
    {
        path: '/content/:id/name/:name',
        name: 'content',
        component: () => import('@/views/content.vue')
    },
    {
        path:'/user',
        component: () => import('@/views/user.vue')
    },
    {
        path:"/vip",
        component: () => import('@/views/vip.vue'),
        // 定义子路由
        children:[
            {
                path:'',
                component: () => import('@/views/vip/default.vue')
            },
            {
                path:'order',
                component: () => import('@/views/vip/order.vue')
            },
            {
                path:'info',
                component: () => import('@/views/vip/info.vue')
            }
        ]
    },
    {
        path:'/svip',
        // 实现重定向
        // redirect:'/vip'
        // 要实现编程式导航一样传递到项目 也就是类似这样子 router.push({name:"content",params:{id:id,name:name}})
        redirect: {name:"content",params:{id:99,name:"十一月"}}
    }
]

再次访问/svip时,会自动跳转到/content/99/name/十一月,并显示内容页。说明是可以正常进行重定向的。

7. 全局前置守卫

全局前置守卫,就是在所有路由跳转前,都会执行一段代码,比如检查是否登录,是否有权限等。
我们在main.js文件中,定义全局前置守卫:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index.js'

// 配置路由前置守卫
router.beforeEach((to, from, next) => {
    // to 即将进入的路径信息
    // from 即将离开的路径信息
    console.log(to)
    console.log(from)

    if(to.name == "content"){
        // 如果跳转路由名称为 content 就进行拦截
        // next(false)
        // 如果跳转路由名称为 content 就进行重定向到vip
        next('/vip')
    }else{
        next()
    }
})

// 将router实例注册到Vue实例上
createApp(App).use(router).mount('#app')

在前置守卫中,对路由名称content的路由进行了拦截,这样子就不会进行跳转,在实际项目中,需要根据实际情况进行拦截、重定向等操作,可以根据实际情况进行操作。

注意:除了全局前置守卫外,还有全局解析守卫、全局后置钩子等,前置守卫最多,剩余内容可以自行查阅官方文档。