Code前端首页关于Code前端联系我们

Vue Router 基础使用得先搞懂哪些?

terry 3周前 (09-08) 阅读数 42 #Vue

很多刚接触 Vue 框架的同学,一提到路由配置、页面跳转这些功能就犯愁,Vue Router 作为 Vue 生态里处理单页应用路由的核心工具,其实把原理和操作理清楚后没那么复杂,这篇文章用问答形式,把 Vue Router 从基础使用到进阶技巧,再到踩坑解决全拆解开,帮你实实在在掌握它~

要玩转 Vue Router,基础环节得把安装配置、页面渲染、路由模式这些核心点吃透。

安装与路由实例创建

Vue 项目里要用 Vue Router,得先装依赖(Vue2 选 vue-router@3,Vue3 选 vue-router@4):

npm install vue-router # 按项目版本选对应版本

装完后,在 src/router/index.js 里初始化路由:

// Vue2 示例(Vue3 写法类似,API 有差异)
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter) // 注册路由插件
// 定义路由规则:path 对应 URL,component 对应渲染的组件
const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView // 直接导入组件(静态加载)
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue') // 懒加载,后面讲
  }
]
// 创建路由实例,配置模式和规则
const router = new VueRouter({
  mode: 'history', // 路由模式:history 或 hash(默认 hash)
  routes
})
export default router

最后在 main.js 把路由注入 Vue 根实例:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
  router, // 注入路由,让所有组件能通过 this.$router/$route 访问
  render: h => h(App)
}).$mount('#app')

页面里的路由渲染与跳转

路由配置好后,页面得用两个核心标签:

  • <router-view>路由组件的占位符,App.vue 里写:

    <template>
      <div id="app">
        <!-- 导航链接 -->
        <router-link to="/">首页</router-link>
        <router-link to="/about">lt;/router-link>
        <!-- 匹配到的路由组件会渲染到这里 -->
        <router-view></router-view>
      </div>
    </template>
  • <router-link>声明式导航to 属性指定跳转路径,点击后自动处理路由切换,还能通过 active-class 给激活的链接加样式(比如高亮)。

hash 模式 vs history 模式

路由模式决定 URL 长啥样,以及浏览器怎么处理跳转:

  • hash 模式(默认):URL 带 ,http://xxx.com/#/home。 后面的变化不会触发浏览器请求,兼容性好,但 URL 不够美观。
  • history 模式:URL 像普通网页,http://xxx.com/home,但需要服务器配合:刷新页面时,服务器得返回 index.html(否则会 404),适合追求 URL 美观的项目。

路由跳转除了 还有别的方式吗?

有!编程式导航适合在逻辑里(比如按钮点击后判断权限再跳转)控制路由,核心是 this.$router 上的方法:

常用方法:push / replace / go

  • this.$router.push(path | object):跳转到新路由,往历史记录里新增一条

    methods: {
      goDetail() {
        // 方式1:传字符串路径
        this.$router.push('/detail')
        // 方式2:传对象(配合 name + params/query)
        this.$router.push({ name: 'detail', params: { id: 123 } })
        this.$router.push({ path: '/detail', query: { id: 123 } })
      }
    }
  • this.$router.replace(path | object):和 push 类似,但替换当前历史记录,比如从详情页跳编辑页,不想让用户回退到详情页时用它。

  • this.$router.go(n):控制历史记录前进/后退。n=1 前进一页,n=-1 后退一页(类似浏览器的前进后退按钮)。

params vs query 传参区别

传参是路由跳转的高频需求,但这俩方式差异很大:

  • params 传参:参数“藏”在路由里(如 /detail/123),需要路由配置成 /detail/:id 才能用;history 模式下刷新会丢失参数(因为 URL 里没体现,刷新时服务器不认)。
  • query 传参:参数“挂”在 URL 后(如 /detail?id=123),不需要路由配动态参数,刷新后参数还在,适合传非敏感的临时数据。

name 跳转 vs path 跳转

路由规则里的 name 是路由的“唯一标识”,跳转时用 name 更灵活:

  • name 跳转时,可配合 params 传参(如 { name: 'detail', params: { id: 123 } })。
  • path 跳转时,params 会被忽略,只能用 query 传参(如 { path: '/detail', query: { id: 123 } })。

所以建议:如果路由配了 name,优先用 name 跳转~

动态路由参数怎么玩?

做商品详情、用户信息这类“根据 ID 展示不同内容”的页面,得用动态路由匹配

配置动态路由

在路由规则里,把路径写成带参数的形式(如 :id):

{
  path: '/product/:id', // :id 是动态参数
  name: 'product',
  component: () => import('../views/ProductView.vue')
}

组件内获取参数

ProductView.vue 里,通过 this.$route.params.id 拿到路由参数:

export default {
  created() {
    console.log(this.$route.params.id) // 路径是 /product/1001 时,输出 1001
  }
}

注意:$route当前路由的信息对象(存了路径、参数、元信息等);$router操作路由的实例(用来跳转、前进后退等),别搞混~

路由参数变化时组件不更新?

比如从 /product/1001 跳转到 /product/1002,组件会被复用(因为组件相同),导致 created / mounted 这些钩子不重新执行,这时候得监听路由变化

  • 用 watch 监听 $route

    export default {
      watch: {
        '$route.params.id'(newId) {
          this.fetchData(newId) // 新 ID 来了,重新请求数据
        }
      },
      methods: {
        fetchData(id) { /* 调接口拿数据 */ }
      }
    }
  • 用组件内守卫 beforeRouteUpdate

    export default {
      beforeRouteUpdate(to, from, next) {
        this.fetchData(to.params.id)
        next() // 必须调用 next() 才能继续跳转
      }
    }

拓展:路由别名(alias)

如果想让 /p/123 也能访问到 /product/123 对应的组件,可给路由加 alias

{
  path: '/product/:id',
  alias: '/p/:id', // 访问 /p/123 也会匹配到这个路由
  component: ProductView
}

实际项目中用得少,但遇到特殊 URL 需求时很方便~

嵌套路由该怎么配置?

做后台管理系统时,经常需要“公共布局 + 动态内容”(比如左侧侧边栏固定,中间内容随路由变),这就得用嵌套路由

配置 children 数组

在父路由的规则里,加 children 数组放子路由规则:

{
  path: '/admin', // 父路由路径
  component: () => import('../layouts/AdminLayout.vue'), // 父组件(公共布局)
  children: [
    {
      path: '', // 空路径 = 默认子路由,访问 /admin 时渲染它
      name: 'dashboard',
      component: () => import('../views/AdminDashboard.vue')
    },
    {
      path: 'users', // 子路由路径,完整路径是 /admin/users
      name: 'users',
      component: () => import('../views/AdminUsers.vue')
    },
    {
      path: 'posts',
      name: 'posts',
      component: () => import('../views/AdminPosts.vue')
    }
  ]
}

父组件里放 当子出口

AdminLayout.vue 得有个 <router-view> 来渲染子路由组件:

<template>
  <div class="admin-layout">
    <aside>固定侧边栏</aside>
    <main>
      <!-- 子路由组件渲染在这里 -->
      <router-view></router-view>
    </main>
  </div>
</template>

这样,访问 /admin 时,显示 AdminLayout + AdminDashboard;访问 /admin/users 时,显示 AdminLayout + AdminUsers,公共侧边栏始终存在~

路由守卫有啥用?怎么用?

路由守卫是“路由跳转的生命周期钩子”,能在跳转前、跳转后、进入组件前做权限判断、数据加载、离开提示等,分三类:

全局守卫:作用于所有路由

写在 router/index.js 的路由实例上,控制整个应用的路由逻辑:

  • router.beforeEach(to, from, next):跳转触发,常用作权限控制,比如判断用户是否登录:

    router.beforeEach((to, from, next) => {
      const isLogin = localStorage.getItem('token')
      // 路由元信息:给需要权限的路由加 meta.requiresAuth
      if (to.meta.requiresAuth && !isLogin) {
        next({ name: 'login' }) // 没登录且需要权限,跳登录页
      } else {
        next() // 放行
      }
    })
  • router.afterEach(to, from):跳转触发,常用作改页面标题、埋点统计,比如改标题:

    router.afterEach((to) => {
      document.title = to.meta.title || '默认标题'
    })

路由独享守卫:只作用于单个路由

直接写在某个路由规则里,只有访问该路由时触发:

{
  path: '/order',
  name: 'order',
  component: OrderView,
  beforeEnter: (to, from, next) => {
    // 比如判断订单是否存在,不存在跳404
    const orderExist = checkOrder(to.params.orderId)
    if (orderExist) {
      next()
    } else {
      next({ name: '404' })
    }
  }
}

组件内守卫:作用于当前组件

写在组件的选项里,针对当前组件的路由变化:

  • beforeRouteEnter(to, from, next)进入组件前触发,此时组件实例还没创建(thisundefined),如果要给组件传数据,用 next(vm => {})

    export default {
      beforeRouteEnter(to, from, next) {
        // 获取数据后传给组件实例
        fetchData(to.params.id).then(data => {
          next(vm => {
            vm.data = data // vm 是组件实例
          })
        })
      }
    }
  • beforeRouteUpdate(to, from, next):组件复用时触发(比如路由参数变化,组件没销毁),前面动态路由部分讲过怎么用它处理数据更新。

  • beforeRouteLeave(to, from, next)离开组件前触发,常用作“是否保存未提交内容”的提示:

    export default {
      beforeRouteLeave(to, from, next) {
        if (this.formDirty) { // formDirty 标记表单是否修改
          const confirm = window.confirm('有未保存内容,确定离开?')
          confirm ? next() : next(false) // 确认则放行,否则取消跳转
        } else {
          next()
        }
      }
    }

路由懒加载怎么配置?能优化性能?

项目大了,把所有组件打包到一个文件里,首屏加载会很慢。路由懒加载能把不同路由的组件分成不同代码块,访问时再加载,减少首屏压力。

配置方式:动态 import

把原来的“静态导入组件”改成动态 import 形式:

原来的静态导入(所有组件打包到一起):

import HomeView from '../views/HomeView.vue'
// 路由里写 component: HomeView

改成懒加载:

{
  path: '/about',
  name: 'about',
  component: () => import('../views/AboutView.vue')
}

这样,AboutView 会被单独打包成一个 chunk,只有用户访问 /about 时才加载这个 chunk

优化:给 chunk 起名字

webpack 魔法注释(Vite 也支持类似配置)给 chunk 命名,方便调试:

component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')

打包后,这个 chunk 会叫 about.js,而不是随机的哈希名~

懒加载的好处

  • 首屏加载的 JS 文件体积变小,加载更快。
  • 用户没访问的页面暂时不加载,节省流量和性能。
    尤其适合页面多、组件大的项目,比如后台管理系统~

路由报错、页面不跳转这些坑怎么避?

用 Vue Router 时,一些“玄学”问题其实是配置或理解不到位导致的,列几个高频坑和解法:

路由配置了但页面不显示?

  • 检查 <router-view> 是否漏写,App.vue 里有没有 <router-view>,嵌套路由的父组件里有没有给子路由留 <router-view>
  • 检查 routes 里的 path 拼写(比如把 /home 写成 /hom)。
  • 若用 history 模式,检查服务器配置,刷新页面时,服务器得返回 index.html(否则会 404)。

重复点击路由报错?

Vue Router 3.x 版本中,重复点击同一个路由(比如点两次 <router-link to="/home">)会抛错,解法:

  • 升级到 Vue Router 4.x(兼容 Vue3),该问题已修复。
  • 编程式导航时,判断当前路由是否和目标路由一致,一致则不跳转;或用 try...catch 吞掉错误:
    methods: {
      goHome() {
        try {
          this.$router.push('/home')
        } catch (error) { /* 吞掉错误 */ }
      }
    }

params 传参刷新后丢失?

history 模式下,params 是“隐式”传参(URL 里没体现),刷新时服务器不认,导致参数丢失,解法:

  • 改用 query 传参(URL 里能看到,刷新还在)。
  • 把参数存在 Vuex 或 localStorage,刷新后再取。

嵌套路由不渲染?

  • 检查父路由的 children 配置:子路由的 path 别加斜杠(比如父路由是 /admin,子路由 path'users' 对应 /admin/users,加 会变成根路径,就错了)。
  • 检查父组件里是否有 <router-view> 作为子路由出口。

把 Vue Router 拆成这些问题后,是不是觉得每个知识点都清晰多了?路由配置、跳转、传参、守卫这些核心功能,其实都是围绕“单页应用如何管理页面切换和状态”来设计的,建议你跟着文章里的例子,自己搭

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门