一、Vite 项目里为啥要整合 Vue Router?
做 Vue 项目时,用 Vite 搭架构再常见不过,但路由管理咋和 Vue Router 结合?从项目初始化到复杂场景(动态路由、权限控制),再到部署踩坑,这篇文章把 Vite + Vue Router 的核心流程拆成问题逐个讲透,新手能跟着配,老手也能补细节~
单页应用(SPA)要做页面跳转、URL 管理,光靠组件切换可不够——用户刷新页面会回到首页、没法收藏特定页面、前进后退也乱套,Vue Router 是 Vue 生态官方路由工具,专门解决这些问题:它能管理 URL 和组件的映射关系,还能通过“路由守卫”控制权限、加载状态,让单页应用有像多页应用一样的路由体验。再看 Vite 和 Vue Router 的适配优势:Vite 主打“极速冷启动”,开发时改代码秒级热更新;Vue Router 4 对 Vue 3 做了深度优化(比如支持 Composition API、Tree - shaking 更彻底),两者结合后,开发阶段改路由配置能立刻看到效果,生产阶段路由懒加载能让首屏体积更小、加载更快,举个实际例子:用 Vite 搭的后台管理系统,加了 Vue Router 后,侧边栏切换子路由时页面不刷新,体验丝滑,还能通过路由守卫拦截未登录用户访问敏感页面。
怎么初始化一个带 Vue Router 的 Vite + Vue 项目?
想快速搭好基础环境,按这三步来:
用 Vite CLI 创建基础项目
打开终端,执行命令(选 Vue 模板):
npm create vite@latest my-vue-app -- --template vue
执行完后,进入项目目录并安装依赖:
cd my-vue-app npm install
安装 Vue Router
Vue 3 要搭配 Vue Router 4.x 版本,所以执行:
npm install vue-router@4
搭建路由相关目录结构
在 src 文件夹下新建 router(放路由配置)、views(放页面级组件)文件夹,最终目录大概长这样:
src/
├─ router/ # 路由核心配置
│ └─ index.js
├─ views/ # 页面组件(如首页、关于页)
│ ├─ HomeView.vue
│ └─ AboutView.vue
├─ components/ # 通用组件(如按钮、弹窗)
├─ App.vue # 根组件
└─ main.js # 项目入口
Vue Router 在 Vite 里的基础配置要注意啥?
核心是创建路由实例 + 挂载到 Vue 应用 + 渲染路由视图,分三步走:
写路由规则(src/router/index.js)
先引入 API 和组件,再定义路由映射关系:
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 路由规则数组
const routes = [
{
path: '/', // URL 路径(根路径)
name: 'home', // 路由名称(可选,用于编程式导航)
component: HomeView // 对应组件
},
{
path: '/about',
name: 'about',
// 懒加载:访问时再加载组件,减少首屏体积(后面优化部分细讲)
component: () => import('../views/AboutView.vue')
}
]
// 创建路由实例
const router = createRouter({
// history 模式:URL 更美观(如 /about),但部署要配服务端;hash 模式是 createWebHashHistory()(URL 带 #)
history: createWebHistory(),
routes // 传入路由规则
})
export default router
挂载路由到 Vue 应用(src/main.js)
把路由实例注入 Vue 应用,让所有组件能访问路由功能:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router' // 引入路由配置
createApp(App)
.use(router) // 挂载路由
.mount('#app')
用 和 渲染(App.vue)
<router-view> 是路由组件的“容器”,<router-link> 是导航链接(代替 <a> 避免页面刷新):
<template>
<div>
<!-- 导航栏 -->
<nav>
<router-link to="/">首页</router-link> |
<router-link to="/about">lt;/router-link>
</nav>
<!-- 路由组件渲染区域 -->
<router-view></router-view>
</div>
</template>
关键细节:createWebHistory 和 createWebHashHistory 咋选?
- 选
history模式,URL 更简洁(如https://xxx.com/about),但部署时服务器要配置“ fallback ”(否则刷新页面会 404); - 选
hash模式,URL 带 (如https://xxx.com/#/about),部署时不用改服务器配置,但 URL 美观度差一些。
动态路由在 Vite + Vue Router 里咋实现?
动态路由用来处理路径带参数的场景(比如商品详情页 /product/123、用户页 /user/456),核心是路由规则里定义参数 + 组件内获取参数。
配置动态路由规则
在 src/router/index.js 里加一条带参数的路由:
{
path: '/product/:id', // :id 是动态参数,冒号是固定语法
name: 'ProductDetail',
component: () => import('../views/ProductDetail.vue')
}
组件内获取参数
Vue 3 推荐用 组合式 API(useRoute),也支持选项式 API(this.$route)。
组合式 API 写法(ProductDetail.vue):
<template>
<div>当前商品 ID:{{ productId }}</div>
</template>
<script setup>
import { useRoute } from 'vue-router' // 引入路由 hooks
const route = useRoute()
const productId = route.params.id // 直接取动态参数
</script>
选项式 API 写法(老项目迁移用):
<template>
<div>当前商品 ID:{{ $route.params.id }}</div>
</template>
<script>
export default {
computed: {
productId() {
return this.$route.params.id
}
}
}
</script>
动态路由的“坑”与解决
路由参数变化时(比如从 /product/1 跳转到 /product/2),组件不会自动重新渲染!因为 Vue Router 认为“组件实例没销毁,只是参数变了”,解决方法有两种:
-
监听路由变化:用
watch监听route.params:import { watch } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() watch( () => route.params.id, (newId) => { // newId 变化时执行逻辑(比如重新请求商品数据) fetchProduct(newId) } ) -
强制组件销毁重建:给
<router-view>加key,值为$route.fullPath(URL 全路径):<router-view :key="$route.fullPath"></router-view>
嵌套路由怎么设计更合理?
嵌套路由适合页面布局有层级的场景(比如后台管理系统:顶部导航 + 侧边栏 + 内容区,内容区随侧边栏切换),核心是 children 配置 + 嵌套 <router-view>。
规划布局与路由结构
以“仪表盘(Dashboard)”为例,结构分层:
- 父组件
DashboardLayout.vue:包含侧边栏和内容区容器; - 子路由:
/dashboard(默认显示DashboardHome)、/dashboard/settings(显示DashboardSettings)。
配置嵌套路由规则(src/router/index.js)
{
path: '/dashboard',
component: () => import('../views/DashboardLayout.vue'), // 父布局组件
children: [
{
path: '', // 空路径表示“默认子路由”
name: 'DashboardHome',
component: () => import('../views/DashboardHome.vue')
},
{
path: 'settings',
name: 'DashboardSettings',
component: () => import('../views/DashboardSettings.vue')
}
]
}
父布局组件里渲染子路由(DashboardLayout.vue)
<template>
<div class="dashboard-wrapper">
<!-- 侧边栏 -->
<aside>
<ul>
<li><router-link to="/dashboard">首页</router-link></li>
<li><router-link to="/dashboard/settings">设置</router-link></li>
</ul>
</aside>
<!-- 子路由渲染区域 -->
<main>
<router-view></router-view> <!-- 子路由组件在这里显示 -->
</main>
</div>
</template>
嵌套路由的优势
- 布局复用:父组件(如
DashboardLayout)只渲染一次,子路由切换时,侧边栏、顶部导航这些固定布局不会重复渲染; - 路由层级清晰:URL 结构(
/dashboard/settings)和组件层级一一对应,后续维护时,看 URL 就知道对应的组件结构。
路由守卫怎么用在实际业务里?
路由守卫是控制页面权限、处理跳转逻辑的关键工具,分全局守卫、路由独享守卫、组件内守卫三类。
全局前置守卫:登录拦截(最常用场景)
需求:访问需要登录的页面(如 /profile)时,判断用户是否登录,没登录就跳转到登录页。
在 src/router/index.js 里配置:
router.beforeEach((to, from, next) => {
// 假设用 localStorage 存 token 判断登录状态
const isLogin = !!localStorage.getItem('token')
// 检查目标路由是否需要权限(通过 meta 字段标记)
if (to.meta.requiresAuth && !isLogin) {
next({ name: 'login' }) // 跳转到登录页
} else {
next() // 放行
}
})
然后在需要权限的路由里加 meta 标记:
{
path: '/profile',
name: 'Profile',
component: () => import('../views/Profile.vue'),
meta: { requiresAuth: true } // 标记该页面需要登录
}
路由独享守卫:进入路由前做逻辑
需求:访问 /order 前,检查订单数据是否加载,没加载就先请求数据。
在路由规则里直接写 beforeEnter:
{
path: '/order',
name: 'Order',
component: () => import('../views/Order.vue'),
beforeEnter: (to, from, next) => {
if (!window.orderData) { // 假设用 window 临时存数据(实际项目用状态管理更优)
fetch('/api/order').then(res => res.json()).then(data => {
window.orderData = data
next() // 数据加载完,放行
})
} else {
next() // 数据已存在,直接放行
}
}
}
组件内守卫:组件生命周期结合路由
需求:用户离开表单页面时,提示“是否放弃未保存内容”。
用 onBeforeRouteLeave(组合式 API):
<template>
<form>...</form>
</template>
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
onBeforeRouteLeave((to, from, next) => {
if (window.confirm('当前表单未保存,确定离开?')) {
next() // 确认离开,放行
} else {
next(false) // 取消离开,留在当前页
}
})
</script>
路由守卫的核心逻辑
to:目标路由对象(要跳转到哪个页面);from:当前路由对象(从哪个页面跳过来);next:必须调用的“放行函数”——调用next()放行,next(false)取消跳转,next({ name: 'xxx' })跳转到指定页面。
怎么通过路由懒加载优化 Vite 项目性能?
Vite 天生支持动态 import(),配合 Vue Router 的懒加载配置,能让首屏只加载“首页”等核心代码,其他页面按需加载,大幅减少首屏加载时间。
路由懒加载的原理
传统打包方式把所有页面组件塞到一个 JS 文件里,首屏要加载全部代码,体积大、速度慢,懒加载是把每个页面组件打成单独的代码块,访问对应路由时再加载,实现“按需加载”。
Vite 里的路由懒加载配置
在路由规则里,把 component 写成动态 import 函数即可:
{
path: '/about',
name: 'about',
// 动态 import,Vite 会自动处理代码分割
component: () => import('../views/AboutView.vue')
}
进阶:分组懒加载 + 加载状态
-
分组懒加载:把多个相关组件打包到一个代码块里(减少请求次数),比如后台管理的子页面:
// 把 Dashboard 相关组件打到同一个 chunk const DashboardRoutes = () => import('../views/dashboard/Index.vue') const DashboardHome = () => import('../views/dashboard/Home.vue') const DashboardSettings = () => import('../views/dashboard/Settings.vue') // 路由配置 { path: '/dashboard', component: DashboardRoutes, children: [ { path: '', component: DashboardHome }, { path: 'settings', component: DashboardSettings } ] } -
加载状态提示:用户访问懒加载页面时,可能看到“白屏”,所以要加加载中提示,用全局路由守卫 + 状态管理实现:
在src/router/index.js里:let loading = false router.beforeEach((to, from, next) => { loading = true // 路由开始加载,标记为 true next() }) router.afterEach(() => { loading = false // 路由加载完成,标记为 false })在
App.vue里显示加载状态:<template> <div v-if="loading" class="loading-mask">加载中...</div> <nav>...</nav> <router-view></router-view> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' const router = useRouter() const loading = ref(false) router.beforeEach(() => { loading.value = true }) router.afterEach(() => { loading.value = false }) </script>
部署 Vite + Vue Router 项目时路由有啥坑?
部署后最容易踩的是 路由模式不兼容,导致页面刷新 404,核心是 history 模式和 hash 模式的区别。
Hash 模式(createWebHashHistory)
URL 格式:http://xxx.com/#/about,路由信息存在 后面。部署时不需要服务端配置——因为浏览器只会把 前面的路径发给服务器,服务器返回 index.html 后,前端路由自己解析 后的内容。
- 优点:部署简单,兼容老服务器;
- 缺点:URL 带 ,美观度差。
History 模式(createWebHistory)
URL 格式:http://xxx.com/about,更像传统网站 URL,但服务器必须配置 fallback——当用户直接访问 /about 时,服务器要返回 index.html(否则会报 404,因为服务器没有 /about 这个物理文件)。
不同平台的配置方式:
-
Vercel/Netlify:自动支持 SPA fallback,部署时不用额外配置;
-
Nginx:在配置文件里加
try_files:server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; # 项目打包后的 dist 目录 index index.html; location / { try_files $uri $uri/ /
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网

