【八. Vue3路由全解析:从入门到精通】

8. Vue3 路由

8.1 对路由的理解

  1. 路由定义:路由是根据不同的地址(URL)展示不同内容的机制。通过对 URL 的识别与匹配,将用户引导至对应的内容页面

  2. 路由本质:就是一组key(通常是指路由的路径(如/home/news等))-value(是与该路径对应的组件(如Home.vueNews.vue等组件))的对应关系

  3. 前端使用路由的目的:前端使用路由是为了实现单页面应用(SPA应用),单页面应用能够在不重新加载整个页面的情况下,动态更新页面内容

  4. 路由器的作用:当存在多个路由时,就需要路由器进行统一管理。它负责监控 URL 的变化,根据预先设定的规则,找到对应的路由配置,并加载相应组件

  5. route规则:route 规则的核心是根据路径寻找与之对应的组件。通过对 URL 路径的解析和匹配,路由器能够确定需要加载的组件,从而展示正确的页面内容

  6. 流程:点击——>路径变化——>路由器捕获——>寻找相应的route规则——>把相应的组件挂载到对应的地方(当用户进行点击操作,触发路径变化,路由器会捕获到这一变化,依据 route 规则,在路由配置中寻找匹配的路径,然后将对应的组件挂载到指定位置,完成页面内容的更新)

8.2 基本切换效果

8.2.1 前置条件:安装 Vue Router

场景:使用路由功能前需全局安装 Vue3 对应的路由库

npm i vue-router

8.2.2 路由配置核心流程(以 router/index.ts 为例)

整体代码:

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(), 
  routes: [ 
    {
      path: '/home',
      component: Home
    },
    {
      path: '/news',
      component: News
    },
    {
      path: '/about',
      component: About
    }
  ]
});

export default router;

目标:创建路由器并配置路由规则,让不同 URL 显示对应组件

  1. 引入核心工具:
  • createRouter:创建路由器实例的函数

  • createWebHistory:设置路由模式为 HTML5 历史模式(无 # 号)

import { createRouter, createWebHistory } from 'vue-router'
  1. 引入页面组件:从项目路径中引入需要显示的组件(如首页、关于页)
  import Home from '@/pages/Home.vue'  // @ 通常代表 src 目录
  import News from '@/pages/News.vue'
  import About from '@/pages/About.vue'
  1. 创建路由器并配置规则:
  • routes 数组定义路由规则,每个对象包含:
    • path:路由路径(浏览器地址栏显示的 URL)
    • component:对应路径显示的组件
  const router = createRouter({
    history: createWebHistory(), // 使用历史模式
    routes: [ 
      {
        path: '/home',// 访问 /home 显示 Home 组件
        component: Home
      },
      {
        path: '/news',// 访问 /news显示 News组件
        component: News
      },
      {
        path: '/about',// 访问 /about 显示 About 组件
        component: About
      }
    ]
  })
  1. 暴露路由器:将配置好的路由器导出,供 main.ts 引用
  export default router

8.2.3 在 main.ts 中启用路由

操作:将路由器挂载到 Vue 应用中,激活路由功能

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index' // 引入路由器

const app = createApp(App)
app.use(router) // 使用路由器
app.mount('#app') // 挂载应用到页面容器

8.2.4 在 App.vue 中实现导航与内容展示

整体代码:

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="increment">点我+1</button>
  <button @click="decrement">点我-1</button>
  <hr>
  <img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)"> 
  <span v-show="dogList.isLoading">加载中......</span><br>
  <button @click="getDog">再来一只狗</button>
</template>

<script lang="ts">
  import {defineComponent} from 'vue'

  export default defineComponent({
    name:'App',
  })
</script>

<script setup lang="ts">
  import useSum from './hooks/useSum'
  import useDog from './hooks/useDog'

  let {sum,increment,decrement} = useSum()
  let {dogList,getDog} = useDog()
</script>
  1. 导航区:使用 RouterLink 组件
  • 作用:生成路由链接,点击时切换页面(不会刷新浏览器)

  • 关键属性:

    • to:目标路由路径(如 /home
    • active-class:选中时添加的 CSS 类名(用于高亮当前链接)
  <template>
    <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>
  </template>
  
  <script setup>
  import { RouterLink } from 'vue-router' // 引入导航组件
  </script>
  1. 展示区:使用 RouterView 组件
  • 作用:作为路由组件的占位符,匹配到的组件会在此处渲染
  <template>
    <div class="main-content">
      <RouterView></RouterView> <!-- 路由匹配的组件会显示在这里 -->
    </div>
  </template>
  
  <script setup>
  import { RouterView } from 'vue-router' // 引入占位符组件
  </script>

8.3 两个注意点

  • 组件存放位置

    • 路由组件:也就是直接和路由配置绑定的页面级组件,一般存放在 pages 或者 views 文件夹

    • 一般组件:是那些被路由组件或者其他组件引用的小部件,通常放在 components 文件夹。像按钮、导航栏、表单输入框这些复用性比较高的组件都属于一般组件

  • 路由组件的卸载与挂载机制:当我们通过点击导航切换页面时,原来显示的路由组件在视觉上 “消失” 了,这时候默认情况下它是被卸载了。也就是说,这个组件的实例会被销毁,相关的资源也会被释放。当我们再次导航到这个页面时,组件会重新挂载,也就是重新创建实例并渲染

8.4 路由器工作模式

总结:

  • 如果更看重 URL 的美观性,不介意服务端配置,选择history模式

  • 如果更关注兼容性,不想处理服务端路径问题,选择hash模式

8.4.1 history 模式

介绍:这种模式的 URL 更好看,没有 #符号,和传统网站的 URL 很像。但有个缺点,项目上线后需要服务端配合处理路径,不然刷新页面会出现 404 错误

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  }
]

const router = createRouter({
  history: createWebHistory(), // 使用history模式
  routes
})

export default router

8.4.2 hash 模式

介绍:它的兼容性更好,因为不需要服务器端处理路径。不过 URL 带有 #符号,不太美观,而且在 SEO 优化方面效果相对较差

import { createRouter, createWebHashHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/contact',
    name: 'Contact',
    component: Contact
  }
]

const router = createRouter({
  history: createWebHashHistory(), // 使用hash模式
  routes
})

export default router

8.5 to的两种写法

在 Vue Router 中,<router-link> 组件用于实现路由导航,它的 to 属性有两种作用相同的写法

8.5.1 字符串写法
  • 语法:直接将路由路径作为字符串传递给 to 属性

  • 优点:简单直观

  • 缺点:在跳转的时候没办法传递参数

  • 例子:

  <router-link to="/home">首页</router-link>
8.5.2 对象写法
  • 语法:将一个对象传递给 to 属性,这个对象可以包含路径、参数、查询参数等信息

  • 优点:灵活强大

  • 例子:

  <router-link :to="{ path: '/home', query: { id: 1 }, hash: '#section' }">首页</router-link>

代码解释:

  • path 代表目标路由的路径
  • query 是一个对象,用于传递查询参数,最终会在 URL 中显示为 ?id=1
  • hash 表示 URL 中的锚点,会显示为 #section

8.6 命名路由

介绍:命名路由就是给路由规则起个名字,方便后续使用

 // router/index.ts
    import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
    import Home from '../views/Home.vue'
    import UserList from '../views/UserList.vue'
    import UserDetail from '../views/UserDetail.vue'
    
    const routes: Array<RouteRecordRaw> = [
      {
        name: 'home',
        path: '/',
        component: Home
      },
      {
        name: 'userList',
        path: '/users',
        component: UserList
      },
      {
        name: 'userDetail',
        path: '/users/:id',
        component: UserDetail,
        props: true // 开启props传参
      }
    ]
    
    const router = createRouter({
      history: createWebHistory(),
      routes
    })
    
    export default router

    <!-- Home.vue -->
    <template>
      <div class="container">
        <h1>用户管理系统</h1>
    
        <!-- 字符串写法(不推荐) -->
        <router-link to="/users" class="btn">用户列表</router-link>
    
        <!-- 对象写法:名字跳转 -->
        <router-link :to="{ name: 'userList' }" class="btn">用户列表</router-link>
    
        <!-- 对象写法:路径跳转 -->
        <router-link :to="{ path: '/users' }" class="btn">用户列表</router-link>
    
        <!-- 带参数的跳转 -->
        <router-link :to="{ name: 'userDetail', params: { id: 1 } }" class="btn">
    
          用户详情
        </router-link>
    
        <!-- 在TS中使用编程式导航 -->
        <button @click="goToUserDetail(2)" class="btn">查看用户2</button>
      </div>
    </template>
    
    <script setup lang="ts">
    import { useRouter } from 'vue-router'
    
    const router = useRouter()
    
    const goToUserDetail = (id: number) => {
      router.push({ name: 'userDetail', params: { id } })
    }
    </script>

8.7 嵌套路由

介绍:允许你在一个路由组件内部再嵌套其他路由组件

步骤:

  1. 编写News的子路由:Detail.vue
 <template>
    <ul class="news-list">
      <li>编号:xxx</li>
      <li>标题:xxx</li>
      <li>内容:xxx</li>
    </ul>
  </template>
  
  <script setup lang="ts" name="About">
  .....
  </script>
  ......
  1. 配置路由规则,使用children配置项
  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
  1. 跳转路由(记得要加完整路径):
  <router-link to="/news/detail">xxxx</router-link>
  <!-- 或 -->
  <router-link :to="{path:'/news/detail'}">xxxx</router-link>
  1. 在外层的News组件中用<router-view>占位
  <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>

8.7.1 query参数

  • 介绍:query 参数是路由传参的一种方式,类似 URL 后面的查询字符串(?key=value)

  • 两种写法:

    • 字符串写法:直接在路径后面拼接参数
   <!-- 基础示例 -->
    <router-link to="/news/detail?a=1&b=2&content=欢迎你">跳转</router-link>//传的是固定的值
    
    <!-- 动态拼接(使用插值表达式) -->
    <router-link :to="`/news/detail?id=${news.id}&title=${news.title}`">
      {{news.title}}
    </router-link>
  • 对象写法:通过 to 的对象形式传递
<!-- 使用 path 跳转 -->
<RouterLink 
  :to="{
    path: '/news/detail',
    query: {
      id: news.id,       // 假设 news 是组件内的数据
      title: news.title,
      content: news.content
    }
  }"=
  {{news.title}}
</RouterLink>

<!-- 使用 name 跳转(效果相同) -->
<RouterLink 
  :to="{
    name: 'NewsDetail', // 路由配置中的 name
    query: {
      id: news.id,
      title: news.title
    }
  }"
  {{news.title}}
</RouterLink>
  • 接收 query 参数:在目标组件中使用 useRoute 钩子获取参数
  <template>
    <div>
      <h1>{{ route.query.title || '默认标题' }}</h1>
      <p>ID: {{ route.query.id }}</p>
      <div>{{ route.query.content }}</div>
    </div>
  </template>
  
  <script setup lang="ts">
  import { useRoute } from 'vue-router'
  
  const route = useRoute()
  
  // 打印参数(开发时调试用)
  console.log(route.query) // 输出: { id: '1', title: '文章标题', content: '内容...' }
  
  // TypeScript 类型断言(可选)
  const newsId = route.query.id as string
  const newsTitle = route.query.title as string
  </script>

8.7.2 params参数

  • 两种写法:

    • 字符串写法:直接在路径中拼接参数
<RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">
  {{ news.title }}
</RouterLink>
  • 对象写法:使用 name 和 params 对象传参
<RouterLink 
  :to="{
    name: 'NewsDetail',  // 必须用name,不能用path
    params: {
      id: news.id,
      title: news.title,
      content: news.content
    }
  }"
>
  {{ news.title }}
</RouterLink>
  • 路由配置中占位:
  import { createRouter, createWebHistory } from 'vue-router'
  
  const router = createRouter({
    history:createWebHistory(),
    routes:[ 
      {
        name:'zhuye',
        path:'/home',
        component:Home
      },
      {
        name:'xinwen',
        path:'/news',
        component:News,
        children:[
          {
            name:' NewsDeta',
            path:'detail/:id/:title/:content?',// 占位参数用:开头
            component:Detail
          }
        ]
      },
      {
        name:'guanyu',
        path:'/about',
        component:About
      }
    ]
  })
  
  export default router
  • 在相应组件中接收参数:
  <template>
    <div>
      <h1>{{ route.params.title }}</h1>
      <p>{{ route.params.content }}</p>
    </div>
  </template>
  
  <script setup lang="ts">
  import { useRoute } from 'vue-router'
  
  // 声明route类型
  const route = useRoute<{
    params: {
      id: string
      title: string
      content: string
    }
  }>()
  
  // 打印参数验证
  console.log('接收的参数:', route.params)
  </script>

8.8 路由的props配置

介绍:让组件更便捷地接收路由参数

8.8.1 对象写法

把固定对象里的键值对当作 props 传递给组件

{
  path: '/user',
  name: 'User',
  component: UserComponent,
  props: { role: 'admin', theme: 'dark' }
}

在 UserComponent 组件里,我们可以这样接收 props:

<template>
  <div>当前角色:{{ role }}</div>
</template>

<script lang="ts" setup>
const props = defineProps<{
  role: string;
  theme: string;
}>()
</script>

注意:对象写法传递的是静态数据,不会随着路由的变化而改变

8.8.2 布尔值写法

当 props 设为 true 时,路由会自动把 params 参数转化为 props 传递给组件

{
  path: '/post/:id',
  name: 'Post',
  component: PostComponent,
  props: true
}

在 PostComponent 组件中接收 props 的方式如下:

<template>
  <div>文章ID{{ id }}</div>
</template>

<script lang="ts" setup>
const props = defineProps<{
  id: string;
}>()
</script>

注意:这种写法只对 params 参数有效,query 参数是无法传递的

8.8.3 函数写法

允许我们对参数进行处理之后再传递给组件

{
  path: '/search',
  name: 'Search',
  component: SearchComponent,
  props: (route) => ({
    keyword: route.query.q as string,
    page: parseInt(route.query.page as string || '1')
  })
}

在 SearchComponent 组件中接收 props:

 <template>
      <div>搜索关键词:{{ keyword }}, 页码:{{ page }}</div>
    </template>
    
    <script lang="ts" setup>
    const props = defineProps<{
      keyword: string;
      page: number;
    }>()
    </script>

8.8.4 总结

8.8.4.1场景一

只传 params 参数(动态路由)

// 路由配置
{
  path: '/article/:id',
  props: true //  布尔值写法
}

// 组件直接用props
const props = defineProps<{ id: string }>()
8.8.4.2场景二

只传固定数据(与路由参数无关)

// 路由配置
{
  path: '/about',
  props: { version: '1.0.0' } //  对象写法
}

// 组件直接用props
const props = defineProps<{ version: string }>()
8.8.4.3场景三

传 query 参数 或 需要处理参数

// 路由配置
{
  path: '/search',
  props: (route) => ({
    keyword: route.query.q as string, //  函数写法
    page: Number(route.query.page || 1) // 参数转换
  })
}

// 组件直接用props
const props = defineProps<{ keyword: string; page: number }>()

8.9 replace属性

  • 介绍:RouterLink 组件的replace属性用于控制路由跳转时浏览器历史记录的处理方式

  • 简单来说:决定了点击链接后,浏览器的历史记录是新增一条记录,还是替换当前记录

  • 历史记录的两种模式:

    • push(默认):追加一条新的历史记录。点击浏览器的 “后退” 按钮时,会返回到上一个页面

    • replace:替换当前的历史记录。点击 “后退” 按钮时,会直接跳过当前页面

  • 使用 replace 属性:

     <!-- 使用replace属性 -->
  <RouterLink to="/news" replace>新闻</RouterLink>
  • 使用场景:

    • 表单提交后跳转:避免用户刷新页面导致重复提交

    • 登录 / 注册后跳转:登录成功后替换登录页面,防止后退回到登录页

    • 重定向:当用户访问需要权限的页面时,重定向到登录页并替换当前历史

8.10 编程式路由导航

  1. 开始,从vue-router中导入两个 hook 函数

    import { useRoute, useRouter } from ‘vue-router’

  1. useRoute():获取当前路由的信息

  2. useRouter():获取路由实例,用于导航操作

  1. 完整的组件示例:
  <template>
    <div>
      <h1>用户详情页</h1>
      <p>用户ID: {{ route.params.id }}</p>
      <p>查询参数: {{ route.query.keyword }}</p>
  
      <button @click="goBack">返回</button>
      <button @click="goToUserList">用户列表</button>
      <button @click="goToUserProfile">个人资料</button>
    </div>
  </template>
  
  <script lang="ts" setup>
  import { useRoute, useRouter } from 'vue-router'
  
  // 获取当前路由信息
  const route = useRoute()
  // 获取路由实例
  const router = useRouter()
  
  // 返回上一页
  const goBack = () => {
    router.back()
  }
  
  // 跳转到用户列表页,使用路径方式
  const goToUserList = () => {
    router.push('/users')
  
    // 或者使用命名路由
    // router.push({ name: 'UserList' })
  }
  
  // 跳转到个人资料页,携带参数
  const goToUserProfile = () => {
    // 携带查询参数,URL会变成 /profile?name=John&age=30
    router.push({
      path: '/profile',
      query: {
        name: 'John',
        age: 30
      }
    })
  
    // 或者携带动态路由参数(需要路由配置中定义:id)
    // router.push({
    //   name: 'UserProfile',
    //   params: {
    //     id: '123'
    //   }
    // })
  }
  
  // 打印当前路由信息
  console.log('当前路由参数:', route.params)
  console.log('当前查询参数:', route.query)
  </script>

8.11 重定向

  • 介绍:当用户访问一个路径时,自动跳转到另一个已有的路由

  • 例子:首先需要安装并配置全局vue-router,然后在路由配置中添加重定向规则

  // router/index.ts
  import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
  
  // 定义路由
  const routes: Array<RouteRecordRaw> = [
    {
      path: '/',
      redirect: '/about' // 根路径重定向到/about
    },
    {
      path: '/about',
      name: 'About',
      component: () => import('../views/About.vue') // 假设我们有一个About组件
    },
    {
      path: '/home',
      name: 'Home',
      component: () => import('../views/Home.vue') // 假设我们有一个Home组件
    },
    {
      path: '/old-home',
      redirect: '/home' // 旧的home路径重定向到新的home路径
    }
  ]
  
  const router = createRouter({
    history: createWebHistory(),
    routes
  })
  
  export default router

8.11.1 路径重定向

直接指定要跳转的路径

{
  path: '/old-path',
  redirect: '/new-path'
}

8.11.2 命名路由重定向

通过路由名称跳转

{
  path: '/',
  redirect: { name: 'Home' }
}

8.11.3 动态重定向

根据条件动态决定跳转路径

{
  path: '/user/:id',
  redirect: to => {
    // to是目标路由对象,可以获取路径参数等信息
    return { name: 'User', params: { id: to.params.id } }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值