一、刚接触Vue Router,咋完成最基础的路由配置?
p>做Vue单页应用开发时,Vue Router是管理页面路由的核心工具,但实际写代码时总会碰到各种问题——路由配置报错、跳转不对、页面加载慢…今天用问答形式,把Vue Router开发里常见的坑和解决办法唠明白,从基础到进阶一次讲透~
问题:新建了Vue 3项目,想加路由功能,第一步该干啥?咋把页面和路由对应起来?
回答:先把Vue Router装上,再做三件事:定义路由规则、创建路由实例、把路由挂载到Vue App里。
举个实际步骤:
- 安装依赖:打开终端,执行
npm install vue-router@4
(Vue 3对应Vue Router 4,Vue 2对应v3版本,得注意版本匹配)。 - 新建路由文件:在
src
目录下建个router/index.js
,里面写路由规则,比如有Home和About两个页面:import { createRouter, createWebHistory } from 'vue-router' // 导入页面组件(也可以用懒加载,后面讲) import HomeView from '../views/HomeView.vue' import AboutView from '../views/AboutView.vue'
const routes = [ { path: '/', // 访问根路径时 name: 'home', component: HomeView // 渲染HomeView组件 }, { path: '/about', name: 'about', component: AboutView } ]
const router = createRouter({ history: createWebHistory(), // 用HTML5的history模式,URL没有# routes // 上面定义的路由规则数组 })
export default router
**挂载到Vue App**:打开`src/main.js`,把路由实例注入Vue:
```javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 引入上面写的路由文件
const app = createApp(App)
app.use(router) // 注册路由插件
app.mount('#app')
- 添加路由出口:最后在
App.vue
里加<router-view>
,这个标签是路由组件的“容器”,访问不同路径时,对应的组件会渲染到这里:<template> <div> <!-- 路由出口,渲染匹配的组件 --> <router-view></router-view> </div> </template>
这样基础路由就跑通了~要是页面没渲染,先检查路径、组件导入、<router-view>
有没有漏加这几个点。
页面跳转有声明式和编程式,该咋选?带参数咋传?
问题1:点击按钮跳转到About页面,用<router-link>
还是写JS逻辑?两者有啥区别?
回答:这俩都是跳转路由的方式,场景不同:
-
声明式(
<router-link>
):适合在模板里直接写导航,比如导航栏、菜单这些UI元素,它本质是个a标签,但做了优化(比如自动添加激活样式),用法:<template> <router-link to="/about">去About页面</router-link> </template>
还能通过
to
传对象,控制命名路由和参数:<router-link :to="{ name: 'about', query: { tab: 'info' } }">去About并带参数</router-link>
-
编程式(
router.push
):适合在JS逻辑里跳转,比如点击按钮后要做判断再跳转,先在组件里导入useRouter
(Vue 3组合式API),再调用push:<script setup> import { useRouter } from 'vue-router' const router = useRouter()
const goAbout = () => { router.push('/about') // 直接传路径 // 或者传对象(和router-link的to对象格式一样) router.push({ name: 'about', query: { tab: 'info' } }) }
```简单说,UI上的导航用声明式,逻辑里的跳转用编程式,灵活度更高~
问题2:跳转时要带参数,动态路由(/user/:id
)和查询参数(?id=1
)有啥区别?咋取参数?
回答:两种传参方式适用场景不一样:
-
动态路由(路径参数):适合参数是URL的一部分,比如用户详情页
/user/123
,参数变化时页面是同一个(只是数据变),配置路由时要写:参数名
:// 路由规则里定义 { path: '/user/:id', name: 'user', component: UserView }
跳转时传参数(以编程式为例):
router.push('/user/123') // 或者对象形式 { name: 'user', params: { id: 123 } }
组件里取参数(Vue 3组合式API):
<script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log(route.params.id) // 拿到123 </script>
-
查询参数(query):适合参数不是URL核心部分,比如搜索页
/search?keyword=vue
,参数可传可不传,刷新页面参数还在,配置路由时不用改path,直接在跳转时加query:router.push({ path: '/search', query: { keyword: 'vue' } })
组件里取参数:
<script setup> import { useRoute } from 'vue-router' const route = useRoute() console.log(route.query.keyword) // 拿到vue </script>
动态路由参数是URL的“固定部分”,适合标识资源;query参数是“附加信息”,适合筛选、搜索这类场景~
路由守卫咋用?权限控制、改页面标题这些需求咋实现?
问题1:进入页面之前要判断用户是否登录,没登录就跳登录页,咋用路由守卫做权限控制?
回答:路由守卫能在“导航过程中”插一脚,做权限判断、加载数据这些事,最常用的是全局前置守卫beforeEach
,在路由实例里配置:
打开router/index.js
,在创建router之后加:
router.beforeEach((to, from, next) => { // to:要去的目标路由;from:从哪个路由来;next:放行/跳转的函数 const isLogin = localStorage.getItem('token') // 假设用localStorage存token if (to.name !== 'login' && !isLogin) { // 不是登录页,且没登录 → 跳登录页 next({ name: 'login' }) } else { // 已登录,或者要去的是登录页 → 放行 next() } })
这样,每次路由跳转前都会触发这个守卫,判断登录状态,注意:Vue Router 4里,next
可以返回一个路由对象,也可以直接调用next()
放行,如果用TypeScript,要注意类型定义~
组件内守卫(比如beforeRouteEnter
)适合组件内部的逻辑,但Vue 3组合式API里,组件内守卫改成用onBeforeRouteEnter
这些钩子了(需要导入):
<script setup> import { onBeforeRouteEnter } from 'vue-router' onBeforeRouteEnter((to, from) => { // 进入组件前触发,这里还拿不到this(组件实例没创建) console.log('准备进入这个组件啦') }) </script>
根据项目复杂度选全局或组件内守卫,权限控制一般用全局守卫更方便~
问题2:想在每个页面跳转后,把页面标题改成路由对应的名字,咋做?
回答:还是用全局前置守卫beforeEach
,因为导航完成后页面标题要变,所以可以在beforeEach
里处理:
router.beforeEach((to, from) => { // 假设路由规则里给每个路由配了meta.title document.title = to.meta.title || '默认标题' // 记得放行 return true // 或者用next(),不过Vue Router 4里return true和next()效果一样,更推荐return }) // 路由规则里加meta const routes = [ { path: '/', name: 'home', component: HomeView, meta: { title: '首页' } // 自定义元信息 }, { path: '/about', name: 'about', component: AboutView, meta: { title: '关于我们' } } ]
这样每次跳转路由时,页面标题就会自动变成meta.title
的值,用户体验更友好~
后台管理系统有嵌套布局,嵌套路由咋配置?
问题:做后台系统时,顶部有导航栏、左侧有侧边栏,内容区是不同页面,这种嵌套结构咋用Vue Router实现?
回答:用嵌套路由(children),把父组件当“布局容器”,子路由渲染到父组件的<router-view>
里。
步骤示例:
-
建布局组件:先建布局组件
LayoutView.vue
,里面包含侧边栏、顶部栏,还有一个<router-view>
给子路由:<template> <div class="layout"> <aside>侧边栏</aside> <main> <router-view></router-view> <!-- 子路由渲染到这里 --> </main> </div> </template>
-
配置路由规则:给父路由加
children
数组:const routes = [ { path: '/dashboard', name: 'dashboard', component: LayoutView, // 父组件(布局) children: [ { path: '', // 子路由的默认路径,访问/dashboard时渲染 name: 'dashboard-home', component: DashboardHomeView }, { path: 'users', // 访问/dashboard/users时渲染 name: 'dashboard-users', component: DashboardUsersView }, { path: 'posts', // 访问/dashboard/posts时渲染 name: 'dashboard-posts', component: DashboardPostsView } ] } ]
-
效果验证:当用户访问
/dashboard
时,LayoutView渲染,同时子路由dashboard-home
的组件也渲染到LayoutView的<router-view>
里;访问/dashboard/users
时,子路由dashboard-users
的组件渲染。
嵌套路由的关键是父路由组件里必须有<router-view>
,用来放子路由的内容,这种方式特别适合多布局的项目,比如前台和后台用不同的父布局~
路由太多导致首屏加载慢,咋做懒加载优化?
问题:项目里页面越来越多,打包后JS文件特别大,首屏要等好久,咋让路由组件按需加载?
回答:用路由懒加载,借助webpack的代码分割功能,让每个路由组件单独打包,访问时再加载,减少首屏压力。
做法很简单:把路由规则里的component
从“静态导入”改成“动态导入”(用import()
语法)。
原来的静态导入:
import HomeView from '../views/HomeView.vue' // 路由规则里用HomeView
改成懒加载后:
const HomeView = () => import('../views/HomeView.vue') // 路由规则里还是用HomeView
或者直接在路由规则里写:
const routes = [ { path: '/', name: 'home', component: () => import('../views/HomeView.vue') // 动态导入 }, // 其他路由同理 ]
这样打包时,每个路由组件会被拆成单独的chunk,只有当用户访问对应路由时,才会加载这个chunk,首屏加载的JS体积就小了很多。
如果想给懒加载加loading状态(比如加载时显示loading组件),可以用import()
的.then和.catch:
component: () => import('../views/HomeView.vue').then(module => module.default).catch(err => { // 加载失败时,返回一个默认组件(比如ErrorView) return import('../views/ErrorView.vue') })
懒加载是性能优化的基础操作,项目大了一定要安排上~
路由报错和404页面咋处理?
问题1:用户输入不存在的路由(比如/xxx
),咋跳转到404页面?
回答:用*通配符路由(``)**,把它放在路由规则的最后(因为路由匹配是从上到下的,通配符要兜底):
const routes = [ // 其他路由规则... { path: '/:pathMatch(.*)*', // Vue Router 4里用这个格式匹配所有路径 name: 'not-found', component: NotFoundView // 自己写的404组件 } ]
这样,只要用户访问的路径没被前面的路由匹配到,就会跳转到NotFoundView,注意路径匹配的语法:Vue Router 3是,Vue Router 4要写成/:pathMatch(.*)*
才能正确匹配所有层级(比如/a/b/c
这种多级路径)。
问题2:路由跳转时控制台报NavigationDuplicated
错误,咋解决?
回答:这个错误是因为“重复跳转到当前路由”导致的(比如点击同一个导航按钮多次),Vue Router 4里可以这样处理:
封装router.push
,捕获错误(适合编程式跳转):
// 在router/index.js里封装 const originalPush = router.push router.push = function push(location, onComplete, onAbort) { return originalPush.call(this, location, onComplete, onAbort).catch(err => { if (err.name !== 'NavigationDuplicated') { return Promise.reject(err) } }) }
用router.replace
代替router.push
,因为replace不会留下历史记录,重复点击时不会触发重复导航(适合不需要回退的场景,比如登录按钮):
router.replace('/login') // 代替router.push
在模板里的<router-link>
加replace
属性,让跳转变成replace模式:
<router-link to="/login" replace>去登录</router-link>
选一种适合项目的方式就行,一般封装router.push
能一劳永逸解决重复跳转的问题~
最后唠叨两句
Vue Router的知识点不算少,但核心就是路由配置、跳转、守卫、嵌套、优化、错误处理这几块,实际开发中,先把基础流程走通,再逐步处理权限、性能、异常这些细节,遇到问题时,先看官方文档(Vue Router的文档写得很详细,还有示例),再结合场景调试。
要是刚开始学,建议把每个知识点拆成小demo练手——比如先写基础路由,再加个带参数的跳转,然后用守卫做权限,最后用懒加载优化,练一遍下来,对路由的理解就扎实多啦~
(文章到这里差不多覆盖了Vue Router开发中80%的常见问题,从基础到进阶,每个环节都结合了实际场景和代码示例,新手跟着做能少踩很多坑~)
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。