当前位置: 首页 > web >正文

Vue-Router简版手写实现

1. 路由库工程设计

首先,我们需要创建几个核心文件来组织我们的路由库:

src/router/index.tsRouterView.tsRouterLink.tsuseRouter.tsinjectionsymbols.tshistory.ts

2. injectionSymbols.ts

定义一些注入符号来在应用中共享状态:

import { inject, provide, InjectionKey, Ref } from 'vue';export interface Router {currentRoute: Ref<Route>;push: (to: string) => void;replace: (to: string) => void;resolve: (to: string) => string;
}export interface Route {path: string;component: any;
}export const routerKey: InjectionKey<Router> = Symbol('router');
export const matchedRouteKey: InjectionKey<Ref<Route>> = Symbol('matchedRoute');
export const viewDepthKey: InjectionKey<number> = Symbol('viewDepth');export function provideRouter(router: Router) {provide(routerKey, router);
}export function useRouter(): Router {return inject(routerKey);
}export function useMatchedRoute(): Ref<Route> {return inject(matchedRouteKey);
}export function provideMatchedRoute(route: Ref<Route>) {provide(matchedRouteKey, route);
}export function useViewDepth(): number {return inject(viewDepthKey, 0);
}export function provideViewDepth(depth: number) {provide(viewDepthKey, depth);
}

3. RouterView.ts

实现RouterView组件:

import { defineComponent, h, computed } from 'vue';
import { useMatchedRoute, useViewController, provideViewController } from './injectionsymbols';export const RouterView = defineComponent({name: 'RouterView',setup() {const depth = useViewController();const matchedRoute = useMatchedRoute();const route = computed(() => matchedRoute.value[depth]);provideViewController(depth + 1);return () => {const Component = route.value && route.value.component;return Component ? h(Component) : null;};}
});

4. RouterLink.ts

实现RouterLink组件:

import { defineComponent, h } from 'vue';
import { useRouter } from './injectionsymbols';export const RouterLink = defineComponent({name: 'RouterLink',props: {to: {type: [String, Object],required: true},replace: Boolean},setup(props, { slots }) {const router = useRouter();const navigate = (event: Event) => {event.preventDefault();if (props.replace) {router.replace(props.to as string);} else {router.push(props.to as string);}};return () => {return h('a', {href: router.resolve(props.to as string),onClick: navigate}, slots.default ? slots.default() : '');};}
});

5. useRouter.ts

实现useRouter函数:

import { inject } from 'vue';
import { routerKey, Router } from './injectionSymbols';export function useRouter(): Router {return inject(routerKey);
}

6. index.ts

实现createRouter函数:

import { ref, reactive, watch, Ref } from 'vue';
import { provideRouter, provideMatchedRoute, Route, Router } from './injectionsymbols';export function createRouter({ history, routes }: { history: any, routes: Route[] }): Router {const currentRoute: Ref<Route> = ref({ path: '/', component: null });function createMatcher(routes: Route[]) {const matchers = routes.map(route => ({...route,regex: new RegExp(`^${route.path}$`)}));return (path: string) => matchers.find(route => route.regex.test(path));}const matcher = createMatcher(routes);function push(to: string) {const route = matcher(to);if (route) {currentRoute.value = route;history.push(to);}}function replace(to: string) {const route = matcher(to);if (route) {currentRoute.value = route;history.replace(to);}}const router: Router = {currentRoute,push,replace,resolve: (to: string) => to};watch(currentRoute, (route) => {provideMatchedRoute(ref(route));});provideRouter(router);return router;
}

7. history.ts

实现createWebHistory函数:

export function createWebHistory() {const listeners: ((path: string) => void)[] = [];window.addEventListener('popstate', () => {listeners.forEach(listener => listener(window.location.pathname));});function push(path: string) {window.history.pushState({}, '', path);listeners.forEach(listener => listener(path));}function replace(path: string) {window.history.replaceState({}, '', path);listeners.forEach(listener => listener(path));}function listen(callback: (path: string) => void) {listeners.push(callback);}return {push,replace,listen};
}

8. 示例应用

最后,我们可以在一个简单的 Vue 应用中使用我们的自定义路由库:

// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { createRouter } from './router';
import { createWebHistory } from './router/history';const routes = [{ path: '/', component: () => import('./components/Home.vue') },{ path: '/about', component: () => import('./components/About.vue') }
];const history = createWebHistory();
const router = createRouter({ history, routes });createApp(App).use(router).mount('#app');

以下是在App.vue中

// App.vue
<template><div id="app"><router-link to="/">Home</router-link><router-link to="/about">About</router-link><router-view></router-view></div>
</template><script lang="ts">
import { defineComponent } from 'vue';export default defineComponent({name: 'App'
});
</script>

这个简化版的 Vue Router 库的 TypeScript 版本包含了核心组件 RouterView、RouterLink 以及核心 API createRouter 和 useRouter,实现了基本的路由功能。通过这个实现,你可以了解 Vue Router 的基本工作原理和核心概念。

9. 补充资料

vue-router 官方文档:https://router.vuejs.org/zh/introduction.html

vue-router 相关 api 速查:https://router.vuejs.org/zh/api/

源码:https://github.com/vuejs/router

浏览器历史记录协议:https://developer.mozilla.org/en-US/docs/Web/API/History_API

路由库实现:https://github.com/vuejs/router/blob/v4.3.3/packages/router/src/history/html5.ts

http://www.xdnf.cn/news/10268.html

相关文章:

  • 2025年5月个人工作生活总结
  • lstm 长短期记忆 视频截图 kaggle示例
  • Rock9.x(Linux)安装Redis7
  • 寒假学习笔记【匠心制作,图文并茂】——1.20拓扑、强连通分量、缩点
  • CppCon 2014 学习: The Implementation of Value Types
  • Compose原理 - 整体架构与主流程
  • JDK21深度解密 Day 8:Spring Boot 3与虚拟线程整合
  • 【清晰教程】查看和修改Git配置情况
  • SCSS 全面深度解析
  • neo4j 5.19.0安装、apoc csv导入导出 及相关问题处理
  • Windows最快速打开各项系统设置大全
  • RAID磁盘阵列配置
  • 鸿蒙编译ffmpeg库
  • M4Pro安装ELK(ElasticSearch+LogStash+Kibana)踩坑记录
  • 性能优化 - 理论篇:性能优化的七类技术手段
  • SMT贴片机工艺优化与效率提升策略
  • WEB3——为什么做NFT铸造平台?
  • 配置远程无密登陆ubuntu服务器时无法连接问题排查
  • 系统是win11+两个ubuntu,ubuntu20.04和ubuntu22.04,想删除ubuntu20.04且不用保留数据
  • 【图像处理入门】3. 几何变换基础:从平移旋转到插值魔法
  • day15 leetcode-hot100-29(链表8)
  • KWIC—Implicit Invocation
  • Redis实战-基于redis和lua脚本实现分布式锁以及Redission源码解析【万字长文】
  • 【android bluetooth 案例分析 04】【Carplay 详解 2】【Carplay 连接之手机主动连车机】
  • 【android bluetooth 案例分析 04】【Carplay 详解 3】【Carplay 连接之车机主动连手机】
  • K 值选对,准确率翻倍:KNN 算法调参的黄金法则
  • 当前用户的Git本地配置情况:git config --local --list
  • Python Day38 学习
  • 2025山东CCPC题解
  • Fragment事务commit与commitNow区别