1.Vue Router setup 到底要怎么初始化配置?
用Vue 3开发项目时,很多同学一碰到Vue Router的setup用法就犯难——毕竟组合式API和之前的选项式写法思路差别挺大,这篇用问答形式,把Vue Router setup里的配置、导航、参数处理这些核心问题拆开来唠,帮你把路由逻辑顺明白,以后写项目少踩坑~
想在Vue 3项目里用Vue Router,第一步得把路由实例“搭”起来,首先得装依赖(如果是Vite+Vue 3的项目,执行`npm i vue-router@4`),然后分两步走:-
第一步:创建路由实例
新建个router/index.js
(或ts)文件,用createRouter
和createWebHistory
(或createWebHashHistory
,看路由模式)来配置。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', component: () => import('../views/AboutView.vue') // 路由懒加载写法 } ] }) export default router
这里
routes
数组里每个对象对应一条路由规则,component
既可以直接导入组件,也能用() => import(...)
做懒加载(后续会讲好处)。 -
第二步:挂载到Vue应用
打开main.js
,把路由实例挂到App上:import { createApp } from 'vue' import App from './App.vue' import router from './router' const app = createApp(App) app.use(router) // 让Vue应用“认识”路由系统 app.mount('#app')
完成这步,项目就有了路由的基础能力,接下来要解决的是组件里怎么用setup和路由互动。
组件里怎么用setup获取路由信息和导航?
Vue 3的组合式API里,操作路由主要靠两个“工具”:useRouter
和useRoute
,需先从vue-router
导入:
import { useRouter, useRoute } from 'vue-router'
-
useRouter:负责“导航跳转”
它返回路由实例,能调用push
、replace
、go
等方法,比如做个按钮,点击跳转到/about页面:<script setup> import { useRouter } from 'vue-router' const router = useRouter() const goAbout = () => { router.push('/about') // 直接写路径跳转 // 也能传对象:router.push({ name: 'about' }) // (路由配置了name时,用name更稳,避免路径拼写错误) } </script> <template> <button @click="goAbout">去关于页</button> </template>
-
useRoute:负责“读路由信息”
它返回当前路由的详细信息,比如path
(当前路径)、params
(动态参数)、query
(查询参数)、meta
(路由元信息)等,举个例子,页面里要显示当前路径:<script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log(route.path) // 打印如“/”或“/about” </script>
简单说,
useRouter
负责“跳”,useRoute
负责“读”,记住这俩工具,组件里和路由互动就有抓手了。
动态路由参数在setup里怎么处理?
比如做用户详情页,路由配置为/user/:id
,不同用户(id不同)需加载不同数据,要注意:同一路由,参数变化时,组件不会重新创建(比如从/user/1
跳到/user/2
,组件实例复用),所以得主动监听参数变化,否则数据不会更新。
-
步骤1:路由配置定义动态参数
在router/index.js
里添加:{ path: '/user/:id', name: 'user', component: () => import('../views/UserView.vue') }
-
步骤2:组件里监听参数变化
在UserView.vue
的setup中,用watch
或watchEffect
紧盯route.params.id
,示例:<script setup> import { useRoute, watch, watchEffect } from 'vue-router' import { ref } from 'vue' const route = useRoute() const userInfo = ref({}) // 方法1:watch监听params变化 watch( () => route.params.id, (newId) => { // 每次id变化,重新请求数据 fetchUser(newId).then(data => { userInfo.value = data }) }, { immediate: true } // 页面首次加载时也执行 ) // 方法2:watchEffect(自动追踪依赖) watchEffect(() => { const id = route.params.id fetchUser(id).then(data => { userInfo.value = data }) }) // 模拟请求函数 function fetchUser(id) { return new Promise(resolve => { setTimeout(() => { resolve({ id, name: `用户${id}` }) }, 500) }) } </script> <template> <div>用户ID:{{ route.params.id }}</div> <div>用户信息:{{ userInfo }}</div> </template>
这里
immediate: true
很关键,页面首次加载时也需执行请求逻辑;若没加,只有参数变化时才触发,刚进页面数据会是空的。
嵌套路由在setup模式下怎么配置和渲染?
做后台管理系统时,常需“嵌套路由”——比如外层是Layout(含侧边栏、顶栏),内层切换Dashboard、Settings等页面,配置和使用分两步:
-
步骤1:路由配置写children
在router/index.js
里,给父路由加children
数组:{ path: '/admin', name: 'admin', component: () => import('../views/AdminLayout.vue'), // 父组件(Layout) children: [ { path: '', // 空路径表示默认子路由 name: 'dashboard', component: () => import('../views/Dashboard.vue') }, { path: 'settings', name: 'settings', component: () => import('../views/Settings.vue') } ] }
-
步骤2:父组件用
渲染子路由
在AdminLayout.vue
的模板里,用放置子组件: <script setup> // 父组件逻辑,比如处理侧边栏导航 import { useRouter } from 'vue-router' const router = useRouter() </script> <template> <div class="admin-layout"> <aside>侧边栏</aside> <main> <!-- 子路由组件渲染到这里 --> <RouterView /> </main> </div> </template>
这样,访问
/admin
时默认显示Dashboard;访问/admin/settings
时显示Settings,父组件负责布局,子组件负责具体内容,分工清晰。
编程式导航在setup里有哪些需要注意的点?
用router.push
等方法时,看似简单,实则有不少细节易踩坑:
-
路径与命名路由的区别
用name
跳转更“稳”,因路径可能变化,但name一般固定,示例:// 方式1:用path(传query需自己拼接) router.push('/about?tab=1') // 方式2:用name + query(更灵活) router.push({ name: 'about', query: { tab: '1' } })
若路由配置了
params
(动态参数),只能用name跳转——因path里的参数是写死的,而name可动态传params:// 错误:path写params会被当作字符串,无法解析 router.push('/user/123') // 硬编码id,不灵活 // 正确:用name + params router.push({ name: 'user', params: { id: 123 } })
-
组件内守卫的替代方案
Vue Router 3中,组件里可写beforeRouteEnter
等守卫,但Vue Router 4的组合式API不支持组件内选项式守卫,若想在导航进入前做判断(如权限验证),需换思路:- 用全局导航守卫:在
router/index.js
里写router.beforeEach
,判断目标路由的meta
是否需权限; - 用组合式API的watch:在组件setup里,watch路由变化提前拦截(但不如全局守卫优雅)。
举个全局守卫的例子:// router/index.js router.beforeEach((to, from) => { if (to.meta.requiresAuth && !isLogin()) { return { name: 'login' } // 未登录则跳登录页 } })
- 用全局导航守卫:在
路由懒加载在setup项目里怎么配置更高效?
路由懒加载的核心是让页面按需加载,减少初始包体积,提升首屏速度,配置简单,但需结合项目需求优化:
-
基础配置:用import()语法
在路由配置里,把component
写成函数形式:{ path: '/about', name: 'about', component: () => import('../views/AboutView.vue') }
这样webpack(或Vite)会把该组件单独打包,只有访问到/about时才加载对应js文件。
-
进阶优化:配合Suspense处理加载状态
若组件加载时想显示“加载中”提示,Vue的组件能派上用场,比如在App.vue里: <template> <Suspense> <template #default> <RouterView /> </template> <template #fallback> <div>加载中...</div> </template> </Suspense> </template>
需注意,路由懒加载的组件若用了异步组件(如
const Comp = defineAsyncComponent(() => import(...))
),Suspense才能捕获加载状态。 -
分组懒加载(可选)
若多个路由组件属于同一类(如都在admin目录下),可用“魔法注释”合并打包:component: () => import(/* webpackChunkName: "admin" */ '../views/AdminView.vue')
这样所有加了
webpackChunkName: "admin"
的组件,会被打包到同一chunk里,减少请求次数。
路由元信息(meta)在setup里怎么用?
路由元信息(meta)是“附加信息容器”——可给路由加自定义信息(如是否需登录、页面标题、权限等级)。
-
步骤1:路由配置加meta
在router/index.js
的路由规则里写:{ path: '/profile', name: 'profile', component: () => import('../views/Profile.vue'), meta: { requiresAuth: true, // 需登录 title: '个人中心' // 页面标题 } }
-
步骤2:在组件或守卫里读取meta
- 在组件里读:用
useRoute().meta
比如在Profile.vue里设置页面标题:<script setup> import { useRoute, onMounted } from 'vue-router' const route = useRoute() onMounted(() => { document.title = route.meta.title || '默认标题' }) </script>
- 在导航守卫里读:判断权限
前文提过的全局守卫,结合meta做权限控制:router.beforeEach((to, from) => { if (to.meta.requiresAuth && !isAuthenticated()) { return { name: 'login' } } })
这样meta就成了路由的“附加属性”,想加什么自定义规则都能往里塞。
- 在组件里读:用
多路由出口(Named Views)在setup中怎么实现?
有时一个页面需同时渲染多个组件(如左侧侧边栏+右侧内容区),这时得用命名视图。
-
步骤1:路由配置写components(注意复数)
在router/index.js
里,给路由规则的components
(复数)配多个命名组件:{ path: '/dashboard', name: 'dashboard', components: { default: () => import('../views/DashboardMain.vue'), // 默认视图 sidebar: () => import('../views/DashboardSidebar.vue') // 命名为sidebar的视图 } }
-
步骤2:父组件用
渲染
在Dashboard的父组件(如App.vue或Layout组件)里,用带name的<template> <div class="dashboard-layout"> <!-- 渲染name为sidebar的组件 --> <RouterView name="sidebar" /> <!-- 渲染默认组件(name不写即为default) --> <RouterView /> </div> </template>
这样访问/dashboard时,两个组件会同时渲染到对应位置,适合复杂布局场景。
把这些问题吃透,Vue Router setup的核心用法基本就拿捏住了~其实思路很简单:先搭好路由实例,组件里用useRouter和useRoute和路由互动,再结合动态参数、嵌套路由、懒加载这些特性,就能应对大部分项目场景,要是还有细节卡壳,不妨回到官方文档(或自己写个小Demo试一把),实践几次就顺溜啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。