简介:本文通过手写实现一个简化版 Vue-Router,深入解析路由核心机制,包括路由注册、匹配、导航守卫及哈希/历史模式实现,帮助开发者理解前端路由工作原理并提升实战能力。
在 Vue.js 生态中,vue-router 作为官方路由库提供了完善的路由功能,但开发者往往停留在使用层面。手写一个简化版 Vue-Router 不仅能加深对前端路由原理的理解,还能提升解决复杂路由场景的能力。例如,当需要定制特殊路由逻辑或优化性能时,理解底层机制至关重要。
首先需要创建一个 Router 类,接收路由配置并初始化核心功能:
class VueRouter {constructor(options) {this.routes = options.routes || []this.routeMap = this.createRouteMap()this.currentPath = '/'}createRouteMap() {const routeMap = {}this.routes.forEach(route => {routeMap[route.path] = route.component})return routeMap}}
通过 Vue.use() 安装插件时,需要实现 install 方法并注入路由实例:
VueRouter.install = function(Vue) {Vue.mixin({beforeCreate() {if (this.$options.router) {Vue.prototype.$router = this.$options.router}}})}
实现路径到组件的映射,支持动态参数:
matchRoute(path) {for (const routePath in this.routeMap) {// 简单动态路由匹配示例if (routePath === path ||(routePath.includes(':') &&path.startsWith(routePath.split(':')[0]))) {return this.routeMap[routePath]}}return this.routeMap['*'] || null // 通配符路由}
监听 hashchange 事件实现路由切换:
initHashMode() {window.addEventListener('hashchange', () => {this.currentPath = window.location.hash.slice(1) || '/'this.updateView()})if (window.location.hash === '') {window.location.hash = '#/'}}updateView() {const component = this.matchRoute(this.currentPath)// 这里应通过 Vue 的响应式系统更新视图console.log('渲染组件:', component)}
使用 History API 实现更美观的 URL:
initHistoryMode() {window.addEventListener('popstate', () => {this.currentPath = window.location.pathnamethis.updateView()})}push(path) {history.pushState({}, '', path)this.currentPath = paththis.updateView()}
实现全局前置守卫:
constructor(options) {// ...原有代码this.beforeHooks = []}beforeEach(fn) {this.beforeHooks.push(fn)}async resolveHooks(to, from, next) {for (const hook of this.beforeHooks) {await hook(to, from, next)}next()}
支持运行时添加路由:
addRoutes(routes) {routes.forEach(route => {this.routeMap[route.path] = route.component})}
通过组件组合实现嵌套结构:
// 路由配置示例{path: '/user',component: UserLayout,children: [{ path: 'profile', component: Profile }]}
创建指令式视图渲染组件:
const RouterView = {render(h) {const component = this.$router.matchRoute(this.$router.currentPath)return h(component || { render: h => h('div', '404') })}}
创建导航链接组件:
Vue.component('router-link', {props: {to: String},render(h) {return h('a', {attrs: { href: `#${this.to}` },on: { click: this.handleClick }}, this.$slots.default)},methods: {handleClick(e) {e.preventDefault()this.$router.push(this.to)}}})
实现基于 Promise 的组件异步加载:
function lazyLoad(loader) {return () => loader().then(module => module.default)}// 使用示例{path: '/dashboard',component: lazyLoad(() => import('./views/Dashboard.vue'))}
使用 LRU 缓存优化频繁访问的路由:
class RouteCache {constructor(maxSize) {this.cache = new Map()this.maxSize = maxSize}get(key) {const val = this.cache.get(key)if (val) {this.cache.delete(key)this.cache.set(key, val)}return val}set(key, val) {this.cache.delete(key)this.cache.set(key, val)if (this.cache.size > this.maxSize) {this.cache.delete(this.cache.keys().next().value)}}}
class VueRouter {constructor(options) {this.mode = options.mode || 'hash'this.routes = options.routes || []this.routeMap = this.createRouteMap()this.currentPath = '/'this.initMode()this.initHooks()}createRouteMap() {// 实现同上}initMode() {if (this.mode === 'history') {this.initHistoryMode()} else {this.initHashMode()}}push(path) {if (this.mode === 'history') {history.pushState({}, '', path)} else {window.location.hash = path}this.currentPath = paththis.updateView()}// 其他方法实现...}// Vue 插件安装VueRouter.install = function(Vue) {Vue.mixin({beforeCreate() {if (this.$options.router) {Vue.prototype.$router = this.$options.router}}})Vue.component('router-view', RouterView)Vue.component('router-link', RouterLink)}
通过手写实现 Vue-Router,开发者不仅能深入理解路由工作原理,还能获得定制化路由方案的能力。这种实践对于解决复杂路由场景、优化应用性能具有重要价值。建议开发者在实际项目中先使用官方路由库,在掌握原理后再尝试定制化开发。