准备工作
使用 pnpm创建一个vue项目
pnpm create vite
.pnpm-store/v3/tmp/dlx-3305 | +1 +
.pnpm-store/v3/tmp/dlx-3305 | Progress: resolved 1, reused 0, downloaded 1, added 1, done
✔ Project name: … mini-vue-router
✔ Select a framework: › Vue
✔ Select a variant: › TypeScript
Scaffolding project in /Users/x/Desktop/workspace/mini-vue-router...
Done. Now run:
cd mini-vue-router
pnpm install
pnpm run dev
新建两个vue
文件作为我们的页面组件
<template>
<div>
this is about
</div>
</template>
<script setup lang="ts">
</script>
<template>
<div>
this is home
</div>
</template>
<script setup lang="ts">
</script>
实现
新建src/router/index.ts
文件,先定义出几个常用的api
导出
const useRouter = () => {
}
const useRoute = () => {
}
const createRouter = () => {
}
const createWebHashHistory = () => {
}
export { useRouter, useRoute, createRouter, createWebHashHistory }
首先,我们使用vue-router
时会先使用createRouter
创建一个router
,然后使用Vue.use(router)
,先实现这部分
class Router {
constructor() {}
install(app) {
// 注册router
// 注册router-view与router-link组件
}
}
注册完成后,vue-router可以在任何地方使用,所以可以使用provide
注入router
,每次获取时使用inject
获取
import { inject } from 'vue'
if (!inject) {
console.error('inject is not defined, please check vue version')
}
const ROUTER_KEY = Symbol('router')
const useRouter = () => {
return inject(ROUTER_KEY)
}
class Router {
constructor(options) {
}
install(app) {
// 注册router
// 注册router-view与router-link组件
// 注入router
app.provide(ROUTER_KEY, this)
}
}
然后是 createRouter
, createRouter
接收options
,并返回router
实例
const createRouter = (options) => {
return new Router(options)
}
继续完善,options
中我们常用的配置有routes
、history
routes
是路由描述的数组,history
是路由模式: 通过使用createWebHashHistory(hash模式)
、createWebHistory(history模式)
这里先接收这两个参数
然后完善createWebHashHistory
函数,这是创建hash
模式的API
,我们先返回一个hashchange
的监听:
const createWebHashHistory = () => {
function historyListeners(cb) {
window.addEventListener('hashchange', cb)
}
return {
historyListeners
}
}
这样在Router
类的constructor
中就可以实时获取到更新后的url
, 这个url
使用ref
存起来
class Router {
constructor(options) {
this.history = options.history
this.routes = options.routes
// 当前的url
const currentUrl = window.location.hash.slice(1) || '/'
this.currentUrl = ref(currentUrl)
// 监听后更新url
this.history.historyListeners(() => {
this.currentUrl.value = window.location.hash.slice(1);
})
}
history: any
routes: any[]
currentUrl: Ref<string>
install(app) {
// 注册router
// 注册router-view与router-link组件
// 注入router
app.provide(ROUTER_KEY, this)
}
}
/router
目录中新建router.ts
,引入我们写的api
使用:
import { createRouter, createWebHashHistory } from './index'
import Home from "../components/Home.vue";
import About from "../components/About.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
component: About,
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
在main.ts
中注册:
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/router'
createApp(App).use(router).mount('#app')
然后我们来实现router-link
和router-view
router-link
:
<template>
<a :href="'#' + props.to">
<slot></slot>
</a>
</template>
<script setup lang="ts">
import { defineProps } from "vue";
// 定义组件的 props
const props = defineProps({
to: {
type: String,
required: true
},
});
</script>
router-view
:
<template>
<component v-if="component" :is="component"></component>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useRouter } from "./index";
let router = useRouter();
const component = computed(() => {
const route = router.routes.find(
(route) => route.path === router.currentUrl.value
);
return route?.component;
});
</script>
这里先简单实现,不考虑各种嵌套路由子路由的情况。
在router/index
中全局注册这两个组件
import { App, inject, ref, Ref } from 'vue'
import RouterView from './RouterView.vue' ~!这里 ++
import RouterLink from './RouterLink.vue' ~!这里 ++
if (!inject) {
console.error('inject is not defined, please check vue version')
}
const ROUTER_KEY = Symbol('router')
const useRouter = () => {
return inject<Router>(ROUTER_KEY)!
}
const useRoute = () => {
}
const createRouter = (options: any) => {
return new Router(options)
}
const createWebHashHistory = () => {
function historyListeners(cb: any) {
window.addEventListener('hashchange', cb)
}
return {
historyListeners
}
}
type RouterOptions = {
history: any,
routes: any[]
}
class Router {
constructor(options: RouterOptions) {
this.history = options.history
this.routes = options.routes
// 当前的url
const currentUrl = window.location.hash.slice(1) || '/'
this.currentUrl = ref(currentUrl)
// 监听后更新url
this.history.historyListeners(() => {
this.currentUrl.value = window.location.hash.slice(1);
})
}
history: any
routes: any[]
currentUrl: Ref<string>
install(app: App) {
// 注册router
// 注册router-view与router-link组件
// 注入router
app.provide(ROUTER_KEY, this)
app.component('router-view', RouterView) ~!这里 ++
app.component('router-link', RouterLink) ~!这里 ++
}
}
export { useRouter, useRoute, createRouter, createWebHashHistory }
注册后在helloword
组件中使用:
可以正常使用。