vue-router零基础入门
vue-router 是 Vue 官方的路由管理器,用于实现单页面应用(SPA)的路由跳转 —— 简单说,就是让页面 URL 变化时,不刷新整个页面,只替换页面中的指定内容,实现 “无刷新跳转”。

vue-router零基础入门

发布时间:2025-12-20 (2025-12-20)

vue-router 是 Vue 官方的路由管理器,用于实现单页面应用(SPA)的路由跳转 —— 简单说,就是让页面 URL 变化时,不刷新整个页面,只替换页面中的指定内容,实现 “无刷新跳转”。

如果刚开始没有选择vue-router,需要npm i下载指定的vue-router版本

npm i vue-router@4.5.0

在src的rourer目录下创建index.ts

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView,
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue'),
    },
  ],
})

export default router

在main.ts里面使用

import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)

简单使用

<template>
  <div>
    <div class="nav">
        <router-link to="/">home</router-link>
        <router-link to="/about">about</router-link>
    </div>
    <div class="view">
      <router-view></router-view>
    </div>

  </div>

</template>

这里引入了两个组件,router-view就是访问对应路由的时候,对应的视图文件就会被替换到这里来

router-link就是一个特殊的a标签,取消了默认跳转,并且加上了一些特定的class样式

路由规则routes结构

import type {RouteRecordRaw} from "vue-router";


const routes: Readonly<RouteRecordRaw[]> = [
    {
        path: '/',
        name: 'home',
        component: HomeView,
    },
    {
        path: '/about',
        name: 'about',
        // route level code-splitting
        // this generates a separate chunk (About.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import('../views/AboutView.vue'),
    },
]

子路由和从属关系

如果有子路由,把它放到children里面

要特别明白路由的从属关系以及路由的层级关系

const routes: Readonly<RouteRecordRaw[]> = [
    {
        path: '/', // 生效的路径
        name: 'home', // 路由的name
        component: HomeView, // 路由对应的视图组件,
    },
    {
        path: '/about',
        name: 'about',
        component: () => import('../views/AboutView.vue'),
    },
    {
        path: "/admin",
        name: "admin",
        component: AdminView,
        children: [
            {
                path: "", // /admin
                name: "adminHome",
                component: AdminHome,
            },
            {
                path: "info", // /admin/info
                name: "infoHome",
                component: infoHome,
            }
        ]
    },
    {
        path: "/admin1",
        name: "admin1",
        children: [
            {
                path: "", // /admin
                name: "adminHome1",
                component: AdminHome,
            },
            {
                path: "info", // /admin/info
                name: "infoHome1",
                component: infoHome,
            }
        ]
    }
]

其中adminHome1和infoHome1都是一级路由,和about、home都是同一级别的路由,替换router-view也只替换一个,但是这两个路由(adminHome1和infoHome1)是从属与admin1的,此时admin1只是一个路由分组

其中adminHome和infoHome是二级路由,路由从属与admin,在替换adminHome和infoHome的视图文件的时候,就得替换两个router-view

路由跳转

  1. 使用router-link跳转
<div class="nav">
  <router-link to="/">home</router-link>
  <router-link to="/about">about</router-link>
</div>
<div class="nav">
  <router-link :to="{name: 'home'}">home</router-link>
  <router-link :to="{name: 'about'}">about</router-link>
</div>
  1. 使用router对象跳转
<div class="nav">
  <a href="javascript:void 0" @click="goRouter1('home')">home</a>
  <a href="javascript:void 0" @click="goRouter2('/about')">about</a>
  <a href="javascript:void 0" @click="goRouter3('adminHome')">admin Home</a>
</div>
  
<script lang="ts" setup>
import router from "@/router";
import {useRouter} from "vue-router";

const router1 = useRouter()

// 用路由名称跳
function goRouter1(name: string){
  router.push({name: name})
}

// 用路径跳转
function goRouter2(path: string){
  router.push(path)
}

// 使用useRouter实例跳
function goRouter3(name: string){
  router1.push({name: name})
}

</script>

两种路由模式

  1. history模式 createWebHistory

/xxx

/admin

/admin/user

如果没有正确的配置nginx,在部署之后有可能会出现404

  1. hash模式 createWebHashHistory

/#/admin1

/#/

/#/admin1/home

路由参数

  1. 查询参数

?name=xxx&age=22

import {useRoute} from "vue-router";

const route = useRoute()
console.log(route.query) // 查询参数

需要在对应的视图文件里面去写

  1. useRoute和useRouter的区别

useRoute是路由信息,一般用于获取当前视图的路由信息

useRouter是路由器对象,一般用于路由操作

  1. 动态路径参数

/article/1 /article/2 /article/:id

定义动态路径

{
    path: "/article/:id",
    name: "articleDetail",
    component: articleDetail,
}

路由跳转的时候,需要传这个动态参数

<router-link :to="{name: 'articleDetail', params: {id: 1}}">article 1</router-link>
<router-link :to="{name: 'articleDetail', params: {id: 2}}">article 2</router-link>
<router-link :to="{name: 'articleDetail', params: {id: 3}}">article 3</router-link>

在视图中获取这个值

<script setup lang="ts">
import {useRoute} from "vue-router";
import {watch} from "vue";

const route = useRoute()

watch(()=>route.params, ()=>{
  console.log("路由路径发生了变化")
})

</script>

<template>
  <div>article detail {{ route.params.id }}</div>
</template>

还可以通过watch监听路由的变化

路由懒加载

之前我们定义的视图文件

import HomeView from '../views/HomeView.vue'
import AdminHome from '../views/admin/home.vue'
import infoHome from '../views/admin/info.vue'
import AdminView from '../views/admin/index.vue'
import articleDetail from '../views/article_detail.vue'

都是直接在index.ts的上方直接import导入的

会有一个问题,在打包的时候,这些视图文件都会被打进一个js文件里面,就会造成这个文件特别大

首次加载就会很久,造成页面白屏

使用import()动态导入的方式,实现路由懒加载

{
    path: "/article/:id",
    name: "articleDetail",
    component: () => import("@/views/article_detail.vue"),
}

打包的时候就会根据路由去创建不同的路由文件,只有访问对应路由的时候,才会加载对应的js文件

路由元信息

在定义路由的时候,可以额外给路由设置一些信息

在使用路由信息的时候可以获取到这些元信息

{
    path: "/admin",
    name: "admin",
    component: AdminView,
    meta:{
        title: "后台",
        login: true
    },
    children: [
        {
            path: "", // /admin
            name: "adminHome",
            component: AdminHome,
            meta:{
                title: "后台Home"
            },
        },
        {
            path: "info", // /admin/info
            name: "infoHome",
            component: infoHome,
            meta:{
                title: "后台Info"
                login: false
            },
        }
    ]
},

这个meta有一个特点,如果当前路由没有对应的值

那么就会往上找父级路由有没有对应的meta信息

比如 在adminHome这个页面,获取meta就是 {title: '后台Home', login: true}

在infoHome页面获取的meta就是 {title: '后台Info', login: false}

ts定义

import 'vue-router';

declare module 'vue-router' {
    interface RouteMeta {
        title?: string
    }
}

作用:

  1. 在路由守卫的时候,通过元信息表示这个路由是否需要登录
  2. 在类似菜单这样的组件的时候,直接进行数据展示

路由守卫

路由守卫(Navigation Guards)是 Vue Router 提供的钩子函数,用于在路由跳转过程中进行拦截、校验或处理,比如登录验证、权限控制、页面跳转前的确认等。

路由守卫主要分为 3 类:全局守卫路由独享守卫组件内守卫

全局守卫

全局守卫注册在路由实例上,对所有路由跳转生效,常用的有:

  • router.beforeEach:路由跳转前触发(最常用)
  • router.afterEach:路由跳转后触发(无拦截能力,仅做后置处理)
// 全局前置守卫:登录验证示例
router.beforeEach((to, from, next) => {
  // to:目标路由对象
  // from:当前即将离开的路由对象
  // next:继续跳转的函数(必须调用,否则路由不会跳转)
  
  // 判断目标路由是否需要登录
  if (to.meta.requiresAuth) {
    // 模拟从本地存储获取登录状态
    const isLogin = localStorage.getItem('token')
    if (isLogin) {
      next() // 已登录,正常跳转
    } else {
      next('/login') // 未登录,跳转到登录页
    }
  } else {
    next() // 不需要登录,直接跳转
  }
})

// 全局后置守卫:页面跳转后处理(比如修改页面标题)
router.afterEach((to, from) => {
  if (to.meta.title) {
    document.title = to.meta.title // 从路由元信息获取标题
  }
})

路由独享守卫

直接在路由配置中定义,仅对当前路由生效,仅 beforeEnter

const routes = [
  {
    path: '/detail/:id',
    name: 'Detail',
    component: () => import('@/views/Detail.vue'),
    // 路由独享守卫
    beforeEnter: (to, from, next) => {
      // 校验路由参数是否合法
      if (Number.isNaN(Number(to.params.id))) {
        next('/404') // 参数不合法,跳转到404
      } else {
        next() // 参数合法,正常跳转
      }
    },
    meta: { title: '详情页' }
  }
]

组件内守卫

在 Vue3 组件中,会触发两个路由钩子函数,如下:

  • onBeforeRouteUpdate:路由参数更新时触发(比如从 /detail/1 跳转到 /detail/2
  • onBeforeRouteLeave:离开组件前触发(比如确认是否放弃未保存的表单)
<template>
  <div>个人中心</div>
</template>

<script setup>
import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router'

// 路由参数更新
onBeforeRouteUpdate((to, from, next) => {
  console.log('参数更新', to.params)
  next()
})

// 离开组件前
onBeforeRouteLeave((to, from, next) => {
  const confirmLeave = confirm('是否确定离开?')
  next(confirmLeave) // 直接传布尔值,true=跳转,false=取消
})
</script>

正则表达式路由

定义路由的时候,除了使用:id表示动态路径

还可以使用:params(正则表达式) 去编写正则表达式的路由规则,更加灵活

 {
    path: '/user/:id(\\d+)', // 注意:JS中反斜杠需要转义,所以写 \\d+ 而不是 \d+
    name: 'User',
    component: () => import('../views/User.vue')
  },
  
   // 匹配 /article/YYYY-MM-XXXX 格式(年份4位、月份2位、ID任意数字)
  {
    path: '/article/:dateId(\\d{4}-\\d{2}-\\d+)',
    name: 'Article',
    component: () => import('../views/Article.vue')
  },

404路由

用于匹配所有未定义的路由并展示对应的页面

 {
    // 通配符匹配所有未定义的路径
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: NotFound
  }

兄弟组件通过路由通信

之前我们讲组件通信的时候,讲到过可以使用路由进行组件通信,实现起来也很简单

A组件通过修改路由参数,B组件监听路由参数即可完成

通过路由通信有一个好处就是刷新之后路由参数还在

a组件

<script setup lang="ts">
import {onBeforeRouteUpdate} from "vue-router";
import {ref, watch} from "vue";
import {useRoute} from "vue-router";
const route = useRoute()
const tag = ref("")

// 刷新的时候不会调这个回调
// onBeforeRouteUpdate((to, from, next)=>{
//   const t = to.query.tag
//   if (t){
//     tag.value = t
//   }
//   next()
// })

watch(()=>route.query.tag, (newValue, oldValue)=>{
  tag.value = newValue
}, {immediate: true})


</script>

<template>
  <div>
    文章列表 搜索的内容是: {{ tag }}
  </div>
</template>

<style scoped>

</style>

b组件

<script setup lang="ts">
import {useRouter} from "vue-router";
import {useRoute} from "vue-router";

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

function search(tag: string) {
  router.push({
    name: route.name, query: {
      tag: tag
    }
  })
}

</script>

<template>
  <div>
    标签云
    <div>
      <span @click="search('python')">python</span>
      <span @click="search('go')">go</span>
      <span @click="search('JavaScript')">JavaScript</span>
    </div>
  </div>
</template>

<style scoped>

</style>

动态路由

router.addRoute() 是 Vue Router 实例的方法,用于在应用运行时动态注册新的路由规则,而不是在初始化时一次性定义所有路由。它弥补了静态路由配置的不足,特别适合需要根据用户权限、异步数据等动态生成路由的场景。

基础语法

// 语法1:添加单个路由
router.addRoute(routeRecord)

// 语法2:添加到指定的路由分组(嵌套路由)
router.addRoute(parentName, routeRecord)

在前端加载

// 动态添加路由  /route
router.addRoute({
    path: "/route", // /admin/info
    name: "route",
    component: () => import("@/views/route.vue"),
})
// 添加到admin1这个路由组里面  // /admin1/route
router.addRoute("admin1", {
    path: "route", // /admin/info
    name: "route",
    component: () => import("@/views/route.vue"),
})

异步加载

请求路由文件,然后加载到路由里面

let addRouteStatus = false
const viewModules = import.meta.glob('@/views/**/*.vue')

// 路由前置守卫
router.beforeEach(async (to, from, next) => {
    if (!addRouteStatus) {
        const routeList = await getRoute()
        const route = routeList[0]
        const componentPath = `/src/${route.component}`
        const component = viewModules[componentPath]

        router.addRoute({
            name: route.name,
            path: route.path,
            component: component,
        })
        
        router.addRoute({
            path: "/:pathMatch(.*)*", // /admin/info
            name: "notfound",
            component: () => import("@/views/404.vue"),
        })

        addRouteStatus = true
        return next({...to, replace: true})
    }
    console.log("前置 from", from)
    console.log("前置 to", to)
    if (to.meta.login) {
        // 这个路由需要登录
        const token = localStorage.getItem("token")
        if (!token) {
            Message.warning("需要登录")
            next("/login")
            return
        }
    }
    next()
})

这里面有非常多的知识点

  1. 刷新404问题
  2. import.meta.glob