深入 Lyt.js 路由系统:L6 生态系统层的核心
基于 Lyt.js v6.6.0 @lytjs/router 和 @lytjs/router-fs 包源码,深入解析路由系统的设计理念、导航守卫、路由匹配和文件系统路由。
一、路由系统架构
在 Lyt.js v6.6.0 的 8 层架构中,路由系统位于 L6 生态系统层:
makefile
L5: 组件基础层
L4: 插件与适配层
L3: 核心运行时层
L2: 渲染引擎层
L1: 核心原语层
↓
L6: 生态系统层
├── @lytjs/router (路由系统)
├── @lytjs/router-fs (文件系统路由)
├── @lytjs/api (API 路由)
├── @lytjs/store (状态管理)
├── @lytjs/ui (UI 组件库)
└── ...
二、快速开始
typescript
import { createApp } from '@lytjs/core'
import { createRouter, createWebHistory } from '@lytjs/router'
import Home from './views/Home.lyt'
import About from './views/About.lyt'
import User from './views/User.lyt'
const router = createRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/user/:id', component: User },
]
})
const app = createApp(App)
app.use(router)
app.mount('#app')
三、路由模式
Lyt.js 支持两种路由模式:
3.1 History 模式
使用 HTML5 History API,适合有服务器配置的场景:
typescript
import { createWebHistory } from '@lytjs/router'
const router = createRouter({
mode: 'history',
history: createWebHistory('/app'),
routes: [...]
})
特点:
- URL 更加美观(
/user/123) - 需要服务器配置处理 404
- 支持
history.pushState和history.replaceState
3.2 Hash 模式
使用 URL hash(#),无需服务器配置:
typescript
import { createHashHistory } from '@lytjs/router'
const router = createRouter({
mode: 'hash',
routes: [...]
})
特点:
- 无需服务器配置
- URL 包含
#(/#/user/123) - 适合静态托管环境
四、路由定义
4.1 基础路由
typescript
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/contact', component: Contact },
]
4.2 动态路由
typescript
const routes = [
{ path: '/user/:id', component: User },
{ path: '/post/:category/:slug', component: Post },
{ path: '/order/:id?', component: Order }, // 可选参数
]
4.3 嵌套路由
typescript
const routes = [
{
path: '/user/:id',
component: UserLayout,
children: [
{ path: '', component: UserProfile },
{ path: 'posts', component: UserPosts },
{ path: 'settings', component: UserSettings },
]
}
]
4.4 命名路由
typescript
const routes = [
{
path: '/user/:id',
name: 'user',
component: User
}
]
// 编程式导航
router.push({ name: 'user', params: { id: '123' } })
4.5 别名路由
typescript
const routes = [
{ path: '/home', component: Home, alias: '/' }
]
五、路由参数
5.1 组件中获取参数
typescript
// 方式一:通过 inject
import { useRoute } from '@lytjs/router'
const route = useRoute()
console.log(route.params.id) // URL 参数
console.log(route.query.search) // 查询参数
console.log(route.hash) // hash 值
// 方式二:通过 $route
defineComponent({
template: '<p>User ID: {{ $route.params.id }}</p>'
})
5.2 参数变化监听
typescript
const route = useRoute()
// 监听参数变化
watch(
() => route.params.id,
(newId, oldId) => {
console.log(`ID 从 ${oldId} 变为 ${newId}`)
// 重新获取数据
}
)
六、导航守卫
6.1 全局前置守卫
typescript
router.beforeEach((to, from, next) => {
// to: 目标路由
// from: 当前路由
// next: 继续导航的函数
if (to.meta.requiresAuth && !isLoggedIn()) {
next('/login')
} else {
next()
}
})
6.2 全局解析守卫
typescript
router.beforeResolve((to, from, next) => {
// 在导航被确认前,所有组件内守卫和异步路由组件被解析之后调用
// 适合做数据预获取
fetchData(to.params.id).then(data => {
to.meta.data = data
next()
})
})
6.3 全局后置钩子
typescript
router.afterEach((to, from) => {
// 导航确认后调用
document.title = to.meta.title || 'Lyt.js App'
analytics.pageView(to.path)
})
6.4 路由独享守卫
typescript
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (isAdmin()) {
next()
} else {
next('/403')
}
}
}
]
6.5 组件内守卫
typescript
defineComponent({
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被确认前调用
// 不能访问 this
next(vm => {
// 通过 vm 访问组件实例
})
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但该组件被复用时调用
// 适合处理参数变化
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问 this
if (hasUnsavedChanges()) {
const answer = confirm('有未保存的更改,确定离开吗?')
if (answer) {
next()
} else {
next(false)
}
} else {
next()
}
}
})
6.7 导航守卫执行顺序
- 导航触发
- 失活的组件调用
beforeRouteLeave - 全局
beforeEach - 复用组件调用
beforeRouteUpdate - 路由独享
beforeEnter - 解析异步路由组件
- 激活组件调用
beforeRouteEnter - 全局
beforeResolve - 导航确认
- 全局
afterEach
七、编程式导航
7.1 基础导航
typescript
// 字符串路径
router.push('/user/123')
// 对象路径
router.push({ path: '/user/123' })
// 命名路由
router.push({ name: 'user', params: { id: '123' } })
// 带查询参数
router.push({ path: '/search', query: { q: 'vue' } })
// 替换当前记录
router.replace('/home')
// 前进/后退
router.go(1) // 前进
router.go(-1) // 后退
router.back() // 后退
router.forward() // 前进
7.2 导航控制
typescript
// 取消导航
router.beforeEach((to, from, next) => {
if (needsGuard(to)) {
next(false) // 取消导航
} else {
next()
}
})
// 重定向
router.beforeEach((to, from, next) => {
if (to.path === '/old') {
next('/new')
} else {
next()
}
})
// 导航错误
router.onError(error => {
console.error('导航错误:', error)
})
八、元信息(Meta)
8.1 定义 Meta
typescript
const routes = [
{
path: '/admin',
component: Admin,
meta: {
requiresAuth: true,
role: 'admin',
title: '管理后台'
}
}
]
8.2 访问 Meta
typescript
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
// 需要认证
}
if (to.meta.role === 'admin') {
// 需要管理员权限
}
next()
})
九、路由懒加载
9.1 箭头函数
typescript
const routes = [
{ path: '/home', component: () => import('./views/Home.vue') },
{
path: '/about',
component: () => import('./views/About.vue')
.then(m => m.default) // 支持 Promise
}
]
9.2 路由懒加载 + Loading
typescript
import { defineAsyncComponent } from '@lytjs/core'
const routes = [
{
path: '/dashboard',
component: defineAsyncComponent({
loader: () => import('./views/Dashboard.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorBoundary,
delay: 200,
timeout: 3000
})
}
]
9.3 路由分组
typescript
// 将某个路由下的所有组件都打包在同一个异步块中
const routes = [
{
path: '/admin',
component: AdminLayout,
children: [
{ path: 'users', component: () => import('./views/AdminUsers.vue') },
{ path: 'settings', component: () => import('./views/AdminSettings.vue') }
]
}
]
十、过渡动画
typescript
// 路由过渡
defineComponent({
template: `
<Transition name="fade" mode="out-in">
<router-view />
</Transition>
`
})
// CSS
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
十一、路由类型安全
Lyt.js 支持 TypeScript 类型推导:
typescript
import type { RouteRecordRaw } from '@lytjs/router'
const routes: RouteRecordRaw[] = [
{
path: '/user/:id',
name: 'User',
component: () => import('./views/User.vue'),
props: true, // 将路由参数作为 props 传递给组件
children: [
{
path: 'posts',
component: () => import('./views/UserPosts.vue')
}
]
}
]
// 组件中使用
defineComponent({
props: {
id: String // 从路由参数自动获取
},
setup(props) {
// props.id 已有类型
}
})
十二、文件系统路由
Lyt.js v6.6.0 提供了 @lytjs/router-fs 包,支持基于文件系统的路由:
bash
src/
└── pages/
├── index.lyt → /
├── about.lyt → /about
├── user/
│ ├── index.lyt → /user
│ └── [id].lyt → /user/:id
└── blog/
├── index.lyt → /blog
└── [slug].lyt → /blog/:slug
typescript
import { createFileSystemRouter } from '@lytjs/router-fs'
const router = createFileSystemRouter('./src/pages', {
extensions: ['.lyt', '.vue'],
// 导入模式
importMode: 'async', // 'sync' | 'async' | (path) => Promise
// 路由选项
routes: {
index: { name: 'home' },
dynamic: { prefix: '[' }
}
})
十三、在 v6.6.0 中的位置
路由系统在 8 层架构中的位置:
bash
L1: 核心原语层
├── @lytjs/reactivity
├── @lytjs/vdom
└── @lytjs/compiler
L2: 渲染引擎层
├── @lytjs/renderer
├── @lytjs/component
└── @lytjs/dom-runtime
L3: 核心运行时层
└── @lytjs/core
L4: 插件与适配层
├── @lytjs/plugin-auth (权限插件)
└── ...
L5: 组件基础层
L6: 生态系统层 ← 当前层
├── @lytjs/router
├── @lytjs/router-fs
├── @lytjs/store
└── ...
L7: 工程化工具层
路由系统依赖核心运行时层,同时被插件层(如权限插件)扩展。