前言

我们编写前端的时候肯定不只有一个页面,有多个页面的时候需要使用路由配置将用户引导到另一个页面,这时候就需要使用Router插件。Vue可以使用官方的VueRouter插件。

Vue Router | Vue.js 的官方路由 (vuejs.org)

快速上手

在终端输入:

1
npm install vue-router@4

package.json中可看到对应的版本信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "demo",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.4.21",
"vue-router": "^4.3.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.4",
"vite": "^5.2.0"
}
}

我们需要创建两个新文件夹,views用来存放页面组件,router用来存放路由信息,在Router文件夹中创建index.js,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import {createRouter, createWebHistory} from 'vue-router'

// 编写路由配置
const routes = [
{
path: '/',
component: () => import('../views/index.vue')
},
{
path: '/content',
component: () => import('../views/content.vue')
}
]


// 创建路由实例
const router = createRouter({
history: createWebHistory(),
routes
})

// 导出路由实例
export default router

routescomponent中所导入的就是views中的页面组件。对于createRouter中的history参数一般有createWebHistorycreateWebHashHistory(在链接后面多了个#)选择。

之后在main.js中导入:

1
2
3
4
5
6
7
8
9
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router/index.js"


const app = createApp(App)

app.use(router)
app.mount('#app')

最后在App.vue中添加一个组件:

1
2
3
4
5
6
7
8
9
10
11
<script setup>

</script>

<template>
<router-view/>
</template>

<style scoped>

</style>

运行项目,在http://localhost:5173显示的是index.vue,在http://localhost:5173/content/显示的是content.vue

配置路径别名 @

在实践中可以发现,我们使用src目录的次数是最为频繁的,为了省去麻烦,可以使用一个别名@来代替../

首先在项目的根目录文件夹下的vite.config.js中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
import path from 'path' // path来自node.js

// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
})

这时候就将src换成了@

这时候就可以将之前在index.js中编写的路径改为:

1
2
3
4
5
6
7
8
9
10
const routes = [
{
path: '/',
component: () => import("@/views/index.vue")
},
{
path: '/content',
component: () => import('@/views/content.vue')
}
]

但这时候发现使用@没有路径提示,在根目录下再创建一个jsconfig.json的文件,在文件中写入如下信息:

1
2
3
4
5
6
7
8
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"] // 配置 @ 符号指向 src 目录及其子目录
}
}
}

这样我们使用@的时候就有提示了。

查询字符串和路径传参

查询字符串传参是一个常见的传参方法,在链接之后添加?后传递参数,最经典的一个例子:https://vuejobs.com/?ref=vuejs,通过?向后端传递了ref:vuejs参数。

我们可以在对应页面的组件中使用$route.query.属性名获得它的值,如在index.js中:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>

</script>

<template>
<h3>主页</h3>
id:{{ $route.query.id }}
name:{{ $route.query.name }}
</template>

<style scoped>

</style>

访问http://localhost:5173/?id=1&name=gc,会看到页面显示出id和name。

路径传参也是常用的一个做法,比如微博的图片链接:https://wx4.sinaimg.cn/mw2000/008B0C4dly1himvdwnl32j31hc0sak6i.jpg。例如访问content页面需要传递id和title两个参数。

首先需要修改路由规则:

1
2
3
4
5
6
7
8
9
10
const routes = [
{
path: '/',
component: () => import("@/views/index.vue")
},
{
path: '/content/id/:id/title/:title',
component: () => import('@/views/content.vue')
}
]

在组件中需要通过$route.params.属性名来获得传递的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>

</script>

<template>
<h3>内容页</h3> <hr>
id:{{ $route.params.id }} <br>
name:{{ $route.params.title }}
</template>

<style scoped>

</style>

通过http://localhost:5173/content/id/1/title/vuerouter即可访问且看到传递的数据。

后续访问http://localhost:5173/content会出现报错vue-router.mjs:35 [Vue Router warn]: No match found for location with path "/content"

如果相设置非严格传递,可以在参数后添加?,这表示这个参数不是必须传递的:

1
2
3
4
5
6
7
8
9
10
const routes = [
{
path: '/',
component: () => import("@/views/index.vue")
},
{
path: '/content/id/:id/title/:title?',
component: () => import('@/views/content.vue')
}
]

路由别名 alias

顾名思义,就是给当前的路由添加一个新的链接,这两个链接访问的页面完全一致:

1
2
3
4
5
6
7
8
9
10
11
12
const routes = [
{
path: '/',
// 添加别名
alias: ['/home','index'],
component: () => import("@/views/index.vue")
},
{
path: '/content/id/:id/title/:title?',
component: () => import('@/views/content.vue')
}
]

之后无论是访问//home/index就都会是index.vue组件

定义路由名称 name

其实就是给路由设置一个唯一的名字,是为了方便后续使用router-link进行链接传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
const routes = [
{
path: '/',
// 添加别名
alias: ['/home','/index'],
component: () => import("@/views/index.vue")
},
{
path: '/content/id/:id/title/:title?',
name: 'post',
component: () => import('@/views/content.vue')
}
]

router-link是用来进行链接转移的,本质是生成一个a标签。

为了更好演示创建一个新的组件user.vue和新的路由规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const routes = [
{
path: '/',
// 添加别名
alias: ['/home','/index'],
component: () => import("@/views/index.vue")
},
{
path: '/content',
component: () => import('@/views/content.vue')
},
{
path: '/user/id/:id/name/:name?',
name: 'user',
component: () => import('@/views/user.vue'),
}
]

index.vue中创建router-link:

1
2
3
4
5
6
7
<template>
<h3>主页</h3>
id:{{ $route.query.id }}
name:{{ $route.query.name }} <br>
<router-link to="/content/?id=1&title=vue">内容页1</router-link> <br>
<router-link to="/user/id/1/name/张三">用户页:张三</router-link> <br>
</template>

点击后可以正常跳转,打开调试工具发现本质是生成a标签:

1
2
<a href="/content/?id=1&amp;title=vue" class="">内容页1</a>
<a href="/user/id/1/name/张三" class="">用户页:张三</a>

也可以将连接写成动态属性绑定的形式,得到的效果是一样的:

1
2
3
<router-link :to="{path:'/content',query:{id:2,title:'vuerouter'}}" >内容2</router-link> <br>

<router-link :to="{name:'user',params:{id:2,name:'李四'}}">用户页:李四</router-link> <br>

可以看到对于链接式传参,需要提供name,而不是path

也可以使用编程式的方式,使用useRouter进行跳转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script setup>
import { useRouter } from 'vue-router';
const route = useRouter();

const goTo1 =()=>{
route.push({path:'/content',query:{id:1,title:'vue'}}); // 跳转到内容页
}

const goTo2 =() => {
route.push({name:'user',params:{id:1,name:'张三'}}); // 跳转到用户页
}

</script>

<template>
<h3>主页</h3>
id:{{ $route.query.id }}
name:{{ $route.query.name }} <br>
<router-link to="/content/?id=1&title=vue">内容页1</router-link> <br>
<router-link to="/user/id/1/name/张三">用户页:张三</router-link> <br>

<router-link :to="{path:'/content',query:{id:2,title:'vuerouter'}}" >内容2</router-link> <br>

<router-link :to="{name:'user',params:{id:2,name:'李四'}}">用户页:李四</router-link> <br>

<button @click="goTo1">跳转到内容页</button> <br>
<button @click="goTo2">跳转到用户页</button>
</template>

<style scoped>

</style>

嵌套路由 children

新建一个vip文件夹,在文件夹下创建default.vueinfo.vueorder.vue,并在src目录下新建一个vip.vue作为所有组件的父组件。等于新建了一个完整的新页面,这个页面下有很多其他功能需要实现,那就需要使用嵌套路由。

首先新建路由匹配,需要定义子路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 编写路由配置
const routes = [
{
path: '/', // http://localhost:5173/
// 添加别名
alias: ['/home','/index'],
component: () => import("@/views/index.vue")
},
{
path: '/content', // http://localhost:5173/content
component: () => import('@/views/content.vue')
},
{
path:"/vip", // http://localhost:5173/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')
},
]
}
]

需要在vip.vue编写如下代码,使用 <router-view/>进行渲染:

1
2
3
4
5
6
7
8
9
10
11
12
<script setup>
import Header from '@/components/header.vue';
import Footer from '@/components/footer.vue';
</script>

<template>
<Header />
<router-view/>
<Footer />
</template>

<style scoped></style>

其中的<Header /><Footer />是可以直接继承到子页面的,不需要每个组件都进行导入。

重定向 redirect

只需要在路由配置的时候添加redirect属性

1
2
3
4
5
{
path : '/svip',
// redirect: '/vip', 跳转到http://localhost:5173/vip
redirect: {path:'/content',query:{id:2,title:'vuerouter'}}, // 跳转到http://localhost:5173/content?id=2&title=vuerouter
}

之后访问/svip就会跳转到/vip,如果想要传递参数重定向,可以参考上述router-link中的:to的写法。

全局前置守卫

后端一般叫它全局中间件,是用来拦截请求的,全局的意思是在整个项目中进行拦截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router/index.js"


const app = createApp(App)

router.beforeEach((to, from, next) => {
console.log('to:', to) // to:要去的路由
console.log('from:', from) // from:从哪来的路由

if (to.path === '/vip/') {
console.log('需要登录')
next(false) // 拦截
} else if (to.name === 'content') {
next({ path: '/vip' }) // 跳转到http://localhost:5173/vip
}else{
next() // 放行
}
})

app.use(router)
app.mount('#app')

一般使用pathname进行判断