Appearance
Vue Router 课程计划
课程概述
本课程将带你从零开始实现一个路由系统,理解 Vue Router 的核心原理。
课程目标
- 理解路由系统的原理
- 掌握路由匹配和导航
- 学习路由守卫和历史管理
- 能够构建自己的路由库
课程结构
8 节课,每节 20-40 分钟,总时长约 90-120 分钟。
第 1 课:Vue Router 简介
目标
- 了解 Vue Router 的背景
- 理解路由系统的概念
- 掌握 Vue Router 的核心功能
内容
Vue Router 背景
- 什么是路由
- 为什么需要路由
- SPA 的路由
核心概念
- 路由器
- 路由
- 导航守卫
- 路由参数
路由模式
- Hash 模式
- History 模式
- Memory 模式
实践步骤
bash
# 1. 创建项目
mkdir vue-router-course
cd vue-router-course
# 2. 初始化项目
npm init -y
# 3. 安装 Vue
npm install vue --save预期输出
✓ 项目初始化完成
✓ Vue 安装完成总结
- ✅ 理解了路由的概念
- ✅ 掌握了核心功能
- ✅ 了解了路由模式
第 2 课:路由匹配
目标
- 理解路由匹配的原理
- 实现路由匹配器
- 学习参数提取
内容
路由匹配
- 什么是路由匹配
- 匹配算法
- 参数提取
实现路由匹配
- 路径匹配
- 正则表达式
- 参数解析
测试路由匹配
- 单元测试
- 边界测试
实践步骤
bash
# 1. 创建路由匹配器
cat > src/matcher.js << 'EOF'
export function matchRoute(path, route) {
const regex = new RegExp('^' + route.path.replace(/:\w+/g, '([^/]+)') + '$')
const match = path.match(regex)
if (match) {
return {
route,
params: extractParams(match, route.path)
}
}
return null
}
function extractParams(match, path) {
const params = {}
const paramNames = path.match(/:(\w+)/g) || []
paramNames.forEach((name, index) => {
params[name.substring(1)] = match[index + 1]
})
return params
}
EOF
# 2. 创建测试
cat > test/matcher.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { matchRoute } from '../src/matcher.js'
describe('路由匹配测试', () => {
it('应该匹配路由', () => {
const route = { path: '/users/:id' }
const result = matchRoute('/users/123', route)
assert.ok(result)
assert.strictEqual(result.params.id, '123')
})
})
EOF
# 3. 运行测试
npm test预期输出
✓ 应该匹配路由总结
- ✅ 实现了路由匹配
- ✅ 理解了匹配算法
- ✅ 掌握了参数提取
第 3 课:路由器实现
目标
- 理解路由器的原理
- 实现路由器
- 学习路由注册
内容
路由器
- 什么是路由器
- 路由器职责
- 路由器配置
实现路由器
- 路由注册
- 路由匹配
- 当前路由
测试路由器
- 单元测试
- 集成测试
实践步骤
bash
# 1. 创建路由器
cat > src/router.js << 'EOF'
export class Router {
constructor(options = {}) {
this.routes = options.routes || []
this.currentRoute = null
}
addRoute(route) {
this.routes.push(route)
}
match(path) {
for (const route of this.routes) {
const result = matchRoute(path, route)
if (result) {
this.currentRoute = result
return result
}
}
return null
}
}
EOF
# 2. 创建测试
cat > test/router.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { Router } from '../src/router.js'
describe('路由器测试', () => {
it('应该创建路由器', () => {
const router = new Router()
assert.ok(router)
})
it('应该匹配路由', () => {
const router = new Router({
routes: [
{ path: '/', component: 'Home' },
{ path: '/about', component: 'About' }
]
})
const result = router.match('/')
assert.ok(result)
})
})
EOF
# 3. 运行测试
npm test预期输出
✓ 应该创建路由器
✓ 应该匹配路由总结
- ✅ 实现了路由器
- ✅ 理解了路由器职责
- ✅ 掌握了路由注册
第 4 课:历史管理
目标
- 理解历史管理的原理
- 实现历史管理器
- 学习 URL 同步
内容
历史管理
- 什么是历史管理
- History API
- Hash 模式
实现历史管理
- URL 同步
- 导航控制
- 事件监听
测试历史管理
- 单元测试
- 浏览器测试
实践步骤
bash
# 1. 创建历史管理器
cat > src/history.js << 'EOF'
export class HTML5History {
constructor(router) {
this.router = router
this.current = window.location.pathname
window.addEventListener('popstate', this.handlePopState.bind(this))
}
push(path) {
window.history.pushState({}, '', path)
this.current = path
this.router.match(path)
}
replace(path) {
window.history.replaceState({}, '', path)
this.current = path
this.router.match(path)
}
handlePopState(event) {
this.current = window.location.pathname
this.router.match(this.current)
}
}
EOF
# 2. 创建测试
cat > test/history.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { HTML5History } from '../src/history.js'
describe('历史管理测试', () => {
it('应该创建历史管理器', () => {
const history = new HTML5History({})
assert.ok(history)
})
})
EOF
# 3. 运行测试
npm test预期输出
✓ 应该创建历史管理器总结
- ✅ 实现了历史管理
- ✅ 理解了 History API
- ✅ 掌握了 URL 同步
第 5 课:路由守卫
目标
- 理解路由守卫的原理
- 实现路由守卫
- 学习导航控制
内容
路由守卫
- 什么是路由守卫
- 守卫类型
- 执行顺序
实现路由守卫
- 全局守卫
- 路由独享守卫
- 组件内守卫
测试路由守卫
- 单元测试
- 集成测试
实践步骤
bash
# 1. 创建路由守卫
cat > src/guards.js << 'EOF'
export class RouterGuard {
constructor(router) {
this.router = router
this.beforeEachGuards = []
this.afterEachGuards = []
}
beforeEach(guard) {
this.beforeEachGuards.push(guard)
}
afterEach(guard) {
this.afterEachGuards.push(guard)
}
async runBeforeGuards(to, from) {
for (const guard of this.beforeEachGuards) {
const result = await guard(to, from)
if (result !== undefined) {
return result
}
}
}
async runAfterGuards(to, from) {
for (const guard of this.afterEachGuards) {
await guard(to, from)
}
}
}
EOF
# 2. 创建测试
cat > test/guards.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { RouterGuard } from '../src/guards.js'
describe('路由守卫测试', () => {
it('应该创建路由守卫', () => {
const guard = new RouterGuard({})
assert.ok(guard)
})
it('应该执行守卫', async () => {
const guard = new RouterGuard({})
let executed = false
guard.beforeEach(() => {
executed = true
})
await guard.runBeforeGuards({}, {})
assert.strictEqual(executed, true)
})
})
EOF
# 3. 运行测试
npm test预期输出
✓ 应该创建路由守卫
✓ 应该执行守卫总结
- ✅ 实现了路由守卫
- ✅ 理解了守卫类型
- ✅ 掌握了导航控制
第 6 课:嵌套路由
目标
- 理解嵌套路由的原理
- 实现嵌套路由
- 学习路由视图
内容
嵌套路由
- 什么是嵌套路由
- 路由视图
- 嵌套配置
实现嵌套路由
- 路由嵌套
- 视图渲染
- 参数传递
测试嵌套路由
- 单元测试
- 集成测试
实践步骤
bash
# 1. 创建嵌套路由
cat > src/nested.js << 'EOF'
export function resolveNestedRoute(routes, path) {
const segments = path.split('/').filter(Boolean)
let currentRoutes = routes
let matched = []
for (const segment of segments) {
const matchedRoute = currentRoutes.find(route => {
return route.path === '/' + segment || route.path === '/' + segment + '/:id'
})
if (matchedRoute) {
matched.push(matchedRoute)
currentRoutes = matchedRoute.children || []
}
}
return matched
}
EOF
# 2. 创建测试
cat > test/nested.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { resolveNestedRoute } from '../src/nested.js'
describe('嵌套路由测试', () => {
it('应该解析嵌套路由', () => {
const routes = [
{
path: '/users',
children: [
{ path: '/:id', component: 'UserDetail' }
]
}
]
const matched = resolveNestedRoute(routes, '/users/123')
assert.ok(matched.length > 0)
})
})
EOF
# 3. 运行测试
npm test预期输出
✓ 应该解析嵌套路由总结
- ✅ 实现了嵌套路由
- ✅ 理解了路由视图
- ✅ 掌握了嵌套配置
第 7 课:动态路由
目标
- 理解动态路由的原理
- 实现动态路由
- 学习路由懒加载
内容
动态路由
- 什么是动态路由
- 动态添加路由
- 路由懒加载
实现动态路由
- 路由注册
- 组件加载
- 路由移除
测试动态路由
- 单元测试
- 集成测试
实践步骤
bash
# 1. 创建动态路由
cat > src/dynamic.js << 'EOF'
export function addDynamicRoute(router, route) {
router.addRoute(route)
}
export function removeDynamicRoute(router, path) {
const index = router.routes.findIndex(r => r.path === path)
if (index > -1) {
router.routes.splice(index, 1)
}
}
export function loadComponent(component) {
if (typeof component === 'function') {
return component()
}
return Promise.resolve(component)
}
EOF
# 2. 创建测试
cat > test/dynamic.test.js << 'EOF'
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { addDynamicRoute, removeDynamicRoute } from '../src/dynamic.js'
describe('动态路由测试', () => {
it('应该添加动态路由', () => {
const router = { routes: [] }
addDynamicRoute(router, { path: '/dynamic' })
assert.strictEqual(router.routes.length, 1)
})
it('应该移除动态路由', () => {
const router = { routes: [{ path: '/dynamic' }] }
removeDynamicRoute(router, '/dynamic')
assert.strictEqual(router.routes.length, 0)
})
})
EOF
# 3. 运行测试
npm test预期输出
✓ 应该添加动态路由
✓ 应该移除动态路由总结
- ✅ 实现了动态路由
- ✅ 理解了路由懒加载
- ✅ 掌握了组件加载
第 8 课:总结与扩展
目标
- 总结所有课程
- 回顾关键概念
- 提供扩展建议
内容
课程总结
- 回顾所有功能
- 总结关键概念
- 展示完整代码
扩展建议
- 添加更多功能
- 优化性能
- 支持更多特性
下一步
- 学习 Vue Router 源码
- 参与开源项目
- 构建自己的路由库
总结
通过本课程,你学会了:
- ✅ Vue Router 简介
- ✅ 路由匹配
- ✅ 路由器实现
- ✅ 历史管理
- ✅ 路由守卫
- ✅ 嵌套路由
- ✅ 动态路由
下一步
- 学习 Vue Router 源码
- 添加更多功能
- 参与开源项目
参考资源

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态
