Appearance
Vue Router 源代码导览
项目结构
vue-router-course/
├── 04-core-feature/ # 核心功能实现
│ ├── src/
│ │ ├── router.js # 路由器
│ │ ├── route.js # 路由记录
│ │ ├── history.js # 历史管理
│ │ └── matcher.js # 路由匹配
│ ├── test/
│ │ ├── router.test.js
│ │ ├── route.test.js
│ │ ├── history.test.js
│ │ └── matcher.test.js
│ ├── package.json
│ └── README.md
├── 05-lesson-plan.md # 课程计划
├── 01-intro.md # 背景研究
├── 02-arch.md # 架构分析
└── 03-code-walkthrough.md # 源代码导览核心文件解析
1. router.js - 路由器
文件路径: src/router.js
核心功能: 路由匹配,导航管理,历史管理。
关键代码:
javascript
// 路由器
export class Router {
constructor(options = {}) {
this.routes = options.routes || []
this.currentRoute = null
this.history = createHistory(options.mode || 'hash')
this.beforeEachHooks = []
this.afterEachHooks = []
this.init()
}
// 初始化路由
init() {
this.history.listen((location) => {
this.handleRouteChange(location)
})
this.handleRouteChange(this.history.getCurrentLocation())
}
// 处理路由变化
handleRouteChange(location) {
const route = this.match(location)
this.runBeforeEachHooks(route, () => {
this.currentRoute = route
this.runAfterEachHooks(route)
})
}
// 匹配路由
match(location) {
const matcher = new Matcher(this.routes)
return matcher.match(location)
}
// 导航到指定路由
push(location) {
this.history.push(location)
}
// 替换当前路由
replace(location) {
this.history.replace(location)
}
// 返回上一页
go(n) {
this.history.go(n)
}
// 添加全局前置守卫
beforeEach(hook) {
this.beforeEachHooks.push(hook)
}
// 添加全局后置钩子
afterEach(hook) {
this.afterEachHooks.push(hook)
}
// 运行全局前置守卫
runBeforeEachHooks(route, callback) {
let index = 0
const next = () => {
if (index >= this.beforeEachHooks.length) {
callback()
return
}
const hook = this.beforeEachHooks[index++]
hook(route, this.currentRoute, next)
}
next()
}
// 运行全局后置钩子
runAfterEachHooks(route) {
this.afterEachHooks.forEach(hook => {
hook(route, this.currentRoute)
})
}
}设计要点: 支持多种历史模式,支持导航守卫,支持路由匹配。
2. route.js - 路由记录
文件路径: src/route.js
核心功能: 存储路由配置,管理子路由,匹配路径。
关键代码:
javascript
// 路由记录
export class RouteRecord {
constructor(record) {
this.path = record.path
this.component = record.component
this.name = record.name
this.children = record.children || []
this.props = record.props
this.meta = record.meta || {}
this.beforeEnter = record.beforeEnter
}
// 添加子路由
addChild(record) {
this.children.push(new RouteRecord(record))
}
// 获取所有子路由
getChildren() {
return this.children
}
}
// 路由对象
export class Route {
constructor(options = {}) {
this.path = options.path || '/'
this.name = options.name
this.params = options.params || {}
this.query = options.query || {}
this.hash = options.hash || ''
this.matched = options.matched || []
this.meta = options.meta || {}
}
// 创建完整路径
get fullPath() {
let path = this.path
if (Object.keys(this.query).length > 0) {
const query = new URLSearchParams(this.query).toString()
path += '?' + query
}
if (this.hash) {
path += '#' + this.hash
}
return path
}
}设计要点: 简单的路由配置,支持嵌套路由,支持自定义属性。
3. history.js - 历史管理
文件路径: src/history.js
核心功能: 管理 URL 历史,监听 URL 变化,导航控制。
关键代码:
javascript
// Hash 历史管理
export class HashHistory {
constructor() {
window.addEventListener('hashchange', this.handleHashChange.bind(this))
}
// 获取当前位置
getCurrentLocation() {
return window.location.hash.slice(1) || '/'
}
// 导航到指定路径
push(path) {
window.location.hash = path
}
// 替换当前路径
replace(path) {
const url = new URL(window.location)
url.hash = path
window.history.replaceState(null, '', url)
}
// 前进或后退
go(n) {
window.history.go(n)
}
// 监听路由变化
listen(callback) {
this.callback = callback
}
// 处理 hash 变化
handleHashChange() {
if (this.callback) {
this.callback(this.getCurrentLocation())
}
}
}
// HTML5 历史管理
export class HTML5History {
constructor() {
window.addEventListener('popstate', this.handlePopState.bind(this))
}
// 获取当前位置
getCurrentLocation() {
return window.location.pathname
}
// 导航到指定路径
push(path) {
window.history.pushState(null, '', path)
}
// 替换当前路径
replace(path) {
window.history.replaceState(null, '', path)
}
// 前进或后退
go(n) {
window.history.go(n)
}
// 监听路由变化
listen(callback) {
this.callback = callback
}
// 处理 popstate 事件
handlePopState() {
if (this.callback) {
this.callback(this.getCurrentLocation())
}
}
}
// 创建历史管理器
export function createHistory(mode) {
switch (mode) {
case 'hash':
return new HashHistory()
case 'html5':
return new HTML5History()
default:
return new HashHistory()
}
}设计要点: 支持多种历史模式,监听 URL 变化,提供导航控制。
4. matcher.js - 路由匹配
文件路径: src/matcher.js
核心功能: 匹配路径,解析参数,构建路由对象。
关键代码:
javascript
// 路由匹配器
export class Matcher {
constructor(routes) {
this.routes = this.createRouteRecords(routes)
}
// 创建路由记录
createRouteRecords(routes, parent = null) {
return routes.map(route => {
const record = new RouteRecord({
...route,
parent
})
if (route.children) {
record.children = this.createRouteRecords(route.children, record)
}
return record
})
}
// 匹配路径
match(path) {
const matched = this.matchRoute(path, this.routes)
if (!matched) {
return null
}
return new Route({
path: matched.path,
params: matched.params,
matched: matched.records
})
}
// 匹配路由
matchRoute(path, routes) {
const segments = path.split('/').filter(Boolean)
let currentRoutes = routes
const matchedRecords = []
let matchedParams = {}
let matchedPath = ''
for (const segment of segments) {
let matched = null
for (const route of currentRoutes) {
const result = this.matchPathSegment(segment, route.path)
if (result) {
matched = { route, params: result.params }
break
}
}
if (!matched) {
return null
}
matchedRecords.push(matched.route)
Object.assign(matchedParams, matched.params)
matchedPath += '/' + segment
currentRoutes = matched.route.children || []
}
return {
path: matchedPath || '/',
params: matchedParams,
records: matchedRecords
}
}
// 匹配路径段
matchPathSegment(segment, routePath) {
// 精确匹配
if (segment === routePath) {
return { params: {} }
}
// 动态参数匹配
const paramMatch = routePath.match(/^:([^/]+)$/)
if (paramMatch) {
return { params: { [paramMatch[1]]: segment } }
}
return null
}
}设计要点: 支持动态参数,支持嵌套路由,精确的路径匹配。
关键设计决策
1. 分离历史管理
原因: 支持多种历史模式,易于扩展,职责分离。
实现:
javascript
class HashHistory { }
class HTML5History { }
function createHistory(mode) {
switch (mode) {
case 'hash': return new HashHistory()
case 'html5': return new HTML5History()
}
}2. 使用守卫系统
原因: 控制导航流程,支持权限验证,支持数据预取。
实现:
javascript
router.beforeEach((to, from, next) => {
// 守卫逻辑
next()
})3. 嵌套路由
原因: 支持复杂布局,支持组件嵌套,支持参数传递。
实现:
javascript
{
path: '/user',
component: User,
children: [
{
path: 'profile',
component: Profile
}
]
}测试策略
单元测试
javascript
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { Router } from '../src/router.js'
describe('Router 测试', () => {
it('应该创建路由器', () => {
const router = new Router({
routes: [
{ path: '/', component: Home }
]
})
assert.ok(router)
})
it('应该匹配路由', () => {
const router = new Router({
routes: [
{ path: '/', component: Home }
]
})
const route = router.match('/')
assert.ok(route)
})
})总结
Vue Router 的源代码体现了四个核心设计原则:模块化采用清晰的模块划分,便于维护和扩展;灵活性支持多种路由模式,满足不同场景需求;可扩展支持自定义路由和导航守卫,便于扩展功能;易于测试提供清晰的接口,便于单元测试和集成测试。理解源代码有助于更好地使用和优化 Vue Router。
参考资源
Vue Router 源码,Vue Router 官方文档,Vue.js 官方文档。

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