Vue - 路由

Vue 3 中的路由系统主要依赖官方的 vue-router 库(当前最新稳定版是 4.x,专为 Vue 3 设计),用于实现单页应用(SPA)的页面跳转和状态管理。以下是 Vue 3 路由的详细解析:

一、核心概念

  1. 路由(Route):对应单页应用中的一个 “页面”,由路径(path)和组件(component)组成。
  2. 路由器(Router):管理所有路由的实例,通过 createRouter 创建。
  3. 路由出口(Router View):组件 <RouterView />,用于渲染当前路由对应的组件。
  4. 路由链接(Router Link):组件 <RouterLink />,用于生成跳转链接(替代 <a> 标签,避免页面刷新)。

二、基础用法

安装

npm install vue-router@4  # Vue 3 需使用 4.x 版本

image

快速入门

1.创建文件router文件夹下面的index.ts

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '@/components/Home.vue'
import NewsView from '@/components/News.vue'
import AboutView from '@/components/About.vue'

const router = createRouter({
  history: createWebHistory(), //路由器工作模式
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView,
    },
    {
      path: '/news',
      name: 'news',
      component: NewsView,
    },
    {
      path: '/about',
      name: 'about',
      component: AboutView,
    }
  ],
})

// 导出路由
export default router

2.创建 Home.vue、About.vue、News.vue,地址 src\components\Home.vue

<template>
    <div>
        <h1>Home</h1>
    </div>
</template>

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

export default defineComponent({
    setup() {
        return {
            msg: 'Hello Vue 3!',
        }
    },
})
</script>

3.src\main.ts 配置使用路由

import { createApp } from 'vue'
// import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

// app.use(createPinia())
app.use(router)

app.mount('#app')

4.app.vue 配置RouterView

RouterView:当前路由对应的组件会渲染在这里

src\App.vue 展示区显示

<template>
    <div class="app">
        <h2 class="title">Vue路由测试</h2>
        <!-- 导航区 -->
        <div class="navigate">
            <a href="#">首页</a>
            <a href="#">新闻</a>
            <a href="#">关于</a>
        </div>
        <!-- 展示区 -->
        <div class="main-content">
            <RouterView />
        </div>
    </div>
</template>

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

5.最后我们把跳转再加上RouterLink跳转,active-class 添加点击效果

<template>
    <div class="app">
        <h2 class="title">Vue路由测试</h2>
        <!-- 导航区 -->
        <div class="navigate">
            <RouterLink to="/" active-class="router-active">首页</RouterLink>
            <RouterLink to="/news" active-class="router-active">新闻</RouterLink>
            <RouterLink to="/about" active-class="router-active">关于</RouterLink>
        </div>
        <!-- 展示区 -->
        <div class="main-content">
            <RouterView />
        </div>
    </div>
</template>

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

点击添加效果:

.router-active {
    color: red !important;
    background-color: aquamarine !important;
    font-weight: bold;
}

三、两点注意

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

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

四、路由工作模式

  1. history 模式

    优点: URL 更加美观,不带有 #,更接近传统的网站 URL
    缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有 404 错误。

    const router = createRouter ({
    history:createWebHistory (), //history 模式
    /******/
    })
    

    history: createWebHistory(import.meta.env.BASE_URL),

    import.meta.env.BASE_URL

    import.meta 是 ECMAScript 标准中引入的一个元数据对象,用于获取与模块相关的信息。在 Vite 等构建工具中,import.meta.env 用于访问环境变量。BASE_URL 是 Vite 预设的一个环境变量,它表示项目的基础公共路径。

    作用

    • 处理项目部署路径:当项目部署在服务器的非根路径下时,比如部署在 https://example.com/myapp/ ,而不是 https://example.com/BASE_URL 就会被设置为 /myapp/ ,将其传递给 createWebHistory ,Vue Router 就能够正确处理所有路由路径,使得在访问不同路由时,路径都能基于这个基础路径来生成。例如,访问 /about 时,实际请求的 URL 就是 https://example.com/myapp/about
    • 确保静态资源路径正确:除了路由相关,BASE_URL 还能帮助项目中正确引用静态资源(如图片、CSS 文件、JavaScript 文件等)。如果没有正确设置,在非根路径部署的情况下,可能会导致静态资源无法正确加载。
  2. hash 模式

    优点:兼容性更好,因为不需要服务器端处理路径。
    缺点: URL 带有 # 不太美观,且在 SEO 优化方面相对较差。

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

五、RouterLink 路由跳转方式 to

1. 路由跳转方式

  • 声明式跳转:使用 <RouterLink to="路径">(推荐,自动处理历史记录)。

    <!-- 基础用法 -->
    <RouterLink to="/news">新闻</RouterLink>
    
    <!-- 带参数的对象形式 -->
    <RouterLink :to="{ path: '/news', query: { id: 1 } }">新闻详情</RouterLink>
    
    <!-- 带参数的对象形式:name -->
    <RouterLink :to="{ name: 'aboutPage' }" active-class="router-active">关于</RouterLink>
    
  • 编程式跳转:通过 useRouter 钩子获取路由实例,调用 push/replace 方法。

    <script setup>
    import { useRouter } from 'vue-router'
    const router = useRouter()
    
    // 跳转
    const goToNews = () => {
      router.push('/news')  // 字符串路径
      // 或对象形式
      router.push({ path: '/news', query: { id: 1 } })
    }
    
    // 替换当前历史记录(无法回退)
    const replaceToAbout = () => {
      router.replace('/about')
    }
    </script>
    

2. 路由参数

  • Query 参数:类似 URL 中的查询字符串(?id=1&name=test),通过 useRoute 钩子的 query 属性获取。

    <!-- 跳转时携带参数 -->
    <RouterLink :to="{ path: '/news', query: { id: 1, name: '新闻1' } }">新闻1</RouterLink>
    
    <!-- 目标组件中获取参数 -->
    <script setup>
    import { useRoute } from 'vue-router'
    const route = useRoute()
    console.log(route.query.id)  // 1
    console.log(route.query.name)  // '新闻1'
    </script>
    
  • Params 参数:路径中的动态片段(如 /news/:id),需在路由规则中定义。

    // 路由规则中定义 params
    {
      path: '/news/:id',  // 动态参数 id
      name: 'NewsDetail',
      component: NewsDetail
    }
    
    <!-- 跳转时携带 params -->
    <RouterLink :to="{ name: 'NewsDetail', params: { id: 1 } }">新闻1</RouterLink>
    <!-- 注意:params 必须配合 name 使用,不能用 path -->
    
    <!-- 目标组件中获取 params -->
    <script setup>
    import { useRoute } from 'vue-router'
    const route = useRoute()
    console.log(route.params.id)  // 1
    </script>
    

六、嵌套路由

先把news.vue挪动一下位置

image

子路由添加 children

{
      path: '/news',
      name: 'newsPage',
      component: NewsView,
      children: [
        {
          path: 'detail',
          name: 'newsDetail',
          component: NewsDetail,
        },
      ],
    },

在需要展示子路由的地方也使用 RouterView

这里用 query 方式传参

news.vue

<template>
    <div class="news">
        <div class="news-list">
            <ul v-for="theNew in newsList" :key="theNew.id">
                <li>
                    <RouterLink :to="{ path: '/news/detail', query: { id: theNew.id } }">{{ theNew.title }}</RouterLink>
                </li>
            </ul>
        </div>
        <!-- 展示区 -->
        <div class="news-content">
            <RouterView />
        </div>
    </div>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';

const newsList = reactive([
    { id: 'asfdtrfay01', title: '经典名著新译本推出', content: '更贴合现代读者阅读习惯' },
    { id: 'asfdtrfay02', title: '传统戏剧展演活动开启', content: '多剧种轮番登台献艺' },
    { id: 'asfdtrfay03', title: '博物馆推出新展览', content: '展示珍贵历史文物' },
    { id: 'asfdtrfay04', title: '文化创意市集热闹举行', content: '各类文创产品琳琅满目' }
]);

</script>

使用 useRoute,接收参数传值 route

detail.vue

<template>
    <ul class="news-list">
        <li>{{ route.query.id }}</li>
        <li>标题:xxx</li>
        <li>内容:xxx</li>
    </ul>
</template>

<script setup lang="ts" name="detail">
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.id)
</script>

image

如果用 params 方式

src/router/index.ts

 {
      path: '/news',
      name: 'newsPage',
      component: NewsView,
      children: [
        {
          path: 'detail/:id/:title/:content',
          name: 'newsDetail',
          component: NewsDetail,
        },
      ],
    },

src\views\news\News.vue

                    <RouterLink
                        :to="{ name: 'newsDetail', params: { id: theNew.id, title: theNew.title, content: theNew.content } }">
                        {{ theNew.title }}</RouterLink>

src\views\news\Detail.vue

<template>
    <ul class="news-list">
        <li>{{ route.params.id }}</li>
        <li>{{ route.params.title }}</li>
        <li>{{ route.params.content }}</li>
    </ul>
</template>

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

image

七、props 配置

在 Vue 中,props 是组件之间传递数据的主要方式,用于从父组件向子组件传递数据。它是实现组件复用和通信的核心机制之一,以下是关于 Vue 中 props 的详细解析:

1. 子组件声明接收 props

子组件需要显式声明它期望接收的 props,可以指定名称、类型、默认值等规则:

  • defineProps:Vue 3 <script setup> 中专门用于声明 props 的宏,无需导入即可使用。

  • TypeScript 类型声明:也可以用 TypeScript 类型直接声明:

const props = defineProps<{
  message: string
  count?: number // 可选参数
}>()

2. 父组件传递 props

父组件在使用子组件时,通过属性的形式传递数据:

<!-- 父组件 Parent.vue -->
<template>
  <div>
    <!-- 传递静态值 -->
    <Child message="Hello" count="10" /> <!-- 注意:这里的 "10" 是字符串,会被自动转换为数字(根据子组件类型) -->
    
    <!-- 传递动态值(使用 v-bind 或 : 简写) -->
    <Child 
      :message="dynamicMsg" 
      :count="dynamicCount" 
    />
  </div>
</template>

<script setup lang="ts">
import Child from './Child.vue'
import { ref } from 'vue'

const dynamicMsg = ref('动态消息')
const dynamicCount = ref(20)
</script>

例子:

{
      path: '/news',
      name: 'newsPage',
      component: NewsView,
      children: [
        {
          path: 'detail/:id/:title/:content',
          name: 'newsDetail',
          component: NewsDetail,
          //第1种:将params的参数作为props传递给子组件(只能传递params参数,方便)
           //props: true,

          //第2种:函数式(可以传递params参数,也可以传递query参数)
          props: (route) => ({
            id: route.params.id,
            title: route.params.title,
            content: route.params.content,
            RouteMessage: '动态传的属性',
          }),
		  
          //第3种:对象式(可以传递params参数,也可以传递query参数)
          //props: { RouteMessage: '动态传的属性' },
        },
      ],
    },

src\views\news\News.vue 父组件

<!-- 展示区 -->
        <div class="news-content">
            <RouterView StaticMessage="静态传的属性" />
        </div>

src\views\news\Detail.vue 子组件

<template>
    <ul class="news-list">
        <li>{{ id }}</li>
        <li>{{ title }}</li>
        <li>{{ content }}</li>
        <li>{{ StaticMessage }}</li>
        <li>{{ RouteMessage }}</li>
    </ul>
</template>

<script setup lang="ts" name="detail">
//第一种写法
// defineProps<{
//     id: string,
//     title: string,
//     content: string
// }>();

//第二种写法
defineProps(['id', 'title', 'content', 'StaticMessage', 'RouteMessage'])

</script>

image

八、编程式导航

在 Vue 中,编程式导航指的是通过JavaScript 代码实现路由跳转,而不是通过 <RouterLink> 组件(声明式导航)。这在需要根据逻辑条件动态跳转的场景中非常有用(如登录成功后跳转首页)。

Vue Router 提供了一系列 API 实现编程式导航,核心方法基于路由实例(通过 useRouter 获取)。

一、核心方法

1. router.push()

最常用的跳转方法,新增一条历史记录(类似浏览器的 history.pushState),用户点击后退按钮可以回到上一页。

基本用法

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()

// 1. 字符串路径
const goToHome = () => {
  router.push('/home')
}

// 2. 对象形式(推荐,支持更多参数)
const goToNews = () => {
  router.push({
    path: '/news', // 路径
    query: { id: 1, name: '新闻1' } // 查询参数(?id=1&name=新闻1)
  })
}

// 3. 命名路由(配合路由配置中的 name 属性)
const goToAbout = () => {
  router.push({
    name: 'About', // 路由名称(在路由规则中定义)
    params: { userId: 123 } // 动态参数(需在路由规则中声明 /about/:userId)
  })
}
</script>

2. router.replace()

替换当前历史记录(类似 history.replaceState),不会新增记录,用户点击后退会跳过当前页。

用法

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()

// 登录成功后跳转首页,不保留登录页历史
const loginSuccess = () => {
  router.replace('/home')
  
  // 也支持对象形式
  router.replace({
    path: '/home',
    query: { from: 'login' }
  })
}
</script>

3. router.go(n)

基于当前历史记录进行前进 / 后退(类似浏览器的 history.go),n 为整数:

  • n = 1:前进一页
  • n = -1:后退一页
  • n = 2:前进两页

用法

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()

const goBack = () => {
  router.go(-1) // 后退一页
}

const goForward = () => {
  router.go(1) // 前进一页
}
</script>

便捷别名:

  • router.back():等价于 router.go(-1)(后退)
  • router.forward():等价于 router.go(1)(前进)

二、参数传递

编程式导航支持两种参数传递方式:queryparams

1. Query 参数(查询字符串)

  • 显示在 URL 中(如 ?id=1&name=test
  • 刷新页面不会丢失
  • 无需在路由规则中预先定义
// 跳转时携带
router.push({
  path: '/news',
  query: { id: 1, name: '新闻1' }
})

// 目标组件中获取
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query.id) // 1
console.log(route.query.name) // '新闻1'

2. Params 参数(动态路径参数)

  • 嵌入在 URL 路径中(如 /news/1
  • 刷新页面不会丢失
  • 必须在路由规则中预先定义(如 path: '/news/:id'
// 路由规则中定义
const routes = [
  {
    path: '/news/:id', // 声明动态参数 id
    name: 'NewsDetail',
    component: NewsDetail
  }
]

// 跳转时携带(必须用 name 匹配路由,不能用 path)
router.push({
  name: 'NewsDetail',
  params: { id: 1 }
})

// 目标组件中获取
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id) // 1

三、常见场景

  1. 登录成功后跳转

    const handleLogin = async () => {
      const success = await api.login(credentials)
      if (success) {
        router.push('/dashboard') // 登录成功跳转到 dashboard
      }
    }
    
  2. 条件判断跳转

    const goToTarget = () => {
      if (hasPermission) {
        router.push('/target')
      } else {
        router.push('/forbidden')
      }
    }
    
  3. 关闭弹窗后返回上一页

    const closeModal = () => {
      router.back() // 关闭弹窗后后退
    }
    

四、注意事项

  1. paramspath 冲突:使用 params 时,必须通过 name 指定路由,不能用 path,否则 params 会被忽略。

    // 错误:用 path 时 params 无效
    router.push({ path: '/news', params: { id: 1 } })
    
    // 正确:用 name 匹配
    router.push({ name: 'NewsDetail', params: { id: 1 } })
    
  2. 路由跳转后的回调pushreplace 支持回调函数(适用于 Vue Router 3.x,4.x 推荐用 await):

    // 4.x 中推荐用 await
    const navigate = async () => {
      await router.push('/home')
      console.log('跳转完成')
    }
    
  3. 避免重复跳转同一路由报错:Vue Router 4.x 中,重复跳转同一路由会报错,可通过捕获错误解决:

    const goToSameRoute = () => {
      try {
        router.push('/current-route')
      } catch (err) {
        // 忽略重复跳转错误
      }
    }
    

九、路由 replace 属性

在 Vue Router 中,replace 是路由跳转的一种方式,与常见的 push 方法不同,它的核心特点是替换当前历史记录,而不是新增一条记录。

一、基本用法

1. 声明式导航(模板中)

使用 <RouterLink> 组件时,添加 replace 属性实现替换式跳转:

<!-- 普通跳转(push,新增历史记录) -->
<RouterLink to="/news">新闻</RouterLink>

<!-- 替换式跳转(replace,替换当前历史记录) -->
<RouterLink to="/news" replace>新闻</RouterLink>

2. 编程式导航(脚本中)

通过 useRouter 获取路由实例,调用 replace 方法:

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()

// 编程式替换跳转
const goToNews = () => {
  // 字符串路径
  router.replace('/news')
  
  // 或对象形式(支持携带参数)
  router.replace({
    path: '/news',
    query: { id: 1 } // 携带查询参数
  })
}
</script>

二、与 push 的核心区别

跳转方式 历史记录行为 后退按钮效果 适用场景
push 新增一条历史记录 点击后退会回到上一个页面 普通跳转(如列表 → 详情)
replace 替换当前历史记录 点击后退会跳过当前页,回到上上个页面 不需要保留当前页历史的场景

三、典型使用场景

  1. 登录页跳转
    用户登录后跳转到首页,通常不需要保留登录页的历史记录(避免后退时回到登录页):

    // 登录成功后
    router.replace('/home')
    
  2. 页面重定向
    例如从 / 重定向到 /home 时,使用 replace 避免用户后退到空路径:

    // 路由配置中
    const routes = [
      {
        path: '/',
        redirect: { path: '/home', replace: true } // 重定向时替换历史
      }
    ]
    
  3. 临时页面跳转
    如弹窗内的跳转、临时预览页等,不需要用户后退时再次访问的场景。

四、注意事项

  • replace 不会改变历史记录的数量,只是替换了当前位置的记录。
  • 携带参数的方式与 push 完全一致(支持 queryparams 等)。
  • 在 Vue Router 4.x(Vue 3 版本)中,replace 的用法与 3.x(Vue 2 版本)完全一致,没有语法差异。

十、页面重定向

页面重定向
例如从 / 重定向到 /home 时,使用 replace 避免用户后退到空路径:

const routes = [
  {
    path: '/',
    redirect: '/home'
  }
]
// 路由配置中
const routes = [
  {
    path: '/',
    redirect: { path: '/home', replace: true } // 重定向时替换历史
  }
]

十一、路由懒加载 component: () => import('../About.vue')

路由懒加载(Route Lazy Loading)是 Vue、React 等单页应用(SPA)中优化性能的重要技术,核心思想是在需要的时候才加载路由对应的组件代码,而不是在应用初始加载时就加载所有组件。

为什么需要路由懒加载?

单页应用的特点是所有代码最终会打包成一个或少数几个 JS 文件。如果应用规模较大(有很多页面组件),初始打包的文件会非常大,导致:

  • 首屏加载时间过长(用户需要等待所有代码下载完成才能看到页面)
  • 浪费带宽(用户可能永远不会访问某些页面,却要下载其代码)

路由懒加载的原理

通过 ES6 的动态 import () 语法,将不同路由对应的组件分割成多个独立的代码块(chunk)。只有当用户访问该路由时,浏览器才会异步下载对应的代码块并执行。

例如,不使用懒加载时,所有组件会被打包到同一个 app.js 中:

// 不使用懒加载:初始加载时就包含 About 组件代码
import About from '../views/About.vue'
const routes = [
  { path: '/about', component: About }
]

使用懒加载后,About 组件会被单独打包成 About.js

// 使用懒加载:只有访问 /about 时才加载 About 组件代码
const routes = [
  { 
    path: '/about', 
    component: () => import('../views/About.vue') // 动态 import
  }
]

路由懒加载的优势

  1. 减小初始加载体积:只加载首页必需的代码,提升首屏加载速度。
  2. 节省带宽资源:用户未访问的页面代码不会被下载。
  3. 优化用户体验:更快的首屏加载速度,减少用户等待时间。

在 Vue Router 中的实现

Vue Router 官方推荐使用动态 import() 实现路由懒加载,语法如下:

// 路由配置文件(router/index.js)
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/HomeView.vue') // 懒加载 Home 组件
  },
  {
    path: '/about',
    name: 'About',
    // 注释说明懒加载的效果
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router
  • 其中 /* webpackChunkName: "about" */ 是可选的魔法注释,用于指定打包后的代码块名称(方便调试)。
  • 打包后会生成类似 about.[hash].js 的独立文件,仅在访问 /about 时加载。

点击关于的时候,再加载

image

总结

路由懒加载是通过动态导入语法,将路由组件代码分割成多个小块,按需加载的技术。它能有效优化单页应用的加载性能,是大型 Vue 项目的必备优化手段。核心就是 “不用不加载,用的时候再加载”。

posted @ 2025-09-10 18:14  【唐】三三  阅读(10)  评论(0)    收藏  举报