vue-router(路由)
本次用的是vue Router4.0 版本的对标的是vue3的组合式api
1.0 安装
npm install vue-router@4
创建文件 router/index.js
// 1 引入 方法
import { createRouter ,createWebHashHistory } from "vue-router";
// 引入组件
import Films from '../views/films.vue'
import Cinemas from '../views/cinemas.vue'
import Center from '../views/cinemas.vue'
const routes = [
{
path: '/',
redirect: '/films', // 重定向
},
{
path:'/films', // 路径名字
component:Films ,// 哪个组件
},
{
path:'/cinemas', // 路径名字
component:Cinemas ,// 哪个组件
},
{
path:'/center', // 路径名字
component:Center ,// 哪个组件
},
]
//2 创建一个对象
const router = createRouter({
//为了简单起见,我们在这里使用 hash 模式。 #/film #/center
// history 模式 /film /center
history: createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
//3 导出对象
export default router
修改main.js
import { createApp } from 'vue'
import App from './App.vue'
// 引入 router
import router from './router/index'
const app = createApp(App)
app.use(router) // 使用路由
app.mount('#app')
修改app.vue
<template>
app
// 路由视图
<router-view></router-view>
</template>
页面就出来了,我们的地址就变成这样了
重定向的第二种写法
const routes = [
{
path: '/',
//redirect: '/films', // 重定向
redirect:{
name:'myFilms' // 根据名字匹配
}
},
{
path:'/films',
name:'myFilms',
component:Films ,
},
{
path:'/cinemas',
component:Cinemas ,
},
{
path:'/center',
component:Center ,
},
]
如果没有匹配到我们的路径我们给个404页面
const routes = [
{
path: '/',
redirect: '/films', // 重定向
},
{
path:'/films', // 路径名字
name:'myFilms',
component:Films ,// 哪个组件
},
{
path:'/cinemas', // 路径名字
component:Cinemas ,// 哪个组件
},
{
path:'/center', // 路径名字
component:Center ,// 哪个组件
},
// 如果没有匹配到我们的路径我们给个404页面
{
path:'/:pathMath(.*)*',
name:'NotFound',
component:NotFound,
}
]
别名:
{
path:'/center', // 路径名字
alias:'/my',// 别名
component:Center ,// 哪个组件
},
意思就是 http://127.0.0.1:5173/#/center 能访问的页面,
我也能 http://127.0.0.1:5173/#/my
2.0 声明式导航
修改 App.vue
<template>
<router-view></router-view>
<Tabbar></Tabbar>
</template>
<script setup>
import Tabbar from './components/tabbar.vue'
</script>
<style>
*{
margin:0;
padding: 0;
}
ul li{
list-style: none;
}
</style>
添加 tabbar.vue
组件
<template>
<div class="tabber">
<ul>
<!-- custom 定制 v-slot 插槽 解构出来状态值-->
<router-link custom to="/films" v-slot="{isActive,navigate }">
<li :class="isActive ? 's-select' : '' " @click="navigate">电影</li>
</router-link>
<router-link custom to="/cinemas" v-slot="{isActive,navigate }">
<li :class="isActive ? 's-select' : '' " @click="navigate">影院</li>
</router-link>
<router-link custom to="/center" v-slot="{isActive,navigate }">
<li :class="isActive ? 's-select' : '' " @click="navigate">我的</li>
</router-link>
</ul>
</div>
</template>
<script setup>
</script>
<style scoped lang="scss">
.tabber{
position: fixed;
bottom:0;
width: 100%;
height:50px;
line-height: 50px;
text-align: center;
background: #fff;
ul{
display: flex;
li{
flex: 1;
}
}
.s-select{
color:#f6574a;
}
}
</style>
点击底部的页面就可以随便跳转了
3.0 嵌套路由
修改我们的 index.js
// 1 引入 方法
import { createRouter ,createWebHashHistory } from "vue-router";
// 引入组件
import Films from '../views/films.vue'
import Cinemas from '../views/cinemas.vue'
import Center from '../views/cinemas.vue'
import NotFound from '../views/notFound.vue'
import Nowplaying from '../views/films/nowplaying.vue'
import Comingsoon from '../views/films/comingsoon.vue'
const routes = [
{
path: '/',
redirect: '/films', // 重定向
// redirect:{
// name:'myFilms'
// }
},
{
path:'/films', // 路径名字
name:'myFilms',
component:Films ,// 哪个组件
redirect: '/films/nowplaying',
children:[
{
path:'/films/nowplaying', // 1 全部路径
component: Nowplaying,
},
{
path:'comingsoon', // 2 自动拼接路径
component: Comingsoon,
},
]
},
{
path:'/cinemas', // 路径名字
component:Cinemas ,// 哪个组件
},
{
path:'/center', // 路径名字
alias:'/my',// 别名
component:Center ,// 哪个组件
},
// 如果没有匹配到我们的路径我们给个404页面
{
path:'/:pathMath(.*)*',
name:'NotFound',
component:NotFound,
}
]
//2 创建一个对象
const router = createRouter({
//为了简单起见,我们在这里使用 hash 模式。 #/film #/center
// history 模式 /film /center
history: createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
//3 导出对象
export default router
修改我们的 films.vue
<template>
<div style="height:100px">我是顶部的大轮播</div>
<ul class="top-card">
<router-link custom to="/films/nowplaying" v-slot="{isActive,navigate }">
<li @click="navigate">
<span :class="isActive ? 's-select' : '' ">正在热映</span>
</li>
</router-link>
<router-link custom to="/films/comingsoon" v-slot="{isActive,navigate }">
<li @click="navigate">
<span :class="isActive ? 's-select' : '' ">即将上映</span>
</li>
</router-link>
</ul>
<!-- 子路由视图-->
<router-view></router-view>
</template>
<script setup>
</script>
<style scoped lang="scss">
.top-card{
display: flex;
li{
flex: 1;
text-align: center;
line-height: 50px;
}
.s-select{
border-bottom: 1px solid #f6574a;
color:#f6574a;
}
}
</style>
4.0 编程式导航
安装一个axios 获取一下卖座网的一个数据
npm i axios
修改nowplaying.vue
<template>
<div>
22
<ul>
<li v-for="(item,index) in list" :key="index" @click="goDetail(item)">{{item.name}}</li>
</ul>
</div>
</template>
<script setup>
import axios from 'axios'
import {onMounted, ref} from "vue";
import { useRouter } from 'vue-router'
let list = ref([])
onMounted(()=>{
axios({
url:'https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6703884',
headers:{
'X-Client-Info':'{"a":"3000","ch":"1002","v":"5.2.1","e":"16879515478504378843463681"}',
'X-Host':'mall.film-ticket.film.list'
}
}).then(res=>{
console.log(res)
list.value = res.data.data.films
})
})
const router = useRouter() // // 返回路由器实例。相当于在模板中使用 $router。
function goDetail(item){
router.push(`/detail/${item.filmId}`)
}
</script>
<style scoped>
li{
font-size: 16px;
margin:10px;
}
</style>
顺便增加一个页面 detail.vue
并且配置路由 动态匹配
{
path: '/detail/:mid', // :mid参数
component: Detail
},
编程式导航的几种写法
// 字符串路径
router.push(`/detail/${ item.filmId }`)
// 带有路径的对象
router.push({path: `/detail/${ item.filmId }`})
// 命名的路由,并加上参数
router.push({name:'detail',params:{mid:'123'}})
// 带查询的参数 结果是 /detail?mid=123
router.push({path:'/detail',query:{mid:'123'}})
detail.vue
接收参数
<template>
<div>
detail
<button @click="goBack">点我返回</button>
<p>电影id{{mid}}</p>
</div>
</template>
<script setup>
import {useRoute,useRouter} from 'vue-router'
import {onMounted,ref} from "vue";
let mid = ref('')
// // 所以我们不能再直接访问 this.$router 或 this.$route。作为替代,我们使用 useRouter 和 useRoute 函数:
let router = useRouter()
let route = useRoute()
onMounted(()=>{
mid.value = route.params.mid // 获取动态id
})
function goBack(){
// router.back() // 返回
router.go(-1)
// // 前进一个页面
// router.forward()
// router.go(1)
}
</script>
这个时候 比如detail页面下还有几个 电影的链接也是跳转到 detail页面,回发生什么呢?
页面顶部路径修改了 但是内容没有变化,这是因为onmounted 不在触发导致的,我们用watch监听
修改detail.vue
<template>
<div>
detail
<button @click="goBack">点我返回</button>
<p>电影id{{mid}}</p>
<hr>
<h3>猜你喜欢</h3>
<p @click="goLike">功夫</p>
</div>
</template>
<script setup>
import {useRoute,useRouter} from 'vue-router'
import {onMounted,ref,watch} from "vue";
let mid = ref('')
// 所以我们不能再直接访问 this.$router 或 this.$route。作为替代,我们使用 useRouter 和 useRoute 函数:
let router = useRouter()
let route = useRoute()
onMounted(()=>{
mid.value = route.params.mid // 获取动态id
})
function goBack(){
// router.back() // 返回
router.go(-1)
// // 前进一个页面
// router.forward()
// router.go(1)
}
function goLike(){
router.push('/detail/6789')
}
// watch 监听
watch(()=> route.params,(newValue,oldValue)=>{
console.log('新电影的id',newValue.mid,route.params.mid) // 新电影的id 6789 6789
})
</script>
5.0 路由模式
hash 模式是用 createWebHashHistory()
创建的:意思就是带#
http://127.0.0.1:5173/#/detail/6789
html5模式 用 createWebHistory()
创建 HTML5 模式,推荐使用这个模式,不带# 但是需要服务器额外的配置
http://127.0.0.1:5173/detail/6789
6.0 全局路由拦截
router.beforeEach
修改index.js
// 1 引入 方法
import { createRouter ,createWebHashHistory } from "vue-router";
// 引入组件
import Films from '../views/films.vue'
import Cinemas from '../views/cinemas.vue'
import Center from '../views/cinemas.vue'
import NotFound from '../views/notFound.vue'
import Nowplaying from '../views/films/nowplaying.vue'
import Comingsoon from '../views/films/comingsoon.vue'
import Detail from '../views/detail.vue'
import Login from '../views/login.vue'
const routes = [
{
path:'/login',
name:'Login',
component: Login,
},
{
path: '/',
redirect: '/films', // 重定向
// redirect:{
// name:'myFilms'
// }
},
{
path:'/films', // 路径名字
name:'myFilms',
component:Films ,// 哪个组件
redirect: '/films/nowplaying',
children:[
{
path:'/films/nowplaying', // 1 全部路径
component: Nowplaying,
},
{
path:'comingsoon', // 2 自动拼接路径
component: Comingsoon,
},
]
},
{
path: '/detail/:mid',
component: Detail
},
{
path:'/cinemas', // 路径名字
component:Cinemas ,// 哪个组件
},
{
path:'/center', // 路径名字
alias:'/my',// 别名
component:Center ,// 哪个组件
meta:{
requireAuth:true,// 需要登录
}
},
// 如果没有匹配到我们的路径我们给个404页面
{
path:'/:pathMath(.*)*',
name:'NotFound',
component:NotFound,
}
]
//2 创建一个对象
const router = createRouter({
//为了简单起见,我们在这里使用 hash 模式。 #/film #/center
// history 模式 /film /center
history: createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
// 全局拦截 全局前置守卫
router.beforeEach((to, from, next) => {
// 登陆成功设置一个token
let isAuthenticated = localStorage.getItem('token')
// 跳转的不是登录页 并且没授权 并且是center页面 跳回到login页面
// 第一种写法
// if (to.name !== 'Login' && !isAuthenticated && to.fullPath !== '/center'){
//第二种写法
if (to.name !== 'Login' && !isAuthenticated && to.meta.requireAuth){
next({ name: 'Login' })
} else{
next()
}
})
// 后置钩子 它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
router.afterEach((to, from) => {
// sendToAnalytics(to.fullPath)
console.log('提交用户行为')
})
//3 导出对象
export default router
组件内的路由拦截
修改center.vue
<template>
<div>
center
</div>
</template>
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate} from 'vue-router'
onBeforeRouteUpdate((to, from) =>{
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
console.log(to,from)
}),
// onBeforeRouteLeave((to, from)=>{
// // 在导航离开渲染该组件的对应路由时调用
// // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
// console.log(to,from)
// })
// 与 beforeRouteLeave 相同,无法访问 `this`
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('你真的要离开吗')
// 取消导航并停留在同一页面上
if (!answer) return false
})
</script>
<style scoped>
</style>
验证onBeforeRouteLeave
修改 detail.vue
<template>
<div>
detail
<button @click="goBack">点我返回</button>
<p>电影id{{mid}}</p>
<hr>
<h3>猜你喜欢</h3>
<p @click="goLike">功夫</p>
</div>
</template>
<script setup>
import {onBeforeRouteLeave, useRoute, useRouter,onBeforeRouteUpdate} from 'vue-router'
import {onMounted,ref,watch} from "vue";
let mid = ref('')
// 所以我们不能再直接访问 this.$router 或 this.$route。作为替代,我们使用 useRouter 和 useRoute 函数:
let router = useRouter()
let route = useRoute()
onMounted(()=>{
mid.value = route.params.mid // 获取动态id
})
function goBack(){
// router.back() // 返回
router.go(-1)
// // 前进一个页面
// router.forward()
// router.go(1)
}
function goLike(){
router.push('/detail/6789')
}
// 1.0 watch 监听
watch(()=> route.params,(newValue,oldValue)=>{
console.log('新电影的id',newValue.mid,route.params.mid) // 新电影的id 6789 6789
})
// 2.0 或者用
onBeforeRouteUpdate((to,from)=>{
console.log('新电影的id',to.params.mid) // 新电影的id 6789 6789
})
</script>
<style scoped>
</style>
验证 onBeforeRouteUpdate
7.0 路由懒加载
用到的页面再加载对应的组件
修改index.vue
{
path:'/login',
name:'Login',
component: () => import('../views/login.vue')
},
{
path:'/center', // 路径名字
component: ()=>import('../views/center.vue') ,// 哪个组件
meta:{
requireAuth:true,// 需要登录
}
},
刷新页面 然后点击对应的页面就会发现,只有进入到对应页面才加载对应的组件了
自暴自弃是一种选择,但是另一种选择就是迎难而上。