vue Router4.0

77 阅读5分钟

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>

页面就出来了,我们的地址就变成这样了

http://127.0.0.1:5173/#/films

重定向的第二种写法

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,// 需要登录
        }
},

刷新页面 然后点击对应的页面就会发现,只有进入到对应页面才加载对应的组件了

项目链接

参考链接(官方文档)

自暴自弃是一种选择,但是另一种选择就是迎难而上。