vue3路由

Vue Router官方文档

对路由的理解

路由的理解

基本切换效果

  • Vue3中要使用vue-router的最新版本,目前是4版本。

  • 路由配置文件代码如下:

    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'
    import Home from '@/pages/Home.vue'
    import News from '@/pages/News.vue'
    import About from '@/pages/About.vue'
    // 创建路由器
    const router = createRouter({
    history:createWebHistory(),// 路由器工作模式:hash模式/history模式
    routes:[
    {
    path:'/home',
    component:Home
    },
    {
    path:'/news',
    component:News
    },
    {
    path:'/about',
    component:About
    }
    ]
    })
    export default router
  • main.ts代码如下:
    1
    2
    3
    4
    5
    6
    // 引入路由器
    import router from './router/index'
    const app = createApp(App) // 创建一个应用
    app.use(router) // 使用路由器

    app.mount('#app') // 挂载整个应用到app容器中
  • App.vue代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <template>
    <div class="app">
    <h2 class="title">Vue路由测试</h2>
    <!-- 导航区 -->
    <div class="navigate">
    <RouterLink to="/home" active-class="active">首页</RouterLink>
    <RouterLink to="/news" active-class="active">新闻</RouterLink>
    <RouterLink to="/about" active-class="active">关于</RouterLink>
    </div>
    <!-- 展示区 -->
    <div class="main-content">
    <RouterView></RouterView>
    </div>
    </div>
    </template>

    <script lang="ts" setup name="App">
    import {RouterLink,RouterView} from 'vue-router'
    </script>

两个注意点

  1. 路由组件通常存放在pagesviews文件夹,一般组件通常存放在components文件夹。

  2. 通过点击导航,视觉效果上“消失” 了的路由组件,默认是被卸载掉的,需要的时候再去挂载

路由器工作模式

一般,后台管理项目,不需要考虑seo,直接用hash模式
前台项目,to C的,直接给客户用的,用history模式

  1. history模式

优点:URL更加美观,不带有#,更接近传统的网站URL

缺点:后期项目上线需要服务端配合处理路径问题,否则刷新会有404错误。

1
2
3
4
const router = createRouter({
history:createWebHistory(), //history模式
/******/
})
  1. hash模式

优点:兼容性更好,因为不需要服务器端处理路径。

缺点:URL带有#不太美观,且在SEO优化方面相对较差

1
2
3
4
const router = createRouter({
history:createWebHashHistory(), //hash模式
/******/
})

to的两种写法

字符串形式 和 对象形式

1
2
3
4
5
<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>

<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>

大部分项目,不是这么写路由的,而是直接 遍历的动态路由这个时候对象尤其重要的

命名路由

作用:可以简化路由跳转及传参(后面就讲)。

给路由规则命名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
routes:[
{
name:'zhuye',
path:'/home',
component:Home
},
{
name:'xinwen',
path:'/news',
component:News,
},
{
name:'guanyu',
path:'/about',
component:About
}
]

跳转路由:

1
2
3
4
5
<!--简化前:需要写完整的路径(to的字符串写法) -->
<router-link to="/news/detail">跳转</router-link>

<!--简化后:直接通过名字跳转(to的对象写法配合name属性) -->
<router-link :to="{name:'guanyu'}">跳转</router-link>

嵌套路由

  1. 编写News的子路由:Detail.vue

  2. 配置路由规则,使用children配置项:

    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
    const router = createRouter({
    history:createWebHistory(),
    routes:[
    {
    name:'zhuye',
    path:'/home',
    component:Home
    },
    {
    name:'xinwen',
    path:'/news',
    component:News,
    children:[
    {
    name:'xiang',
    path:'detail',
    component:Detail
    }
    ]
    },
    {
    name:'guanyu',
    path:'/about',
    component:About
    }
    ]
    })
    export default router
  3. 跳转路由(记得要加完整路径):

    1
    2
    3
    <router-link to="/news/detail">xxxx</router-link>
    <!-- 或 -->
    <router-link :to="{path:'/news/detail'}">xxxx</router-link>
  4. 记得去Home组件中预留一个<router-view>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <template>
    <div class="news">
    <nav class="news-list">
    <RouterLink v-for="news in newsList" :key="news.id" :to="{path:'/news/detail'}">
    {{news.name}}
    </RouterLink>
    </nav>
    <div class="news-detail">
    <RouterView/>
    </div>
    </div>
    </template>

路由传参 / 动态路由

query参数

  1. 传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!-- 跳转并携带query参数(to的字符串写法) -->
    <router-link to="/news/detail?a=1&b=2&content=欢迎你">
    跳转
    </router-link>

    <!-- 跳转并携带query参数(to的对象写法) -->
    <RouterLink
    :to="{
    //name:'xiang', //用name也可以跳转
    path:'/news/detail',
    query:{
    id:news.id,
    title:news.title,
    content:news.content
    }
    }"
    >
    {{news.title}}
    </RouterLink>
  2. 接收参数:

    1
    2
    3
    4
    import {useRoute} from 'vue-router'
    const route = useRoute()
    // 打印query参数
    console.log(route.query)

params参数

用这种方式传参会被同事骂?因为它的字符串写法可读性很差?XDD
不过对象写法和query参数也没大差吧?而且开启路由传参也比较方便,为什么呢?
不过需要占位,这里也比query要麻烦,而且如果是可以不传的参数还要加?号

  1. 传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!-- 跳转并携带params参数(to的字符串写法) -->
    <RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink>

    <!-- 跳转并携带params参数(to的对象写法) -->
    <RouterLink
    :to="{
    name:'xiang', //用name跳转,不能用path,会报错
    params:{
    id:news.id,
    title:news.title,
    content:news.title
    }
    }"
    >
    {{news.title}}
    </RouterLink>
  2. 接收参数:

    1
    2
    3
    4
    import {useRoute} from 'vue-router'
    const route = useRoute()
    // 打印params参数
    console.log(route.params)
  • 需要提前在路由规则占位
    1
    2
    3
    4
    5
    6
    7
    {
    path:'/news',
    component: News,
    children:[
    {name:'xiang',path:'detail/:id/:title/:content',component: Detail}
    ]
    }
  • 如果是可传可不传的参数,需要在规则中加?,否则会报错
    1
    2
    3
    4
    // content 可不传
    children:[
    {name:'xiang',path:'detail/:id/:title/:content?',component: Detail}
    ]

备注1:传递params参数时,若使用to的对象写法,必须使用name配置项,不能用path

备注2:传递params参数时,需要提前在规则中占位。

路由的props配置

作用:让路由组件更方便的收到参数(可以将路由参数作为props传给组件)
写法:params参数用 布尔值写法; query参数用 函数写法

一般组件是直接写标签的,可以直接传递props
路由组件(这里是Detail)都没有机会写标签,所以通过这种方式来传props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
name:'xiang',
path:'detail/:id/:title/:content', // params参数的占位规则写法
component:Detail, // 这里就相当于 底层写了一个 <Detail/>
// 下面的prop配置就相当于 <Detail :id=?? :title=?? :content=?? />

// 第一种写法:
// props的布尔值写法,作用:把收到了每一组 params参数 ,作为props传给Detail组件
// 只能处理params参数,也推荐params时候使用这种方式
// props:true

// 第二种写法:
// props的函数写法,返回一个对象
// 作用:把 返回的对象中 每一组key-value 作为 props 传给Detail组件
props(route){ // 可以随意命名,但都是route对象
return route.query // 或params
}

// 第三种写法: (极少用)
// props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
// props:{a:1,b:2,c:3}, // 把参数写死了,没有意义,所以写的非常少
}

replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式。

  2. 浏览器的历史记录有两种写入方式:分别为pushreplace

    • push是追加历史记录(默认值)。
    • replace是替换当前记录。(登陆页面应该会用这个)
  3. 开启replace模式:

    1
    <RouterLink replace>News</RouterLink>

声明式导航&编程式导航

编程式路由导航:脱离<RouterLink>实现路由跳转 (开发常用
路由组件的两个重要的属性:$route$router变成了两个hooks

声明式 编程式
导航到不同的位置 <router-link :to="..."> router.push(...)
替换当前位置 <router-link :to="..." replace> router.replace(...)
横跨历史
\
router.go(n)
  • router.go(n):横跨历史。采用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 window.history.go(n)
  • n = 1时,等同于:router.forward()
  • n = -1时,等同于:router.back()

router.pushrouter.replacerouter.gowindow.history.pushStatewindow.history.replaceStatewindow.history.go 的翻版,它们确实模仿了 window.history 的 API。
值得一提的是,无论在创建路由器实例时传递什么 history 配置(history模式,和hash模式 的路由器工作模式),Vue Router 的导航方法 (push、replace、go) 都能始终正常工作。

应用场景:

  1. 符合某些条件才跳转,不是说一点就跳转

    到了10点整,自动跳到秒杀路由;只有登陆成功,自动跳转到个人中心

  2. 鼠标滑过一个东西跳转(不点)
  3. 还有手机摇一摇跳转
1
2
3
4
5
6
7
8
9
10
11
import {useRoute,useRouter} from 'vue-router'

const route = useRoute()
const router = useRouter()

console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)

router.push('/news') // to能咋写,push就能咋写 // 1. 字符串写法 2. 对象写法

点击button按钮进行路由跳转:

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
33
34
35
36
37
38
39
40
41
42
43
<template>
<div class="news">
<!-- 导航区 -->
<ul>
<li v-for="news in newsList" :key="news.id">
<button @click="showNewsDetail(news)">查看新闻</button>
<RouterLink :to="{...}">{{news.title}}</RouterLink>
</li>
</ul>
<!-- 展示区 -->
<div class="news-content">
<RouterView></RouterView>
</div>
</div>
</template>

<script setup lang="ts" name="News">
import {reactive} from 'vue'
import {RouterView,RouterLink,useRouter} from 'vue-router'

const newsList = reactive([...])

const router = useRouter()

// 定义一个接口
interface NewsInter {
id:string,
title:string,
content:string
}
// 直接写news会报错:参数news隐式具有any类型(就是不知道news是什么)
// 如果想图方便简单,可以直接:news:any
function showNewsDetail(news:NewsInter){
router.replace({
name:'xiang',
query:{
id:news.id,
title:news.title,
content:news.content
}
})
}
</script>

在vue2中,编程式路由导航,如果重复跳转是报错的,但是vue3里面不会报错

重定向

作用:将特定的路径,重新定向到已有路由。

  • 具体编码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
path:'/',
redirect:'/about'
},
{
path:'/news',
component: News,
redirect:'/news/detail', // 嵌套子路由重定向
children:[
{
name:'xiang',path:'detail/:id?/:title?/:content?',component: Detail,
props(route){
return route.params
}
}
]
}

路由进阶

导航守卫、路由元信息、路由懒加载、路由故障等等
参考官方文档

导航守卫

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。

  1. 全局前置守卫
    当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve(解析) 完之前一直处于等待中
    • 参数:
      • to: 即将要进入的目标
      • from: 当前导航正要离开的路由

        新版Vue Router移除了next参数,但仍支持使用,因为其是常见的错误来源
        确保 next 在任何给定的导航守卫中都被严格调用一次,否则钩子永远都不会被解析或报错。

    • 返回值:
      • false:取消导航,如果浏览器的 URL 改变了,URL 地址会重置到 from 路由对应的地址。
      • 一个路由地址:如同调用 router.push(),且可以传入诸如 replace: true 或 name: ‘home’ 之类的选项。
        它会中断当前的导航,同时用相同的 from 创建一个新导航。
      • 如果什么都没有,undefined 或返回 true,则导航是有效的,并调用下一个导航守卫

        即:
        return true 或 undefined(没有返回值默认 为un) ,正常跳转
        return false,阻止跳转
        return 字符串 或 对象 (就和router.push()传参一样,也即使和to传参一样)

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
// router/index.ts
const router = createRouter({ ... })

router.beforeEach((to, from) => {
console.log("路由前置守卫",to,from);
if(to.path === '/about'){

console.log("路由前置守卫触发,从about跳转到news/detail");
return '/news/detail' // 字符串形式,值是path地址,将用户重定向到新闻
return { name: 'news/detail' } // 将用户重定向到新闻页面,对象形式


console.log('路由前置守卫触发,false取消当前的导航,不跳转');
return false

console.log('路由前置守卫触发,return 对象形式 并传参');
return {
name: 'xiang',
params:{
id: '001',
title: '路由前置守卫跳转',
content:'跳转成功'
}
}

}
})

全局前置守卫最常用的导航守卫,其余还有:全局解析守卫、全局后置钩子、路由独享的守卫、组件内的守卫等,具体使用查看官方文档 - 导航守卫

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。