前言
我们编写前端的时候肯定不只有一个页面,有多个页面的时候需要使用路由配置将用户引导到另一个页面,这时候就需要使用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
|
routes
的component
中所导入的就是views
中的页面组件。对于createRouter
中的history
参数一般有createWebHistory
和createWebHashHistory
(在链接后面多了个#)选择。
之后在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'
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/*"] } } }
|
这样我们使用@
的时候就有提示了。
查询字符串和路径传参
查询字符串传参是一个常见的传参方法,在链接之后添加?后传递参数,最经典的一个例子: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
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&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.vue
、info.vue
、order.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: '/', alias: ['/home','/index'], component: () => import("@/views/index.vue") }, { path: '/content', component: () => import('@/views/content.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') }, ] } ]
|
需要在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: {path:'/content',query:{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) console.log('from:', from)
if (to.path === '/vip/') { console.log('需要登录') next(false) } else if (to.name === 'content') { next({ path: '/vip' }) }else{ next() } })
app.use(router) app.mount('#app')
|
一般使用path
和name
进行判断