vue-router的使用
基于vite安装和使用vue-router
vue-router是什么
vue-router
是基于路由
和组件
的, 路由
是用来设定访问路径, 将路径
和组件
映射起来。
vue-router基本使用
01~定义路由文件并导出
src文件夹
下面创建一个router文件夹, 写一个index.js
, 即src/router/index.js
放置路由文件, 对路由进行集中管理。
src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'
/**
* 为了规范管理,常用的目录结构
* 1. src下面创建一个router文件夹, 写一个index.js
* 即src/router/index.js 放置路由文件, 对路由进行集中管理
*
*
* 2. src下面新建一个views的文件夹, 对应的路由组件都放在里面
*
*/
/**
* 1. 定义路由组件, 也可以从其他文件导入
*
* 导入组件, 组件后面的.vue可以要, 可以不要
* 比如 import Home from '../views/Home' 也是可以的
*/
import Home from '../views/Home.vue'
import About from '../views/About.vue'
/**
* 2. 定义一些路由
* 每个路由都需要映射到一个组件
* 点击哪一个路由, 就对应哪一个组件
* 我们后面再套路嵌套路由
*
* 路由表
*/
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
/**
* 3. 创建路由实例并传递 上面的 'routers'配置
* 你可以在这里输入更多的配置, 但我们在这里
* 暂时保持简单
*
* 官网示例中有VueRouter.createRouter, 是因为它是
* cdn引入的; npm安装的, 可以直接使用它的方法
*
* 方法createRouter和createWebHashHistory都是VueRouter
* 里面引入进来的
*
*/
const router = createRouter({
/**
*4. 内部提供了 history 模式的实现,\;
* 为了简单起见, 我们在这里使用hash模式
* 路由有两种模式, 一种是hash模式, 一种是history模式
*/
history: createWebHashHistory(),
routes, // 'routes:routes' 的简写 配置路由表
})
// 路由文件写好之后, 怎么使用呢? 需要导出, 然后去main.js里面配置
export default router
02~入口main.js中引入并使用路由文件
src/main.js
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
// 引入了App组件
/**
* 引入路由文件, 注意, 如果from后面是 './router',
* 其实默认的是去找里面的index文件,就是'./router/index.js'
*/
import router from './router'
const app = createApp(App)
app.use(router) // 使用路由要在挂载app前面
app.mount('#app')
03~src创建路由组件
src下面新建一个views的文件夹, 对应的路由组件都放在里面
04~模版中使用路由
- 使用
router-link
组件进行导航, 通过传递to
来指定链接 <router-view></router-view>
渲染路由匹配到的组件内容
<script setup>
import Content from './components/Content.vue'
</script>
<template>
<!--
vue-router是基于`路由`和`组件`的, `路由`是用来设定访问路径,
将`路径`和`组件`映射起来。
-->
<Content />
<!--
使用 router-link 组件进行导航
通过传递 to 来指定链接
<router-link> 将呈现一个带有正确 href 属性的 a 标签
使用一个自定义组件 router-link 来创建链接; 这使得Vue Router可以在不重新
加载页面的情况下更改Url, 处理Url的生成及编码。
to="路径"
-->
<p>
<router-link to="/">Go to Home</router-link>
</p>
<p>
<router-link to="/about">Go to About</router-link>
</p>
<!-- 路由出口,占位符 -->
<!-- 路由匹配到的组件将渲染在这里, 组件内容展示的位置
上面点击Home, 就展示Home的内, 点击About就展示About的内容
-->
<router-view></router-view>
</template>
<style></style>
带参数的动态路由匹配
很多时候, 我们需要将
给定匹配模式
的路由映射到同一个组件
,; 例如: 我们有一个user
组件, 它显示用户的详情; 但用户ID
不同, 显示的详情内容自然不同, 这个时候就需要动态传一个ID
参数。
在Vue Router
中, 我们在路径中使用一个动态字段
来实现, 我们称之为路径参数
。
index.js路由文件中
const routes = [
/**
* 动态字段以冒号开始
* 路径参数用冒号 `:` 表示, 当一个路由被匹配时, 它的 params的值将在每个组件中以 `this.$route.params`
* 的形式暴露出来
*/
{
// 多个参数动态路由
path: '/user/:id/detail/:name',
component: User
},
{
// 动态路由
path: '/member/:id',
component: Member
}
]
组件中导航路由并传参
<p>
<router-link to="/user/123/detail/张三">Go to User</router-link>
</p>
<p>
<router-link to="/member/666">Go to Member</router-link>
</p>
路由组件中获取参数id
- vue2获取,
this.$route.params
<template>
<!-- vue2的方法获取路由传递过来的参数
this.$route.params 获取所有参数
-->
<div>用户: {{ this.$route.params }}
----{{this.$route.params.id }}
---{{this.$route.params.name }}
</div>
</template>
- vue3
setup
中获取路由参数
<template>
<div>用户id: {{ userId }}</div>
</template>
<script setup>
/**
* vue3 ##serup中获取路由传递过来的参数
*/
import { useRoute } from 'vue-router';
// 获取所有路由参数
console.log(useRoute().params)
// 获取用户传过来的id
const userId = useRoute().params.id
</script>
vue-router的404页面
当前端路径不存在的时候, 我们要默认访问一个404页面, 体验更加友好
在路由组件中定义一个404页面
src/views/NotFound.vue
<template>
<h2>找不到页面</h2>
</template>
在路由文件的路由表中,添加404组件路由
使用正则的方式
,匹配没有路由的404页面
import NotFound from '../views/NotFound.vue'
const routes = [
{
/**
* 404 路由
* 使用正则的方式,匹配没有路由的404页面
* 路由会现在上面找, 如果都没有匹配到, 会配置到下面的404路由
* (.*) 任意的; :path(.*) 任意动态的路由都会展示这个页面
*/
path: '/:path(.*)',
component: NotFound
}
]
动态路由参数的正则限制
- 动态路由的参数必须是数字,当不是数字的时候就会到404页面
{
// 动态路由的参数必须是数字,当不是数字的时候就会到404页面
path: '/news/:id(\\d+)',
component: News
},
- 动态路由参数可以有多个
{
// 表示参数可以有多个
path: '/news/:id+',
component: News
},
- 动态路由参数可有可无,但后面可以有多个参数
{
// 表示参数可有可无,但后面可以有多个参数
path: '/news/:id*',
component: News
},
- 表示参数可有可无,但后面只能有一个参数, 不可以重复叠加,否则404
{
// 表示参数可有可无,但后面只能有一个参数, 不可以重复叠加,否则404
path: '/news/:id?',
component: News
},
嵌套路由
嵌套路由
就是在一个被路由过来的页面下
可以继续使用路由
,嵌套也就是路由中
的路由
的意思。
路由文件的写法
index.js
import StyleOne from '../views/StyleOne.vue'
import StyleTwo from '../views/StyleTwo.vue'
const routes = [
{
path: '/parent',
component: Parent,
children: [
{
// 这里面的路由不需要再加斜杠了, 访问的时候, 会自动加上
path: "styleone",
component: StyleOne
},
{
path: "styletwo",
component: StyleTwo
}
]
},
]
路由组件里面再次写路由
<template>
<h2>父组件</h2>
<p>
<router-link to="/parent/styleone">样式一</router-link>
</p>
<p>
<router-link to="/parent/styletwo">样式二</router-link>
</p>
<!-- router-view 这个不要遗漏, 不然再次嵌套的路由不知道把页面渲染在哪-->
<router-view></router-view>
</template>
路由跳转
路由跳转
, 就是点击路由
之后, 跳转
新内容, 路由跳转包括声明式路由
和编程式路由
声明式路由
前面用的的, 都是声明式路由
<router-link to="路由地址">点我去首页</router-link>
声明式路由: 路由跳转组件
是<router-link to='路由地址'>
路由跳转</router-link>
,在最终编译的时候会编程成<a>
路由跳转</a>
标签。
使用<router-link>
组件使我们的编程很不灵活
- 用户必须点击触发
<router-link>
才能实现跳转。 - 自由度不高,例如:等待1秒再进行路由跳转。
<router-link>
最终转成<a>
标签。如果你想再按钮上进行路由跳转,实现不了。
编程式导航(编程式路由)
编程式路由导航的实质,就是拿到路由器中的api,调用api实现路由的跳转或者其他操作
也叫js跳转页面, 可以记录各种跳转历史, 可以利用浏览器前进和后退页面
问题来了我们怎样拿到路由器
?
通过组件实例对象,this.$router
- 这里的
this
代表vuecomponent实例对象。 - 这里是通过
$router
访问路由实例,这个就是跳转页面(push),返回页面(forword),go方法等操作; $route
少了一个r
, 这个表示当前活跃的路由对象, 比如拿到path,params,query,name等。
路由器上的方法:
this.$router.push()
//使用push模式跳转
比如, 跳转商品详情页面
this.$router.push('/goods/detail')
- 字符串形式跳转首页
<template>
<h2>page页面</h2>
<div><button @click="goPage">跳转页面</button></div>
</template>
<script>
export default {
methods:{
goPage:function()
{
console.log('跳转页面处理一些逻辑逻辑, 然后再利用按钮调整',this.$router)
// 进行一些逻辑判断, 然后再做页面跳转
this.$router.push('/') // 字符串形式跳转首页路由
}
}
}
</script>
- 传入对象跳转首页路由
<template>
<h2>page页面</h2>
<div><button @click="goPage">跳转页面</button></div>
</template>
<script>
export default {
methods:{
goPage:function()
{
console.log('跳转页面处理一些逻辑逻辑, 然后再利用按钮调整',this.$router)
// 进行一些逻辑判断, 然后再做页面跳转
this.$router.push({path:'/'}) // 通过传入对象跳转首页路由
}
}
}
</script>
- 带参数跳转路由
<template>
<h2>page页面</h2>
<div><button @click="goPage">跳转页面</button></div>
</template>
<script>
export default {
methods:{
goPage:function()
{
console.log('跳转页面处理一些逻辑逻辑, 然后再利用按钮调整',this.$router)
// 带参数
this.$router.push({path:'/member/123'})
}
}
}
</script>
- 路由文件里面定义一个name, 然后利用name跳转
{
name: 'about', // 给路由取一个名字, 通过名字js跳转
path: '/about',
component: About
},
<template>
<h2>page页面</h2>
<div><button @click="goPage">跳转页面</button></div>
</template>
<script>
export default {
methods:{
goPage:function()
{
console.log('跳转页面处理一些逻辑逻辑, 然后再利用按钮调整',this.$router)
// 进行一些逻辑判断, 然后再做页面跳转
// 路由里面定义一个name, 然后利用name跳转
this.$router.push({name:'about'})
}
}
}
</script>
- 路由里面定义一个name,通过name带参数跳转
<template>
<h2>page页面</h2>
<div><button @click="goPage">跳转页面</button></div>
</template>
<script>
export default {
methods:{
goPage:function()
{
console.log('跳转页面处理一些逻辑逻辑, 然后再利用按钮调整',this.$router)
// 进行一些逻辑判断, 然后再做页面跳转
// 路由里面定义一个name,通过name带参数跳转
this.$router.push({name:'news',params:{id:123}})
}
}
}
</script>
- 通过问号(?)的方式传参数跳转
<template>
<h2>page页面</h2>
<div><button @click="goPage">跳转页面</button></div>
</template>
<script>
export default {
methods:{
goPage:function()
{
console.log('跳转页面处理一些逻辑逻辑, 然后再利用按钮调整',this.$router)
// 进行一些逻辑判断, 然后再做页面跳转
// 通过问号(?)的方式传参数跳转
this.$router.push({path:'news',query:{name:'张三'}})
}
}
}
</script>
利用this.$route获取路由传递过来的参数
$route
比$router
少了一个r
, 这个表示当前活跃的路由对象, 比如拿到path,params,query,name等。
<template>
<p>新闻</p>
</template>
<script>
export default{
mounted(){
// 打印当前活跃的路由对象
console.log('news: ',this.$route)
// 获取当前传过来的参数
console.log('?传过来的参数: ',this.$route.query.name)
}
}
</script>
替换当前位置
它的作用类似
router.push
, 唯一不同的是, 它在导航时不会向history
添加新纪录, 正如它的名字所暗示的那样—它取代了当前的条目(页面)。
两种写法
- 声明式
<router-link to="/" replace>Go to Home</router-link>
- 编程式
router.push({path:'/',replace:true})
// 或下面这种写法
router.replace({path:'/'})
也可以直接传递给router.push
的routelocation
中增加一个属性replace:true
历史记录的前进或者后退
history
中进行前进或者后退
// 前进,传入正值, 后退, 传入的值为负值
this.$router.go(1) // 前进一步
this.$router.go(-1) // 后退一步
this.$router.go(-2) // 后退两步,一般就是后推一步
this.$router.back() // 后退一步, 等于 this.$router.go(-1)
this.$router.forword()// 前进一步, 等于 this.$router.go(1)
命名路由
路由命名的有点
:
- 没有硬编码的
URL
params
的自动编码和解码- 防止在
url
中出现打字错误 - 绕过路径排序(如显示一个)
路由文件index.js
{
// 表示参数可以有多个
name: 'news', // 利用name对路由命名
path: '/news/:id+',
component: News
},
声明式路由
: 要链接到一个命名
的路由
, 可以向router-link
组件的to
属性传递一个对象
<p>
<!-- 注意这里要用动态帮忙, 不然后面跟的, 会被识别为一个字符串 -->
<router-link :to="{name:'news',params:{id:888}}">
Go to 新闻
</router-link>
</p>
**导航式路由
**的写法
this.$router.push({path:'news',query:{name:'张三'}})
命名视图
有时候想同时(同级)展示多个视图, 而不是嵌套展示。 例如创建一个布局,有
侧导航
和主要内容
两个视图, 这个时候命名视图
就派上用场了; 我们可以在界面上拥有多个单独命名的视图, 而不是只有一个单独的出口。 例如router-view
没有设置名字, 那么默认为default
。
路由文件写法
{
path: "/shop",
// 一个路由显示多个视图, 需要用复数
components: {
default: About, // 访问这个路由时的默认组件
/**
* 名称:组件 这个名称与 `<router-view>` 上的 `name` 属性匹配,
* 匹配到之后, 就会把对应的组件放到对应占位符的位置
*/
User: User,
News: News
}
}
- 视图渲染的写法
<router-view name="User"></router-view>
<router-view name="News"></router-view>
<!--没有名称的, 名称为name = "default" -->
<router-view></router-view>
路由重定向
- 重定向也是通过
routes
配置来完成, 下面例子就是从/
重定向到/home
:
const routes = [
{
path: '/',
//访问'/'路由, 重定向到 `/home`
redirect: '/home'
},
{
path: '/home',
component: Home
}
]
- 重定向的目标也可以是一个
命名
的路由
const routes = [
{
path: '/',
//访问'/'路由, 重定向到 `/home`
// 通过name来重定向
redirect: { name: 'home' }
},
{
name: 'home',
path: '/home',
component: Home
}
]
- 定义一个方法, 动态返回重定向目标
const routes = [
{
path: '/',
//访问'/'路由, 重定向到 `/home`
// 方法,
redirect: (to) => {
console.log(to) // 利用这个对象, 可以做一个业务判断, 实现动态重定向
// return { name: 'home' } // 函数这样返回可以
return { path: '/home' } // 函数这样返回也可以
}
},
{
name: 'home',
path: '/home',
component: Home
}
]
路由别名
除了通过
path
路径来访问组件, 有时候path
比较复杂, 可以通过取别名(alias)的方式来访问
const routes = [
{
path: '/',
//访问'/'路由, 重定向到 `/home`
// 方法,
redirect: (to) => {
console.log(to) // 利用这个对象, 可以做一个业务判断, 实现动态重定向
// return { name: 'home' } // 函数这样返回可以
return { path: '/homeAlias' } // 函数这样返回也可以
}
},
{
/**
* 除了通过path路径来访问组件, 还可以通过取别名(alias)的方式来访问
*/
name: 'home',
// alias: '/homeAlias', // 取一个别名
alias: ['/homeAlias', '/homeSon'], // 可以用数组的形式取多个别名
path: '/home',
component: Home
}
]
路由组件传参接收问题
路由表传参
路由文件(index.js)
const routes = [
{
// 带参数的动态路由
path: '/member/:id',
component: Member,
props: true // 通过使用props: true的配置, 来传递路由参数
},
]
选项式api接收参数(prop)
<script>
export default{
props:['id'],
mounted(){
console.log('组件传过来的参数id: ',this.$route.params.id)
console.log('组件传过来的参数id: ',this.id)
}
}
</script>
组合式api接收路由参数(prop)
<script setup>
/**
* vue3 ##serup中获取路由传递过来的参数
*/
// 组合式api中, props的写法
const props = defineProps({
id:String
})
console.log('vue3_props',props)
console.log('vue3_props',props.id)
</script>
命名视图的情况下传参
路由表中 index.js
const routes = [
{
// 命名视图, 需要为每个命名视图定义`props`配置,才能传参
path: "/shop/:id",
// 一个路由显示多个视图, 需要用复数
components: {
default: About, // 访问这个路由时的默认组件
User: User,
News: News
},
props: {
default: true, // 只在About中接受到这个id参数就可以
User: false, // 不接收路由传过来的参数
News: false // 不接收路由传过来的参数
}
}
]
不同的历史记录模式
路由模式有两种,二者的区别,
Hash模式
有#
号,history模式
没有#
号。
Hash模式
hash
模式使用createWebHashHistory
创建的; 它在内部传递的实际URL
之前使用了一个哈希字符(#)
; 由于这部分URL
从未被发送到服务器, 所以它不需要在服务器层面上进行任何特殊处理。不过, 它在SEO
中确实有不好的影响。 如果考虑SEO
这个问题, 可以使用HTML5
模式。
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes, // 'routes:routes' 的简写 配置路由表
})
HMTL5模式(history模式)
用createWebHistory()
创建HTML5
模式,推荐使用这个模式
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes, // 'routes:routes' 的简写 配置路由表
})
使用这种历史模式时, URL
会看起来很正常
, 例如http://localhost:5173/news/456
;
不过, 问题来了?
由于我们的应用是一个单页的客户端应用。 如果没有适当的服务器配置, 用户在浏览器中直接访问http://localhost:5173/news/456
, 就会得到一个404错误
; 这样就不友好了。
不用担心, 要解决这个问题, 需要做的就是在您的服务器上**添加一个简单的回退路由
**。如果URL
不匹配任何静态资源, 它应提供与您的应用程序中的index.html
相同的页面。
history的nginx配置
location / {
# 项目根目录
root /data/www/web;
# 项目入口文件
index index.html;
# try_files 是nginx的一个指令
try_files $uri $uri/ /index.html;
}
路由守卫
本质就是权限判断
全局前置守卫
// 全局前置守卫,不管进入哪一个组件, 都会执行这个函数
router.beforeEach((to, from, next) => {
console.log('to', to)
console.log('from', from)
next() // 表示一个通行证的意思, 通过验证之后, 页面才会往下走
})
路由独享守卫
{
name: 'about', // 给路由取一个名字, 通过名字js跳转
path: '/about',
component: About,
// 路由独享守卫
beforeEnter: (to, from, next) => {
console.log('to', to)
console.log('from', from)
if (123 == 1213) {
// 比如, 登录判断, 如果登录了, 就方形
next()// 表示一个通行证的意思, 通过验证之后, 页面才会往下走
}
}
}
组件内的守卫
<template>
<p>新闻</p>
</template>
<script>
export default{
data(){
return {
age:18
}
},
beforeRouteEnter(to, from,next) {
console.log('路由进入组件之前')
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 因为当守卫执行时,组件实例还没被创建!
// 但是可以通过回调函数拿到组件实例
next((vm)=>{
console.log('通过回调函数拿到组件实例, 获取到传入的数据',vm.age)
})
},
beforeRouteUpdate(to, from) {
console.log('路由更新组件之前')
/**
* 在当前路由改变,但是该组件被复用时调用
* 举例来说,对于一个带有动态参数的路径 `/users/:id`,
* 在 `/users/1` 和 `/users/2` 之间跳转的时候,由于会渲染同样的 `UserDetails`
* 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
* 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
* 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
*/
},
beforeRouteLeave(to, from) {
console.log('路由离开组件之前')
// 在导航离开渲染该组件的对应路由时调用
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
</script>
路由懒加载(以后都要这么用)
路由懒加载, 用到时再加载
因为如果组件太多, 一次加载完, 效率很低, 如果打开首页, 就只加载首页的路由, 打开详情页, 只加载详情页的路由, 这样就会更高效! 一般来说,对所有的路由都使用动态导入是个好主意。
语法:
const routes = [
{
path: '/page',
// 使用路由懒加载后,上面就不需要再导入了
component: () => import('../views/Page.vue')
}
]
一般都会抽离一个变量出来, 方便管理和维护
语法:
const Home = ()=>import('../views/Page.vue')
const routes = [
{
path: '/page',
// 使用路由懒加载后,上面就不需要再导入了
component: Home
}
]