4. 音频采集
单页应用(SPA)的核心,说白了就是「页面不刷新,内容随便换」。Vue Router 就是帮我们实现这件事的官方工具。我刚开始用 Vue 做项目时,总觉得路由就是个跳转链接,后来踩了几个坑才明白——路由设计的好坏,直接决定了项目的可维护性。
这一章,我们一步步把 Vue Router 装进语音识别前端系统里。从安装配置,到嵌套路由,再到导航守卫,我会把项目中实际遇到的问题都告诉你。
4.1 安装与基础配置
先装包。用 npm 或 yarn 都行,我个人习惯用 npm:
npm install vue-router@4
装完之后,在 src 目录下新建一个 router 文件夹,里面放 index.js。这是路由的配置文件,所有页面路径都在这定义。
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
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(),
routes
})
export default router
然后在 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')
嗯,这里要注意:createWebHistory 用的是 HTML5 的 History 模式,URL 里没有 #,看着清爽。但如果你部署到生产环境,需要后端配合做重定向,否则刷新页面会 404。我之前有个项目上线忘了配,结果用户一刷新就白屏……那场面,别提多尴尬了。
createWebHistory 没问题。如果后端不好配,可以改用 createWebHashHistory,URL 里会带 #,但永远不会 404。
4.2 页面跳转:声明式 vs 编程式
Vue Router 提供了两种跳转方式。先看声明式,就是在模板里用 <router-link>:
<!-- 在 App.vue 或任何组件里 -->
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- 还可以传参数 -->
<router-link :to="{ name: 'About' }">关于(命名路由)</router-link>
另一种是编程式导航,在 JavaScript 里用 router.push:
// 在某个方法里
function goToAbout() {
router.push('/about')
}
// 或者带参数
router.push({ name: 'About', query: { source: 'home' } })
我个人更推荐用命名路由(name)而不是写死路径。为什么?因为路径可能会改,但名字一般不变。你想想看,如果项目做到一半,产品经理说「首页路径改成 /home 吧」,你只需要改一处配置,所有跳转都不用动。
name 就别用 path。这是我在多个项目中总结出的血泪教训。
4.3 嵌套路由:页面里的子页面
语音识别系统里,肯定有「用户中心」这种模块。用户中心下面可能有「个人资料」「我的录音」「设置」等子页面。这就是嵌套路由的典型场景。
先定义父路由,里面用 children 配置子路由:
const routes = [
{
path: '/user',
name: 'User',
component: () => import('../views/User.vue'),
children: [
{
path: 'profile',
name: 'UserProfile',
component: () => import('../views/UserProfile.vue')
},
{
path: 'recordings',
name: 'UserRecordings',
component: () => import('../views/UserRecordings.vue')
},
{
path: 'settings',
name: 'UserSettings',
component: () => import('../views/UserSettings.vue')
}
]
}
]
然后在 User.vue 里放一个 <router-view>,子页面就会渲染到这里:
<!-- User.vue -->
<template>
<div class="user-container">
<nav>
<router-link to="/user/profile">个人资料</router-link>
<router-link to="/user/recordings">我的录音</router-link>
<router-link to="/user/settings">设置</router-link>
</nav>
<router-view />
</div>
</template>
注意子路由的 path 前面不要加 /,否则会变成绝对路径。我曾经犯过这个错,子路由怎么跳都跳到根路径去,查了半天才发现是多了一个斜杠。
path 不要以 / 开头。加了 / 就是绝对路径,会脱离父路由的上下文。
4.4 导航守卫:控制谁能进哪个页面
语音识别系统里,有些页面需要登录才能访问。比如「我的录音」页面,没登录的用户应该被重定向到登录页。导航守卫就是干这个的。
Vue Router 提供了三种守卫:全局守卫、路由独享守卫、组件内守卫。最常用的是全局前置守卫 beforeEach:
// src/router/index.js
router.beforeEach((to, from, next) => {
// 检查目标路由是否需要登录
if (to.meta.requiresAuth) {
// 假设我们有个 auth 模块来判断登录状态
const isLoggedIn = localStorage.getItem('')
if (isLoggedIn) {
next()
} else {
next({ name: 'Login' })
}
} else {
next()
}
})
然后在路由配置里加 meta 字段:
{
path: '/user/recordings',
name: 'UserRecordings',
component: () => import('../views/UserRecordings.vue'),
meta: { requiresAuth: true }
}
这样,没登录的用户访问 /user/recordings 时,会自动跳到登录页。登录成功后,还可以用 router.replace 回到之前想去的页面。
除了全局守卫,还有路由独享守卫 beforeEnter:
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
// 只有管理员能进
const role = localStorage.getItem('role')
if (role === 'admin') {
next()
} else {
next({ name: 'Home' })
}
}
}
组件内守卫用得少一些,但有个场景很实用:用户编辑表单时,不小心点了返回,你想弹个确认框。用 beforeRouteLeave 就能拦截:
// 在组件内部
beforeRouteLeave(to, from, next) {
if (this.hasUnsavedChanges) {
const answer = window.confirm('你有未保存的修改,确定要离开吗?')
if (answer) {
next()
} else {
next(false)
}
} else {
next()
}
}
next() 一定要调用,否则页面会卡住不动。我第一次写守卫时忘了调 next(),结果页面白屏,控制台也没报错,排查了半天……
4.5 路由设计:语音识别系统的实际案例
好了,理论说完了。我们来看看语音识别前端系统的路由该怎么设计。下面这张图是我画的路由结构:
对应的路由配置大概是这样的:
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
},
{
path: '/recording/:id',
name: 'RecordingDetail',
component: () => import('../views/RecordingDetail.vue'),
props: true // 把 params 作为 props 传给组件
},
{
path: '/user',
name: 'User',
component: () => import('../views/User.vue'),
meta: { requiresAuth: true },
children: [
{
path: 'profile',
name: 'UserProfile',
component: () => import('../views/UserProfile.vue')
},
{
path: 'recordings',
name: 'UserRecordings',
component: () => import('../views/UserRecordings.vue')
},
{
path: 'settings',
name: 'UserSettings',
component: () => import('../views/UserSettings.vue')
}
]
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('../views/NotFound.vue')
}
]
注意最后那个 /:pathMatch(.*)*,这是 Vue Router 4 的 404 通配写法。Vue Router 3 用的是 *,升级到 4 后改成了正则匹配。我第一次升级项目时没注意这个,结果所有未匹配路径都白屏了。
/recording/:id 里的 :id 是动态参数。在组件里用 route.params.id 获取。加上 props: true 后,可以直接用 props 接收,更符合 Vue 的组件设计理念。
4.6 路由懒加载:让首屏更快
语音识别系统里,有些页面(比如录音详情)可能包含很大的组件。如果所有页面都打包到一个文件里,首屏加载会变慢。用路由懒加载可以按需加载,只有访问到某个路由时才加载对应的组件。
实现方式很简单,把 component 改成箭头函数 + import():
{
path: '/recording/:id',
name: 'RecordingDetail',
component: () => import('../views/RecordingDetail.vue')
}
Webpack 或 Vite 会自动把每个懒加载的路由打包成独立的 chunk。你可以在浏览器开发者工具的 Network 面板里看到,访问新路由时会多一个请求。
我个人习惯把所有路由都写成懒加载,除了首页。首页是用户第一个看到的页面,预加载能减少白屏时间。其他页面用户不一定访问,按需加载就好。
<link rel="prefetch">,还能在空闲时预加载用户可能访问的页面。
4.7 总结一下
Vue Router 的核心就三件事:配置路由、实现跳转、控制访问。配置路由时注意嵌套和动态参数;跳转时优先用命名路由;控制访问用导航守卫,别忘了调 next()。
语音识别系统的路由设计,我建议你从首页和登录页开始,逐步添加用户中心和录音详情。每加一个路由,都想想它需不需要登录、有没有子页面、要不要懒加载。这样一步步来,不会乱。
嗯,这一章的内容就到这。路由配好了,下一步就是页面之间的数据传递和状态管理了。