红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 前端学习笔记
  3. 正文

Vue3 学习笔记 5:路由

2025年9月12日 10点热度 0人点赞 0条评论

路由器和路由

新建一个 Vue3 项目,创建一个简单的 App.vue:

​
<template>
  <div class="app">
    <h2 class="title">Vue路由测试</h2>
    <!-- 导航区 -->
    <div class="navigate">
      <a href="#" class="active">首页</a>
      <a href="#">新闻</a>
      <a href="#">关于</a>
    </div>
    <!-- 展示区 -->
    <div class="main-content">
      这里展示内容
    </div>
  </div>
</template>
​
<script setup lang="ts">
​
</script>
​
<style scoped>
  /* App */
  .title {
    text-align: center;
    word-spacing: 5px;
    margin: 30px 0;
    height: 70px;
    line-height: 70px;
    background-image: linear-gradient(45deg, gray, white);
    border-radius: 10px;
    box-shadow: 0 0 2px;
    font-size: 30px;
  }
  .navigate {
    display: flex;
    justify-content: space-around;
    margin: 0 100px;
  }
  .navigate a {
    display: block;
    text-align: center;
    width: 90px;
    height: 40px;
    line-height: 40px;
    border-radius: 10px;
    background-color: gray;
    text-decoration: none;
    color: white;
    font-size: 18px;
    letter-spacing: 5px;
  }
  .navigate a.active {
    background-color: #64967E;
    color: #ffc268;
    font-weight: 900;
    text-shadow: 0 0 1px black;
    font-family: 微软雅黑;
  }
  .main-content {
    margin: 0 auto;
    margin-top: 30px;
    border-radius: 10px;
    width: 90%;
    height: 400px;
    border: 1px solid;
  }
</style>

包含三部分:

  • 标题

  • 导航栏

  • 内容展示区

最终希望实现的效果是点击按钮可以加载不同的内容,下面通过 vue 的路由器(Router)实现这一点。

首先安装 vue 的路由组件:

npm install vue-router

创建路由文件src/router/index.ts:

import {createRouter,createWebHistory} from 'vue-router'
import Home from '@/components/Home.vue'
import News from '@/components/News.vue'
import About from '@/components/About.vue'
​
// 创建路由器
const router = createRouter({
    history: createWebHistory(), // 路由器的工作模式
    routes:[ // 路由规则 
        {
            path:'/home',
            component: Home
        },
        {
            path: '/news',
            component: News
        },
        {
            path: '/about',
            component: About
        }
    ]
})
// 暴露路由器
export default router

在这个路由文件中通过createRouter方法创建了一个路由器(router),这个路由器包含三条路由(route)规则。每个路由规则包含一个路径和一个组件。

在 main.ts 中用 app 加载路由:

import './assets/main.css'
​
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
​
const app = createApp(App)
// 使用路由器
app.use(router)
app.mount('#app')

现在路由环境已经配置好了,Vue 应用已经可以通过路由器知道什么路径对应什么组件。但是还缺少一步——应用不知道路由规则中的组件应该加载到页面的什么地方。

需要在 App.vue 中加载路由规则中组件的部位使用RouterView进行占位:

​
<template>
  <div class="app">
    <h2 class="title">Vue路由测试</h2>
    <!-- 导航区 -->
    <div class="navigate">
      <a href="#" class="active">首页</a>
      <a href="#">新闻</a>
      <a href="#">关于</a>
    </div>
    <!-- 展示区 -->
    <div class="main-content">
      <RouterView/>
    </div>
  </div>
</template>
​
<script setup lang="ts">
import {RouterView} from 'vue-router'
​
</script>

现在修改浏览器路径(比如 http://localhost:5173/about)已经可以看到相应组件的加载。

要让链接点击后跳转到相应的路由路径并加载组件,就不能使用a标签,而是使用RouterLink组件:

​
<template>
  <div class="app">
    <h2 class="title">Vue路由测试</h2>
    <!-- 导航区 -->
    <div class="navigate">
      <RouterLink to="/home" class="active">首页</RouterLink>
      <RouterLink to="/news">新闻</RouterLink>
      <RouterLink to="/about">关于</RouterLink>
    </div>
    <!-- 展示区 -->
    <div class="main-content">
      <RouterView/>
    </div>
  </div>
</template>
​
<script setup lang="ts">
import {RouterView, RouterLink} from 'vue-router'
​
</script>

现在点击链接已经可以实现路由功能了,缺点是因为class="active"的缘故第一个按钮一直高亮,无法实现点击哪个按钮哪个高亮。

可以使用RouterLink的active-class属性:

    <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>

由路由控制和加载的组件被称作视图组件,通常会放在/src/views或/src/pages目录下。

此外,发生路由切换时,之前的组件会被从组件树卸载,这点可以通过使用生命周期钩子证明:

<script setup lang="ts" name="About">
import { onMounted,onUnmounted } from 'vue';
onMounted(()=>{
  console.log("About 组件被加载")
})
onUnmounted(()=>{
  console.log("About 组件被卸载")
})
</script>

路由器工作模式

前端框架的路由器通常有两种工作模式:

  • History

  • Hash

vue3 使用 History 模式:

import {createRouter,createWebHistory} from 'vue-router'
// ...
​
// 创建路由器
const router = createRouter({
    history: createWebHistory(), // 路由器的工作模式
    // ...
})
// 暴露路由器
export default router

此时浏览器中的请求路径没有#,比如 http://localhost:5173/home,更接近于传统 web 应用的请求路径。

使用 Hash 模式:

import {createRouter, createWebHashHistory} from 'vue-router'
// ...
​
// 创建路由器
const router = createRouter({
    history: createWebHashHistory(), // 路由器的工作模式
    // ...
})
// 暴露路由器
export default router

此时浏览器中的请求路径中会有#符号,比如:http://localhost:5173/home#/。

这两种模式各有优缺点:

模式 优点 缺点
History 没有#,SEO良好 上线部署时需要修改 Nginx 设置
Hash 上线部署时不需要特殊设置 有#、不利于 SEO

通常来说,面向 2C 的网站会使用 History 模式,面向 2B 的后台管理系统会使用 Hash 模式。

to 属性

RouterLink的to属性除了可以直接指定路径进行跳转:

      <RouterLink to="/home" active-class="active">首页</RouterLink>

还可以指定包含路径的对象:

      <RouterLink :to="{path:'/news'}" active-class="active">新闻</RouterLink>

注意属性值是表达式时属性名称前要使用:

路由是可以命名的:

const router = createRouter({
    history: createWebHistory(), // 路由器的工作模式
    routes:[ // 路由规则 
        {
            name:'zhuye',
            path:'/home',
            component: Home
        },
        {
            name:'xinwen',
            path: '/news',
            component: News
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})

此时 to 属性还可以通过路由名称进行跳转:

      <RouterLink :to="{name:'guanyu'}" active-class="active">关于</RouterLink>

嵌套路由

首先将新闻页面的新闻列表改为动态加载:

<template>
  <div class="news">
    <ul>
      <li v-for="news in newsList">
        <RouterLink to="#">{{ news.title }}</RouterLink>
      </li>
    </ul>
  </div>
</template>
​
<script setup lang="ts" name="News">
import { ref } from 'vue'
import { RouterLink } from 'vue-router'
interface News {
  id: number
  title: string
  content: string
}
const newsList = ref<News[]>([
  { id: 1, title: '头条新闻', content: '以色列轰炸卡塔尔' },
  { id: 2, title: '科技新闻', content: '网传Windows更新补丁损坏硬盘' },
  { id: 3, title: '震惊', content: '江南皮革厂倒闭了' }
])
</script>

为新闻页面增加一个新闻详情页作为子展示区:

<template>
  <ul class="news-list">
    <li>编号:xxx</li>
    <li>标题:xxx</li>
    <li>内容:xxx</li>
  </ul>
</template>
​
<script setup lang="ts" name="About">
​
</script>
​
<style scoped>
  .news-list {
    list-style: none;
    padding-left: 20px;
  }
​
  .news-list>li {
    line-height: 30px;
  }
</style>

在路由规则中添加一个子路由,指向新闻详情页的组件:

// ...
import Detail from '@/views/Detail.vue'
​
// 创建路由器
const router = createRouter({
    history: createWebHistory(), // 路由器的工作模式
    routes:[ // 路由规则 
        {
            name:'zhuye',
            path:'/home',
            component: Home
        },
        {
            name:'xinwen',
            path: '/news',
            component: News,
            children:[
                {
                    path: 'detail',
                    component: Detail
                }
            ]
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})
// 暴露路由器
export default router

注意,子路由的path属性不需要以/开头。

在新闻页增加一个RouterView占位:

<template>
  <div class="news">
    <ul>
      <li v-for="news in newsList">
        <RouterLink to="/news/detail">{{ news.title }}</RouterLink>
      </li>
    </ul>
    <div class="news-content">
      <RouterView></RouterView>
    </div>
  </div>
</template>

路由传参

query

可以通过 to 属性以拼接查询字符串的方式传递参数:

  <li v-for="news in newsList">
    <RouterLink :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{ news.title }}</RouterLink>
  </li>

在 Detail.vue 中接收参数:

<template>
  <ul class="news-list">
    <li>编号:{{ route.query.id }}</li>
    <li>标题:{{ route.query.title }}</li>
    <li>内容:{{ route.query.content }}</li>
  </ul>
</template>
​
<script setup lang="ts" name="About">
import {useRoute} from 'vue-router'
const route = useRoute()
</script>

通过useRoute(这是一个 hooks)获取到route,其query属性中有路由中传递的参数。

除了拼接字符串,可以用对象的方式更容易的传递 query 参数:

  <li v-for="news in newsList">
    <RouterLink :to="{path:'/news/detail',query:news}">{{ news.title }}</RouterLink>
  </li>

在读取路由参数时可能会通过解构赋值进行简化:

<template>
  <ul class="news-list">
    <li>编号:{{ query.id }}</li>
    <li>标题:{{ query.title }}</li>
    <li>内容:{{ query.content }}</li>
  </ul>
</template>
​
<script setup lang="ts" name="About">
import {useRoute} from 'vue-router'
const route = useRoute()
const {query} = route
</script>

但需要注意的是这样写是有问题的,因为route是一个响应式数据,但从中解构出的query是一个非响应式数据,在这个示例中,多个新闻链接都使用的是同一个组件Detail.vue,因此进行路由切换不会触发组件的卸载和加载,继而其内容不会发生变化。

此时需要借助toRefs方法解构出响应式数据:

<script setup lang="ts" name="About">
import {useRoute} from 'vue-router'
import { toRefs } from 'vue'
const route = useRoute()
const {query} = toRefs(route)
</script>

params

路由的 params 传参有点像后端框架定义的路径参数:

{
    name:'xinwen',
    path: '/news',
    component: News,
    children:[
        {
            path: 'detail/:id/:title/:content',
            component: Detail
        }
    ]
},

这里使用:xxx的方式在子路由的路径中为 params 参数占位。

传参:

      <li v-for="news in newsList">
        <RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{{ news.title }}</RouterLink>
      </li>

接收参数:

<template>
  <ul class="news-list">
    <li>编号:{{ params.id }}</li>
    <li>标题:{{ params.title }}</li>
    <li>内容:{{ params.content }}</li>
  </ul>
</template>
​
<script setup lang="ts" name="About">
import {useRoute} from 'vue-router'
import { toRefs } from 'vue'
const route = useRoute()
const {params} = toRefs(route)
</script>

与接收query参数类似,区别是要从route.params属性获取参数。

params 参数也可以用对象的方式传递:

      <li v-for="news in newsList">
        <RouterLink :to="{name:'xiangqing',params: news}">{{ news.title }}</RouterLink>
      </li>

需要注意的是,这种方式下必须使用路由名称(name)而非路径(path)指定路由,否则会报错。

最后,如果 params 参数的最后一位是可有可无的,可以:

path: 'detail/:id/:title/:content?',

路由 props 配置

路由使用 params 传参时,可以添加一个props:true属性:

{
    name: 'xiangqing',
    path: 'detail/:id/:title/:content?',
    component: Detail,
    props: true
}

此时路由会将 params 参数作为路由组件的 props 进行传递,换言之,可以在路由组件中使用接收 props 的方式接收 params 参数:

<template>
  <ul class="news-list">
    <li>编号:{{ id }}</li>
    <li>标题:{{ title }}</li>
    <li>内容:{{ content }}</li>
  </ul>
</template>
​
<script setup lang="ts" name="About">
defineProps(['id','title','content'])
</script>

如果是 query 传参,就需要以另一种方式实现作为组件的 props 传递:

{
    name: 'xiangqing',
    path: 'detail',
    component: Detail,
    props(route){
        return route.query
    }
}

此时路由的props属性是一个方法,其接收的参数实际上就是route对象,返回值是一个对象,其属性会作为路由组件的 props 进行传递。在这里使用了route.query以将路由的query参数作为路由组件的 props 进行传递,实际上你可以通过这种方式向路由组件传递任何想传递的内容。

最后还有一种形式:

props:{
    id: '你好',
    title: '哈哈',
    content: '嘿嘿'
}

直接将路由的 props 设置成一个对象。当然在这里没有任何意义,相当于写死了新闻内容,任何新闻链接都展示一样的内容。着相当于第二种方式的特异化,即没有使用参数直接返回一个特定内容的对象。

replace 属性

实现路由跳转后,会修改浏览器的历史记录,有两种模式:push 和 replace,默认是 push。

push 实际上就是将当前请求路径加入历史记录的栈顶,此时可以通过浏览器的后退操作退回到前一个页面(请求地址)。replace 则是用当前请求路径替换掉前一个路径在历史记录栈中的位置,这样就无法会退到之前的页面(请求地址)。

要让 RouterLink 标签使用 replace 跳转,只需要添加一个replace属性:

      <RouterLink replace :to="{name:'guanyu'}" active-class="active">关于</RouterLink>

此时如果通过这个 RouterLink 跳转,浏览器执行回退操作会退回到上上一个页面。

编程式路由跳转

除了使用 RouterLink 组件实现路由跳转,还可以用编程的方式实现路由跳转:

<template>
  <div class="news">
    <ul>
      <li v-for="news in newsList">
        <button @click="showNewsDetail(news)">新闻详情</button>
        <RouterLink :to="{ name: 'xiangqing', query: news }">{{ news.title }}</RouterLink>
      </li>
    </ul>
    <div class="news-content">
      <RouterView></RouterView>
    </div>
  </div>
</template>
​
<script setup lang="ts" name="News">
import { ref } from 'vue'
import { RouterLink, useRouter } from 'vue-router'
interface News {
  id: number
  title: string
  content: string
}
const newsList = ref<News[]>([
  { id: 1, title: '头条新闻', content: '以色列轰炸卡塔尔' },
  { id: 2, title: '科技新闻', content: '网传Windows更新补丁损坏硬盘' },
  { id: 3, title: '震惊', content: '江南皮革厂倒闭了' }
])
const router = useRouter()
const showNewsDetail = (news:News) => {
  router.push(`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`)
}
</script>

通过useRouter方法获取到路由器对象router,并且通过router.push在按钮点击事件中实现了路由跳转。

useRouter是获取路由器,useRoute是获取当前路由,注意有没有r的区别,这里很容易搞混。

router.push能接收的参数与RouterLink的to属性一致,因此除了普通的字符串形式的路径外,还可以是对象:

const showNewsDetail = (news: News) => {
  router.push({
    path: '/news/detail',
    query: { ...news }
  })
}

router 也可以以 replace 的方式跳转:router.replace(...)

重定向

当前路由设置中没有对根路径/的设置,因此访问项目根路径时不会加载任何路由组件,页面是空白的。要让根路径能展示有效的内容页,最简单的方式是为其设置一个路由重定向:

const router = createRouter({
    history: createWebHistory(), // 路由器的工作模式
    routes:[ // 路由规则 
        // ...
        {
            path:'/',
            redirect: '/home'
        }
    ]
})

现在只要访问根路径,比如http://localhost:5173/,就会自动跳转到/home路径。

参考资料

  • 尚硅谷Vue3入门到实战

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: route router vue
最后更新:2025年9月12日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号