1. 为什么需要前端路由?
现代Web应用早已不再是简单的多页面跳转,而是演变成了复杂的单页应用(SPA)。想象一下你正在使用一个在线文档编辑器——每次点击菜单项时,页面不会完全刷新,但URL会变化,内容区域会无缝切换。这种体验的背后,正是前端路由在发挥作用。
我2016年第一次在项目中使用Vue Router时,它彻底改变了我对前端导航的认知。传统后端路由每次跳转都要重新加载整个页面,而前端路由只需按需更新视图层,配合Ajax数据请求,实现了近乎原生应用的流畅体验。
2. Vue Router核心概念解析
2.1 路由的本质是什么?
路由的核心是建立URL与组件之间的映射关系。当URL变化时,Vue Router会:
- 解析URL路径
- 匹配预先定义的路由规则
- 动态渲染对应的组件
- 保持其他部分不变
这就像酒店的楼层导航图——改变房间号(URL)只会带你到对应的房间(组件),而大堂(布局)始终保持不变。
2.2 基础配置实战
创建一个基础路由配置文件:
javascript复制// 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(process.env.BASE_URL),
routes
})
export default router
关键点说明:
createWebHistory使用HTML5 History API(无#号URL)routes数组定义所有路由规则- 每个路由对象包含path、name、component三个基本属性
注意:在Vue 2项目中应使用
new VueRouter()构造函数,而Vue 3使用createRouter工厂函数
3. 动态路由与高级匹配模式
3.1 带参数的路由配置
实际项目中,我们经常需要处理像/user/123这样的动态路径:
javascript复制{
path: '/user/:id',
name: 'User',
component: UserProfile
}
在UserProfile组件中,可以通过$route.params.id获取参数值。但更推荐使用props解耦:
javascript复制{
path: '/user/:id',
props: true,
component: UserProfile
}
// UserProfile.vue
export default {
props: ['id']
}
3.2 复杂路由匹配技巧
路由匹配支持强大正则表达式:
javascript复制// 只匹配数字ID
path: '/user/:id(\\d+)'
// 可选参数
path: '/search/:query?'
// 多段参数
path: '/files/:path*'
我曾在一个CMS项目中遇到这样的需求:需要区分/posts/2023和/posts/vue-router。通过自定义正则轻松实现:
javascript复制path: '/posts/:year(\\d{4})',
path: '/posts/:slug'
4. 编程式导航与路由守卫
4.1 导航的两种方式
除了使用<router-link>,编程式导航更加灵活:
javascript复制// 字符串路径
router.push('/about')
// 带参数
router.push({ name: 'User', params: { id: 123 } })
// 带查询参数
router.push({ path: '/search', query: { q: 'vue' } })
实际项目中,我更喜欢使用命名路由(name)而非路径,因为:
- 避免硬编码路径
- 重构时只需修改路由配置
- 参数传递更明确
4.2 路由守卫实战指南
路由守卫是权限控制的利器,常见场景包括:
- 登录验证
- 页面访问权限
- 数据预加载
- 表单离开确认
全局前置守卫示例:
javascript复制router.beforeEach((to, from, next) => {
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated) {
next('/login')
} else {
next()
}
})
组件内守卫同样实用:
javascript复制export default {
beforeRouteEnter(to, from, next) {
// 不能访问this,因为组件实例还没创建
next(vm => {
// 通过vm访问组件实例
})
},
beforeRouteUpdate(to, from, next) {
// 当前路由改变但组件被复用时调用
this.userData = fetchUser(to.params.id)
next()
}
}
5. 路由元信息与懒加载优化
5.1 元数据的高级用法
meta字段可以存储任意路由信息:
javascript复制{
path: '/admin',
meta: {
requiresAuth: true,
adminOnly: true,
transition: 'fade'
}
}
在导航守卫中可以通过to.meta访问这些数据。我在后台管理系统中的典型应用:
javascript复制router.beforeEach((to, from, next) => {
const userRole = store.state.user.role
if (to.meta.adminOnly && userRole !== 'admin') {
next('/forbidden')
} else {
next()
}
})
5.2 性能优化:路由懒加载
随着项目增长,应该拆分代码块:
javascript复制// 静态导入(打包到一个文件)
import Home from '../views/Home.vue'
// 动态导入(单独代码块)
const About = () => import('../views/About.vue')
Webpack会为每个动态导入的组件生成单独的文件,按需加载。实测数据:
- 首页加载时间减少40%
- 首屏资源体积缩小65%
进阶技巧:给webpackChunkName注释
javascript复制const UserProfile = () => import(/* webpackChunkName: "user" */ '../views/User.vue')
6. 常见问题排查手册
6.1 路由不生效的7个检查点
- 路由实例是否挂载:确保main.js中
app.use(router) <router-view>是否存在:检查App.vue是否包含出口- base路径配置:检查
process.env.BASE_URL是否正确 - history模式后端支持:非根目录部署需要服务器配置
- 路由定义顺序:通用路由应放在最后
- 组件命名冲突:避免与HTML标签同名
- 浏览器缓存:开发时尝试强制刷新
6.2 动态路由参数更新问题
当从/user/1跳转到/user/2时,如果组件没有更新:
- 使用
beforeRouteUpdate守卫 - 或者watch
$route对象 - 或者给
<router-view>添加key:
html复制<router-view :key="$route.fullPath" />
6.3 滚动行为定制
创建路由实例时:
javascript复制const router = createRouter({
scrollBehavior(to, from, savedPosition) {
// 返回期望的滚动位置
if (savedPosition) {
return savedPosition
} else if (to.hash) {
return { el: to.hash }
} else {
return { top: 0 }
}
}
})
7. 企业级项目最佳实践
7.1 模块化路由设计
大型项目推荐按功能拆分路由:
code复制src/
router/
index.js # 主路由
auth.js # 认证相关路由
admin.js # 管理后台路由
customer.js # 客户端路由
然后在主文件中合并:
javascript复制import authRoutes from './auth'
import adminRoutes from './admin'
const routes = [
...authRoutes,
...adminRoutes,
// 其他路由
]
7.2 权限路由动态加载
结合Vuex实现动态路由:
javascript复制// 登录成功后
store.dispatch('user/login').then(roles => {
// 根据角色过滤路由
const allowedRoutes = filterRoutes(roles)
// 动态添加路由
allowedRoutes.forEach(route => {
router.addRoute(route)
})
// 跳转到首页
router.push('/dashboard')
})
7.3 路由过渡动画技巧
给所有路由添加统一过渡:
html复制<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
特定路由自定义动画:
javascript复制{
path: '/special',
component: SpecialPage,
meta: {
transition: 'slide'
}
}
然后在router-view中:
javascript复制<transition :name="$route.meta.transition || 'fade'">
8. Vue Router 4.x新特性解析
8.1 组合式API支持
useRouter和useRoute替代this.$router和this.$route:
javascript复制import { useRouter, useRoute } from 'vue-router'
export default {
setup() {
const router = useRouter()
const route = useRoute()
const navigate = () => {
router.push(`/user/${route.params.id}/profile`)
}
return { navigate }
}
}
8.2 路由匹配语法升级
新的路径匹配语法更强大:
javascript复制// 可重复参数
path: '/files/:path+'
// 可选参数组
path: '/:lang(en|zh)?/about'
8.3 路由API改进
router.addRoute()动态添加路由router.removeRoute()删除路由router.hasRoute()检查路由是否存在router.getRoutes()获取所有路由记录
9. 测试与调试技巧
9.1 单元测试策略
测试路由组件时:
javascript复制import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{ path: '/', component: Home }
]
const router = createRouter({
history: createWebHistory(),
routes
})
test('renders home component', async () => {
router.push('/')
await router.isReady()
const wrapper = mount(App, {
global: {
plugins: [router]
}
})
expect(wrapper.findComponent(Home).exists()).toBe(true)
})
9.2 开发调试工具
- Vue DevTools:查看路由状态和导航历史
- 路由日志:开发时添加导航日志
javascript复制router.afterEach((to, from) => {
console.log(`Navigated from ${from.path} to ${to.path}`)
})
- 路由快照测试:确保路由配置不变
javascript复制expect(router.getRoutes()).toMatchSnapshot()
10. 性能优化进阶
10.1 预加载策略
javascript复制router.beforeEach((to, from, next) => {
if (to.meta.preload) {
const components = to.matched.map(record => record.components.default)
components.forEach(component => {
if (typeof component === 'function') {
component()
}
})
}
next()
})
10.2 路由分包优化
按路由层级分包:
javascript复制const UserProfile = () => import('../views/user/Profile.vue')
const UserSettings = () => import('../views/user/Settings.vue')
const routes = [
{
path: '/user',
component: () => import('../layouts/UserLayout.vue'),
children: [
{ path: 'profile', component: UserProfile },
{ path: 'settings', component: UserSettings }
]
}
]
10.3 持久化滚动位置
结合sessionStorage:
javascript复制const router = createRouter({
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
const key = to.path
const saved = sessionStorage.getItem(`scroll:${key}`)
if (saved) {
return JSON.parse(saved)
} else {
return { top: 0 }
}
}
})
router.afterEach((to) => {
window.addEventListener('scroll', () => {
const key = to.path
const position = { top: window.scrollY }
sessionStorage.setItem(`scroll:${key}`, JSON.stringify(position))
}, { once: true })
})
11. 实战案例:电商网站路由设计
11.1 路由结构设计
javascript复制const routes = [
{
path: '/',
component: MainLayout,
children: [
{ path: '', component: Home },
{ path: 'products', component: ProductList },
{ path: 'products/:id', component: ProductDetail },
{ path: 'cart', component: Cart },
{
path: 'checkout',
component: Checkout,
meta: { requiresAuth: true }
},
{ path: 'search', component: SearchResults }
]
},
{
path: '/login',
component: AuthLayout,
children: [
{ path: '', component: Login },
{ path: 'register', component: Register }
]
},
{
path: '/:pathMatch(.*)*',
component: NotFound
}
]
11.2 特殊场景处理
商品分类面包屑导航:
javascript复制// 路由配置
{
path: '/products/:category/:subcategory?',
component: ProductList,
props: route => ({
category: route.params.category,
subcategory: route.params.subcategory || null
})
}
// 组件内
export default {
props: ['category', 'subcategory'],
computed: {
breadcrumbs() {
const items = [
{ text: 'Home', to: '/' },
{ text: 'Products', to: '/products' }
]
if (this.category) {
items.push({
text: this.category,
to: `/products/${this.category}`
})
if (this.subcategory) {
items.push({
text: this.subcategory,
active: true
})
}
}
return items
}
}
}
搜索页面的URL设计:
javascript复制// 搜索组件
export default {
methods: {
performSearch() {
this.$router.push({
path: '/search',
query: {
q: this.searchQuery,
sort: this.sortBy
}
})
}
},
watch: {
'$route.query'(newQuery) {
this.loadResults(newQuery)
}
}
}
12. 与状态管理集成
12.1 Vuex与路由同步
保持路由状态与Vuex同步:
javascript复制// store/modules/router.js
export default {
state: {
currentRoute: null
},
mutations: {
SET_ROUTE(state, route) {
state.currentRoute = route
}
},
actions: {
updateRoute({ commit }, to) {
commit('SET_ROUTE', {
path: to.path,
params: to.params,
query: to.query
})
}
}
}
// 在路由守卫中
router.afterEach((to) => {
store.dispatch('router/updateRoute', to)
})
12.2 Pinia集成方案
使用Pinia更加简洁:
javascript复制// stores/router.js
export const useRouterStore = defineStore('router', {
state: () => ({
previousRoute: null,
currentRoute: null
}),
actions: {
setRoute(to, from) {
this.previousRoute = from
this.currentRoute = to
}
}
})
// 在组件中
const routerStore = useRouterStore()
routerStore.setRoute(route)
13. 服务端渲染(SSR)特别处理
13.1 路由模式选择
SSR需要特殊处理:
javascript复制// 客户端入口
import { createSSRApp } from 'vue'
import { createRouter } from './router'
export function createApp() {
const app = createSSRApp(App)
const router = createRouter()
app.use(router)
return { app, router }
}
// 服务端入口
import { createMemoryHistory } from 'vue-router'
export function createRouter() {
return createRouter({
history: createMemoryHistory(),
routes
})
}
13.2 数据预取策略
使用路由的serverPrefetch:
javascript复制// 路由配置
{
path: '/product/:id',
component: ProductPage,
meta: {
ssrPrefetch: true
}
}
// 组件内
export default {
async serverPrefetch() {
await this.fetchProduct(this.$route.params.id)
},
methods: {
fetchProduct(id) {
return store.dispatch('products/fetch', id)
}
}
}
14. 微前端集成方案
14.1 主应用路由配置
javascript复制const routes = [
{
path: '/app1/*',
component: () => import('@/layouts/MicroApp.vue'),
meta: {
microApp: 'app1'
}
},
{
path: '/app2/*',
component: () => import('@/layouts/MicroApp.vue'),
meta: {
microApp: 'app2'
}
}
]
14.2 子应用路由隔离
子应用应使用base路径:
javascript复制const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
并在主应用中动态设置:
javascript复制window.__POWERED_BY_QIANKUN__ && (process.env.BASE_URL = '/app1')
15. 移动端特殊处理
15.1 路由过渡优化
针对移动端性能优化:
css复制/* 使用transform代替left属性 */
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
}
.slide-leave-to {
transform: translateX(-30%);
}
15.2 手势返回支持
javascript复制let startX = 0
document.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX
}, { passive: true })
document.addEventListener('touchend', (e) => {
const deltaX = e.changedTouches[0].clientX - startX
if (deltaX > 50 && window.scrollX === 0) {
router.back()
}
}, { passive: true })
16. 安全最佳实践
16.1 路由权限验证
javascript复制router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAuth) {
try {
await store.dispatch('auth/verify')
next()
} catch (error) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
}
} else {
next()
}
})
16.2 敏感路由保护
javascript复制const sensitiveRoutes = ['/admin', '/dashboard']
router.beforeEach((to, from, next) => {
if (sensitiveRoutes.some(path => to.path.startsWith(path))) {
if (!store.state.user.isSuperAdmin) {
next('/forbidden')
return
}
}
next()
})
17. 调试与错误处理
17.1 导航失败处理
javascript复制router.push('/admin').catch(error => {
if (error.name === 'NavigationDuplicated') {
console.warn('重复导航到相同位置')
} else {
showErrorToast('导航失败')
}
})
17.2 路由错误边界
vue复制<template>
<router-view v-slot="{ Component }">
<ErrorBoundary>
<component :is="Component" />
</ErrorBoundary>
</router-view>
</template>
<script>
import ErrorBoundary from './ErrorBoundary.vue'
export default {
components: { ErrorBoundary }
}
</script>
18. 国际化路由方案
18.1 多语言路由设计
javascript复制const routes = [
{
path: '/:lang',
component: Layout,
children: [
{ path: '', component: Home },
{ path: 'about', component: About }
]
}
]
router.beforeEach((to, from, next) => {
const lang = to.params.lang || 'en'
if (!['en', 'zh', 'ja'].includes(lang)) {
next('/en' + to.fullPath)
} else {
store.commit('i18n/setLanguage', lang)
next()
}
})
18.2 SEO友好URL
javascript复制// 英文
{
path: '/en/products',
name: 'Products-en',
component: Products
}
// 中文
{
path: '/zh/products',
name: 'Products-zh',
component: Products
}
19. 与第三方库集成
19.1 与Google Analytics集成
javascript复制router.afterEach((to) => {
if (window.gtag) {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: to.path,
page_title: to.meta.title || 'Default Title'
})
}
})
19.2 与Sentry错误追踪
javascript复制router.onError((error) => {
Sentry.captureException(error)
})
20. 未来演进与替代方案
20.1 文件系统路由
类似Nuxt.js的约定式路由:
javascript复制// 自动生成路由
const pages = import.meta.glob('./pages/**/*.vue')
const routes = Object.keys(pages).map(path => {
const name = path
.replace(/^\.\/pages\//, '')
.replace(/\.vue$/, '')
.replace(/\//g, '-')
return {
path: '/' + name.toLowerCase(),
component: pages[path]
}
})
20.2 基于API的路由
从后端获取路由配置:
javascript复制async function initRouter() {
const response = await fetch('/api/routes')
const dynamicRoutes = await response.json()
dynamicRoutes.forEach(route => {
router.addRoute(route)
})
router.isReady().then(() => {
app.mount('#app')
})
}
21. 性能监控与优化
21.1 路由切换耗时统计
javascript复制let navigationStart
router.beforeEach(() => {
navigationStart = performance.now()
})
router.afterEach(() => {
const duration = performance.now() - navigationStart
console.log(`路由切换耗时: ${duration.toFixed(2)}ms`)
if (duration > 200) {
trackSlowNavigation(router.currentRoute.value)
}
})
21.2 组件加载性能分析
javascript复制const loadTimes = new Map()
router.beforeEach((to) => {
to.matched.forEach(record => {
if (typeof record.components.default === 'function') {
loadTimes.set(record.path, performance.now())
}
})
})
router.afterEach((to) => {
to.matched.forEach(record => {
if (loadTimes.has(record.path)) {
const loadTime = performance.now() - loadTimes.get(record.path)
console.log(`${record.path} 加载耗时: ${loadTime.toFixed(2)}ms`)
loadTimes.delete(record.path)
}
})
})
22. 无障碍访问(A11Y)优化
22.1 焦点管理
javascript复制router.afterEach(() => {
setTimeout(() => {
const main = document.querySelector('main')
if (main) {
main.setAttribute('tabindex', '-1')
main.focus()
}
}, 100)
})
22.2 屏幕阅读器提示
javascript复制router.afterEach((to) => {
const title = to.meta.title || '页面'
const liveRegion = document.getElementById('a11y-live-region')
if (liveRegion) {
liveRegion.textContent = `${title}已加载`
}
})
23. 渐进式Web应用(PWA)集成
23.1 离线路由处理
javascript复制// service-worker.js
const CACHE_NAME = 'routes-cache-v1'
const ROUTES_TO_CACHE = ['/', '/about', '/contact']
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
return cache.addAll(ROUTES_TO_CACHE)
})
)
})
// 在Vue组件中检查在线状态
export default {
data() {
return {
isOnline: navigator.onLine
}
},
created() {
window.addEventListener('online', this.updateOnlineStatus)
window.addEventListener('offline', this.updateOnlineStatus)
},
methods: {
updateOnlineStatus() {
this.isOnline = navigator.onLine
}
}
}
23.2 应用外壳架构
javascript复制// 主路由配置
const routes = [
{
path: '/',
component: AppShell,
children: [
{ path: '', component: Home },
// 其他路由...
]
}
]
24. 测试驱动开发(TDD)实践
24.1 路由单元测试
javascript复制import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import App from '@/App.vue'
describe('Router', () => {
it('navigates to about page', async () => {
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: { template: '<div>Home</div>' } },
{ path: '/about', component: { template: '<div>About</div>' } }
]
})
const wrapper = mount(App, {
global: {
plugins: [router]
}
})
await router.push('/about')
expect(wrapper.html()).toContain('About')
})
})
24.2 导航守卫测试
javascript复制describe('Auth Guard', () => {
it('redirects to login when not authenticated', async () => {
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/dashboard',
component: { template: '<div>Dashboard</div>' },
meta: { requiresAuth: true }
},
{ path: '/login', component: { template: '<div>Login</div>' } }
]
})
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
next('/login')
} else {
next()
}
})
await router.push('/dashboard')
expect(router.currentRoute.value.path).toBe('/login')
})
})
25. 部署与持续集成
25.1 History模式服务器配置
Nginx配置示例:
nginx复制location / {
try_files $uri $uri/ /index.html;
}
Apache配置示例:
apache复制<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
25.2 CI/CD中的路由测试
yaml复制# .github/workflows/test.yml
jobs:
test:
steps:
- name: Run router tests
run: |
npm run test:router
npm run test:router-guards
26. 社区资源与进阶学习
26.1 官方文档精要
26.2 推荐开源项目
- vue-router-layout - 智能布局系统
- vite-plugin-pages - 文件系统路由
- vue-router-composable - 组合式API增强
27. 从Vue 2迁移到Vue 3
27.1 破坏性变更处理
- new Router() → createRouter()
- mode → history
mode: 'history'→history: createWebHistory()mode: 'hash'→history: createWebHashHistory()
的tag属性移除 :使用v-slot API替代
27.2 迁移策略
- 先升级到vue-router 3.6(支持Vue 2但兼容Vue 3 API)
- 逐步替换旧API
- 使用迁移构建模式:
javascript复制import { createRouter } from 'vue-router'
const router = createRouter({
// 新配置
history: createWebHistory(),
routes
})
// 保持旧API可用
router.push = router.push.bind(router)
router.replace = router.replace.bind(router)
28. 可视化路由工具
28.1 路由关系图生成
javascript复制function generateRouteGraph(routes, parentPath = '') {
return routes.map(route => {
const fullPath = `${parentPath}${route.path}`
return {
path: fullPath,
name: route.name,
children: route.children ? generateRouteGraph(route.children, fullPath) : []
}
})
}
// 使用示例
const graphData = generateRouteGraph(router.options.routes)
console.log(JSON.stringify(graphData, null, 2))
28.2 与D3.js集成示例
javascript复制import * as d3 from 'd3'
export function renderRouteTree(container, routes) {
const width = 800
const height = 600
const treeLayout = d3.tree().size([width, height])
const root = d3.hierarchy(routes)
treeLayout(root)
const svg = d3.select(container)
.append('svg')
.attr('width', width)
.attr('height', height)
// 绘制节点和连线...
}
29. 微优化技巧合集
29.1 路由预取策略
javascript复制// 鼠标悬停时预取
<router-link
to="/about"
@mouseover="() => import('../views/About.vue')"
>
关于我们
</router-link>
29.2 智能预加载
javascript复制// 预加载当前页面的所有子路由
router.afterEach((to) => {
to.matched.forEach(record => {
record.components.default().catch(() => {})
})
})
29.3 路由缓存策略
javascript复制const routeCache = new Map()
router.beforeEach((to, from, next) => {
if (routeCache.has(to.path)) {
const { scrollPosition, data } = routeCache.get(to.path)
restorePage(data)
next(false)
setTimeout(() => {
window.scrollTo(scrollPosition)
}, 50)
} else {
next()
}
})
router.afterEach((to) => {
const data = capturePageState()
routeCache.set(to.path, {
scrollPosition: window.scrollY,
data
})
})
30. 终极性能检查清单
- 路由懒加载:所有非核心路由使用动态导入
- 组件复用:合理使用
key属性避免不必要的重建 - 预加载策略:对高优先级路由实施预加载
- 滚动恢复:实现平滑的滚动位置恢复
- 路由分割:按功能模块拆分路由配置
- 持久化状态:缓存频繁访问的路由状态
- 错误边界:防止单个路由错误影响整个应用
- 性能监控:跟踪路由切换耗时
- A/B测试:对关键路由进行性能对比
- 定期审计:使用路由分析工具检查冗余
31. 企业级架构设计模式
31.1 领域驱动设计(DDD)应用
javascript复制// src/modules/products/routes.js
export default [
{
path: '/products',
component: () => import('../views/ProductList.vue'),
meta: {
domain: 'catalog'
}
}
]
// src/router/index.js
import productRoutes from '@/modules/products/routes'
import orderRoutes from '@/modules/orders/routes'
const routes = [
...productRoutes,
...orderRoutes
]
31.2 前端BFF层路由
javascript复制// 根据API网关动态注册路由
async function initBffRoutes() {
const response = await fetch('/api/routes-config')
const config = await response.json()
config.modules.forEach(module => {
router.addRoute({
path: module.path,
component: () => import(`@/bff/${module.name}.vue`)
})
})
}
32. 路由与GraphQL集成
32.1 路由参数转GraphQL变量
javascript复制// 路由配置
{
path: '/product/:id',
component: ProductPage,
props: route => ({
id: route.params.id
})
}
// 组件内
export default {
props: ['id'],
apollo: {
product: {
query: gql`
query GetProduct($id: ID!) {
product(id: $id) {
id
name
}
}
`,
variables() {
return {
id: this.id
}
}
}
}
}
32.2 基于路由的预取查询
javascript复制router.beforeResolve(async (to) => {
if (to.meta.prefetchQuery) {
await apolloClient.query({
query: to.meta.prefetchQuery,
variables: to.meta.prefetchVariables?.(to) || {}
})
}
})
33. Web Components集成
33.1 自定义元素路由
javascript复制// 注册Web Component
class MyElement extends HTMLElement {
connectedCallback() {
this.innerHTML = `<div>Hello from Web Component</div>`
}
}
customElements.define('my-element', MyElement)
// 路由配置
{
path: '/custom',
component: {
template: '<my-element></my-element>'
}
}
33.2 混合架构路由方案
javascript复制// 主路由配置
const routes = [
{
path: '/legacy',
component: { template: '<div id="legacy-app"></div>' },
meta