vue-router npm 是什么?怎么在项目里用?常见问题咋解决?
想搞懂vue-router npm 咋回事、咋在项目里用,还有碰到问题咋解决?这篇从基础到实操,把常见疑问拆明白,不管是刚学Vue的新手,还是想优化路由配置的老手,都能找到有用的点~
vue-router npm 是干啥的?
首先得拆成两部分理解:vue-router 是Vue.js官方的路由管理工具,专门解决单页应用(SPA)里“页面”切换、URL管理这些事儿;npm 是Node.js的包管理工具,我们通过npm把vue-router装到项目里,就像在手机应用商店下App一样。
单页应用最大特点是整站只有一个HTML文件,用户切换“页面”时不刷新浏览器,靠JS动态渲染组件,vue-router就是干这个的:它能让不同路径(/home
、/about
)对应不同组件,还支持嵌套路由(像后台管理系统的侧边栏+内容区结构)、动态路由(/user/123
里的用户ID)、路由守卫(控制页面权限、跳转逻辑)这些核心功能,要是没有它,你得自己写一堆逻辑判断URL变化、渲染不同组件,麻烦到哭~
怎么用npm装vue-router到项目里?
步骤不难,但得注意Vue版本!Vue2和Vue3对应的vue-router版本不一样,装错了容易报错:
- Vue2项目:装
vue-router@3
,命令是npm install vue-router@3
; - Vue3项目:装
vue-router@4
,命令是npm install vue-router
(因为@4是默认最新版)。
装好后得配置路由,以Vue3 + Vite项目为例,流程大概这样:
新建路由配置文件
在 src
文件夹下建 router
文件夹,里面写 index.js
;
引入并创建路由实例
// src/router/index.js import { createRouter, createWebHistory } from 'vue-router' // 引入你写的页面组件,比如Home和About import Home from '../views/Home.vue' import About from '../views/About.vue' const routes = [ { path: '/', // 根路径 name: 'Home', component: Home }, { path: '/about', name: 'About', component: About } ] const router = createRouter({ history: createWebHistory(), // 路由模式用history(URL里没#),想兼容旧浏览器用createWebHashHistory() routes // 上面定义的路由规则数组 }) export default router
把路由挂载到Vue实例
打开 src/main.js
,引入路由并use:
// src/main.js 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>
显示匹配的组件,用 <router-link to="/about">关于页</router-link>
做跳转链接,路由就跑起来啦~
vue-router配置路由有哪些关键步骤?
核心就四点:路由映射、模式选择、特殊路由(嵌套/动态)、导航方式,一个个说:
路由映射:path和component绑定
每个路由规则是个对象,path
是URL路径,component
是对应要渲染的Vue组件,比如用户访问 /product
,就显示 Product.vue
组件,还能给路由加 name
(命名路由),跳转时用name更稳,不怕path改了要全局替换。
路由模式:history vs hash
两种模式决定URL长啥样:
- history模式:URL像
https://xxx.com/about
,好看但需要后端配置(否则刷新404); - hash模式:URL带 ,像
https://xxx.com/#/about
,兼容性好(老浏览器也支持),不用后端配。
Vue3里用 createWebHistory()
或 createWebHashHistory()
选模式,Vue2里是 new VueRouter({ mode: 'history' })
。
特殊路由:嵌套和动态
嵌套路由
适合有公共布局的场景(比如后台管理系统,侧边栏是固定的,内容区换组件),配置时用 children
数组,父路由组件里得有 <router-view>
渲染子路由:
const routes = [ { path: '/admin', component: AdminLayout, // 父组件,里面有侧边栏+<router-view> children: [ { path: 'dashboard', component: Dashboard }, // 子路由,访问/admin/dashboard { path: 'settings', component: Settings } ] } ]
动态路由
处理带参数的路径,/user/123
里的用户ID,配置时用 /user/:id
,组件里通过 $route.params.id
(Vue2)或 useRoute().params.id
(Vue3)拿参数。
导航方式:声明式和编程式
- 声明式:用
<router-link to="/path">
写在模板里,自动生成a标签,还能控制激活样式; - 编程式:用
router.push('/path')
写在JS里(比如点击按钮后跳转),灵活度高,还能传参(后面讲传参时细说)。
单页应用里,vue-router咋处理页面跳转和传参?
跳转分声明式和编程式,传参分query和params,组合起来有四种玩法,先看跳转:
声明式跳转(<router-link>
)
基础用法:<router-link to="/about">去关于页</router-link>
,点击就跳。
传参的话,分query和params:
- query传参:URL带参数,像
/about?name=张三
,配置:<router-link :to="{ path: '/about', query: { name: '张三' }}"></router-link>
; - params传参:参数藏在路由path里(需要路由配置时写
/user/:id
),配置:<router-link :to="{ name: 'User', params: { id: 123 }}"></router-link>
(注意要用name,不能用path,否则params传不过去)。
编程式跳转(router.push
)
在JS里写,比如按钮点击事件:
// Vue2:this.$router.push methods: { goAbout() { this.$router.push('/about') // 传query: this.$router.push({ path: '/about', query: { name: '张三' } }) // 传params(要配合命名路由): this.$router.push({ name: 'User', params: { id: 123 } }) } } // Vue3:用useRouter import { useRouter } from 'vue-router' const router = useRouter() const goAbout = () => { router.push('/about') // 传参和Vue2逻辑一样 }
传参后咋接收?
目标组件里,用 $route.query
或 $route.params
拿参数(Vue3用 useRoute().query
/ useRoute().params
):
// Vue2组件里 export default { mounted() { console.log(this.$route.query.name) // 拿query参数 console.log(this.$route.params.id) // 拿params参数(路由得配了/:id) } } // Vue3组件(组合式API) import { useRoute } from 'vue-router' const route = useRoute() console.log(route.query.name) console.log(route.params.id)
注意:params传参如果没在路由path里定义(比如路由path是/user
,没写/user/:id
),刷新页面参数会丢!因为params不显示在URL里;而query参数在URL里,刷新也能保留,所以如果需要刷新不丢参,优先用query,或者把params对应的参数写到路由path里。
vue-router碰到404页面咋配置?
核心思路是“通配符路由”,让所有没匹配到的路径都跳404组件,但得注意Vue2和Vue3的写法区别:
Vue3里的404配置
路由规则最后加个通配符路由:
const routes = [ // 其他路由... { path: '/:pathMatch(.*)*', // 匹配所有没定义的路径,括号里的(.*)是正则,*表示可选 name: 'NotFound', component: () => import('../views/NotFound.vue') // 也可以用懒加载 } ]
NotFound.vue
里就写“页面走丢啦~返回首页?”之类的提示,再加个返回按钮用 router.push('/')
跳回去。
Vue2里的404配置
写法更简单,用 path: '*'
:
const routes = [ // 其他路由... { path: '*', component: NotFound } ]
不管Vue2还是Vue3,404路由必须放最后!因为路由是“从上到下”匹配的,前面的精确路由要优先匹配,最后再匹配通配符,不然把正常路由也截胡成404了。
vue-router嵌套路由咋玩?实际项目里常用吗?
嵌套路由在中大型项目里超级常用!比如电商网站的商品列表页点进商品详情,或者后台管理系统的布局(顶部导航+侧边栏+内容区),它的核心是“路由里套路由”,父路由组件里有 <router-view>
来渲染子路由组件。
举个后台管理系统的例子:
// 路由配置 const routes = [ { path: '/admin', component: AdminLayout, // 父组件,包含侧边栏和<router-view> children: [ { path: 'dashboard', component: Dashboard }, // 子路由1:仪表盘 { path: 'orders', component: OrderList }, // 子路由2:订单列表 { path: 'products', component: ProductManage } // 子路由3:商品管理 ] } ] // AdminLayout.vue(父组件) <template> <div class="admin-layout"> <aside>侧边栏:<router-link to="dashboard">仪表盘</router-link> | <router-link to="orders">订单</router-link></aside> <main><router-view></router-view></main> <!-- 子路由组件渲染在这 --> </div> </template>
用户访问 /admin/dashboard
时,AdminLayout
会渲染,Dashboard
组件渲染在 <router-view>
里,这样做的好处是布局复用(侧边栏不用每个页面都写一遍)、代码拆分清晰(不同功能模块的路由集中管理)。
vue-router动态路由和路由传参有啥坑?咋避?
踩过这些坑的人不少,总结三个高频坑和解决方法:
坑1:params传参刷新后丢失
场景:用 router.push({ name: 'User', params: { id: 123 } })
跳转,URL是 /user
(没把id写进path),刷新页面后 $route.params.id
没了。
原因:params参数没出现在URL里,属于“内存式”传参,刷新就没了。
解决:要么把参数放到query里(query: { id: 123 }
),要么在路由path里定义 /user/:id
,让参数显示在URL中(如 /user/123
),这样刷新也能保留。
坑2:动态路由组件复用,生命周期不触发
场景:从 /user/1
跳到 /user/2
,两个页面用同一个组件(UserDetail.vue
),这时候组件实例会复用,mounted
钩子不会重新执行,导致数据没更新。
解决:
-
用
watch
监听路由变化:// Vue2 export default { watch: { '$route' (to, from) { // to是目标路由,from是来源路由 this.fetchData(to.params.id) // 重新拉取数据 } } } // Vue3(组合式API) import { watch } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() watch(route, (to) => { fetchData(to.params.id) }, { immediate: true })
-
给
<router-view>
加key,强制组件重新渲染:<router-view :key="$route.fullPath" />
(fullPath包含路径和参数,参数变了key就变,组件重新加载)。
坑3:query传参是对象/数组,URL解析异常
场景:想传个对象 { name: '张三', age: 18 }
到query里,结果URL变成 /path?query=%5Bobject%20Object%5D
,取的时候解析出错。
原因:query参数会被转成字符串,对象/数组直接转会乱码。
解决:用 JSON.stringify
转成字符串传,取的时候 JSON.parse
解析:
// 传参时 router.push({ path: '/path', query: { info: JSON.stringify({ name: '张三', age: 18 }) } }) // 取参时 const info = JSON.parse(route.query.info)
vue-router路由守卫咋用?能解决啥问题?
路由守卫就是“在路由跳转的各个阶段插一脚”,控制权限、处理逻辑,分三类:全局守卫、路由独享守卫、组件内守卫。
全局守卫:控制所有路由
最常用的是 router.beforeEach
(跳转前拦截)和 router.afterEach
(跳转后执行),比如做权限控制:用户没登录,就跳转到登录页。
// src/router/index.js router.beforeEach((to, from, next) => { // to:要去的路由;from:从哪来的路由;next:放行/跳转的函数 const isLogin = localStorage.getItem('token') // 假设用localStorage存登录状态 if (to.meta.requiresAuth && !isLogin) { // 路由元信息requiresAuth为true时需要登录 next('/login') // 没登录,跳登录页 } else { next() // 放行 } }) // 路由规则里加meta { path: '/order', component: Order, meta: { requiresAuth: true } // 需要登录才能访问 }
路由独享守卫:只拦单个路由
在路由规则里写 beforeEnter
,比如某个路由需要检查参数是否合法:
{ path: '/user/:id', component: UserDetail, beforeEnter: (to, from, next) => { const id = to.params.id if (/^\d+$/.test(id)) { // 检查id是否是数字 next() } else { next('/404') // 不是数字,跳404 } } }
组件内守卫:组件里的生命周期
常用 beforeRouteLeave
(离开组件前拦截),比如用户填了表单没保存,离开时提示:
// Vue2组件 export default { data() { return { formDirty: false } }, beforeRouteLeave(to, from, next) { if (this.formDirty) { if (window.confirm('表单没保存,确定离开?')) { next() // 确定就放行 } else { next(false) // 取消就留在当前页 } } else { next() } } } // Vue3组件(选项式API里写,组合式API用onBeforeRouteLeave) import { onBeforeRouteLeave } from 'vue-router' export default { setup() { const formDirty = ref(false) onBeforeRouteLeave((to, from, next) => { if (formDirty.value) { // 同上逻辑 } }) return { formDirty } } }
这些守卫能解决的问题太多了:权限控制、数据预加载(进入路由前先拉数据)、离开页面确认、埋点统计(afterEach里记跳转次数)……
vue2和vue3里的vue-router用法有啥不一样?
Vue2用 vue-router@3
,Vue3用 vue-router@4
,核心区别在创建路由的方式、API风格、路由模式写法、组件内使用方式这些地方:
创建路由实例
Vue2:new VueRouter({ routes, mode })
;
Vue3
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。