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前端网发表,如需转载,请注明页面地址。
code前端网


