在之前呢,我写了一篇有关于手写实现hash router 的文章手写Hash Router实战—我的学习心得分享 ,在上面这篇文章中,我只是使用html+js以及浏览器的api大概模拟了一下router-link
的实现,那么这篇文章我将实现vue-hashrouter的基本功能,包括router-link 和 router-vue以及useRoute的基本实现
废话不多说,开始今天的主题
了解vue-router
首先我们先来解析一下官方的router
,首先是先安装一个npm i vue-router
依赖,然后在router文件夹下的index.js中使用createRouter
创建路由,以及创建工厂函数createWebHashHistory
。
配置好路由后,在入口文件main.js
挂载--app.use(router)
。我们先来搞清楚这几个函数的作用,才能更好的理解路由的实现。为此我翻阅了vue-router的官方文档
-
createRouter 函数 在官方 Vue Router 中,createRouter 函数负责创建一个包含所有路由信息和配置的实例。为了简化,我们的 createRouter 将创建一个 Router 类的实例,并传入配置。
-
createWebHashHistory 函数 这个函数用于创建一个 hash 模式的 history 对象,它会监听浏览器地址栏中的 hash 变化,并在变化时更新路由。
-
app.use(router)
简单来说就是在 Vue 中,app.use 是用来安装 Vue 插件的。对于 Vue Router,这会注册全局的 router-link 和 router-view 组件,并设置一些全局的响应式数据,如当前路由。
Router实现
了解完后,我们来手写实现路由,首先我们先初始化路由和配置
- 首先,我们需要创建一个 createRouter 函数,用于初始化路由实例。
import { ref } from 'vue';
import RouterLink from './RouterLink.vue';
import RouterView from './RouterView.vue';
export const createRouter = (options) => {
return new Router(options);
};
- 接下来,使用浏览器api
hashchange
实现 createWebHashHistory 函数,用于监听 hash 变化。
export const createWebHashHistory = () => {
function bindEvents(fn) {
window.addEventListener('hashchange', fn);
}
return {
url: window.location.hash.slice(1) || '/',
bindEvents
};
};
bindEvents
是一个内部函数,其作用是绑定一个事件监听器到浏览器的 hashchange
事件上。这个事件会在浏览器地址栏中的 hash
部分发生变化时触发。fn
参数是将要执行的回调函数,每当 hashchange
事件发生时,这个函数会被调用。
return {
url: window.location.hash.slice(1) || '/',
bindEvents
};
};
函数 createWebHashHistory
返回一个对象,该对象包含两个属性:
-
url
:表示当前页面hash
部分的值。如果window.location.hash
的值以#
开头,slice(1)
方法会移除这个#
字符。如果没有hash
,则默认值为'/'
。 -
bindEvents
:这是上面定义的bindEvents
函数,它用于绑定hashchange
事件。
createWebHashHistory
返回一个配置对象,这个对象可以被用作 Vue Router 的 history
配置选项。当 Vue Router 使用这个对象时,它会监听 hashchange
事件,并使用 url
属性来跟踪当前的 hash
路由。这样,Vue Router 可以在 hash
改变时更新应用的状态和视图。
- 实现 Router 类 Router 类是整个路由系统的核心,负责管理路由状态和执行路由守卫。
class Router {
constructor(options) {
this.history = options.history;
this.routes = options.routes;
this.current = ref(this.history.url);
this.history.bindEvents(() => {
this.current.value = window.location.hash.slice(1);
});
}
install(app) {
app.provide(ROUTER_KEY, this);
app.component('router-link', RouterLink);
app.component('router-view', RouterView);
}
}
- constructor(options): 构造函数接收一个配置对象,这个对象包含 history 和 routes 属性。history 是一个对象,包含当前 URL 的信息以及绑定事件的方法。routes 是一个数组,包含所有的路由配置信息。
- this.current: 这是一个响应式的引用,它的值是当前路由的路径。当 URL 的 hash 部分发生变化时,这个值也会更新,从而触发 Vue 的响应式更新。
- this.history.bindEvents(...): 这个方法用于监听浏览器地址栏中的 hash 变化。当 hash 变化时,回调函数会被调用,this.current 的值会被更新为新的 hash 值。
- install(app): 这个方法用于将路由器实例安装到 Vue 应用中。它通过 app.provide 方法将路由器实例注入到应用上下文中,使得在任何组件中都可以通过 inject 方法来访问这个实例。同时,它还注册了全局的 router-link 和 router-view 组件。
最后我创建一个useRoute 钩子函数,
const ROUTER_KEY = '__router__'
// use 开头的是一派 hooks 函数式编程
export const useRoute = () => {
return inject(ROUTER_KEY)
}
这就是路由的实现,接下来我们来实现一下router-link
组件和router-view
组件
实现 router-link 和 router-view
- RouterLink.vue
使用响应式路径 把使用组件时的to属性传进来使用,而 允许父组件传递任意内容到 RouterLink 组件内,通常是链接文本。
<template>
<a :href="'#' + props.to">
<slot></slot>
</a>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
to: {
type: String,
required: true
}
});
</script>
- RouterView.vue
我在这里使用了<component>
标签,它是一个特殊的 Vue 组件,它可以动态地渲染任何组件。:is 绑定决定了要渲染哪个具体组件。我使用了自定义的useRoute
把路径信息获取下来,然后使用计算属性查找与当前路由路径匹配的路由配置,并返回相应的组件。如果没有找到匹配项,则返回 null,<component>
渲染为空。
<template>
<component :is="component" />
</template>
<script setup>
import { computed } from 'vue';
import { useRoute } from './index';
import Home from '../../pages/Home.vue';
const router = useRoute();
const component = computed(() => {
const route = router.routes.find(route => route.path === router.current.value);
return route ? route.component : null;
});
</script>
使用
那么vue-router就已经封装好了,在router文件夹下的index.js文件中导入上述的createRouter方法和createWebHashHistory方法就可以开始使用了
我写好了index.js如下
import { createRouter,createWebHashHistory } from './grouter/index'
import Home from '../pages/Home.vue'
import About from '../pages/About.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/About',
name: 'about',
component: About
}
]
})
export default router
然后我们在main.js中导入 且挂载
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index.js'
const app = createApp(App)
// vue 方法做了什么事
// vue 只负责 组件思想,mvvm ?响应式 等核心,
// 其他的交给生态系统 一起开源 vue-router 是vue生态中的路由模块
// vue 和生态的对接呢? 就是这个use 方法
app.use(router)
app.mount('#app')
这是我的App.vue
<template>
<nav>
<router-link to="/">首页</router-link>
<router-link to="/About">About</router-link>
</nav>
<main>
<router-view></router-view>
</main>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
创建好Home.vue和About.vue,就完成了
实现效果
总结
在本文中,我深入探讨并亲手构建了一个简化的 Vue Hash Router,实现了路由的基本功能,包括 router-link 和 router-view 组件的搭建,以及 useRoute 的实用钩子函数。
从创建 createRouter 函数开始,我们逐步建立起路由实例,并通过 createWebHashHistory 监听浏览器的 hashchange 事件,确保了 URL 的变化能够被捕捉并及时更新应用状态。Router 类作为整个路由系统的中枢,管理着路由状态和事件,而 useRoute 提供了访问路由实例的便捷途径。
在 router-link 和 router-view 组件的设计中,我们运用了 Vue 的响应式系统和动态组件功能,实现了导航链接和视图切换的自动化。
感谢阅读,期待你在实际项目中应用和扩展这些知识!