简易版 Vue-Router 实现思路
1. 规划路由器核心功能
路由注册与初始化
- 在
grouter/index.js文件中,定义了Router类与createRouter函数来创建和初始化路由实例。此实例包含路由表(routes数组)以及历史管理对象(通过createWebHashHistory()函数生成)。 - 路由表
routes设置了路径和对应的组件,使得打开特定URL时可以加载相应的组件。
路由响应与视图更新
RouterView组件在路由变化时,动态地根据当前路由渲染相应组件。这通过计算属性component实现,它读取当前路由和匹配的组件。- 路由切换触发时,依赖于
current响应式变量的变化,该变量代表当前激活的路由URL。
History管理
- 实现了基于hash的路由(
createWebHashHistory),封装了URL变化的监听和更新逻辑。它通过bindEvents方法绑定浏览器的hashchange事件,使得每次URL改变都会更新Router的当前路由状态。
2. 设计组件接口
RouterLink
- 实现了路由链接组件,使用户能够通过点击导航到指定的路由。组件渲染为
<a>标签,其href属性预设为#加上目标路由的路径。
RouterView
- 动态渲染当前路由对应的组件。根据
Router实例的当前路由状态从路由表中查找并渲染相应的Vue组件。
3. 开发History管理器
createWebHashHistory函数封装了浏览器历史API的使用方式,专门针对hash形式的URL。提供了方法来绑定事件监听器,响应URL的hash部分的变化,并通告给路由系统。
4. 实现Router类
- 此类构造时接收一个包含
history和routes的选项对象。通过bindEvents绑定的方法监听URL的变化,并更新当前路由状态。 - 提供
install方法,使其能够作为Vue插件安装,注册RouterLink和RouterView全局组件,并在Vue实例中提供路由器实例。
5. 集成到Vue中
- 通过
app.use(router)将路由器集成到Vue应用中,这一步是在main.js文件中完成的。安装路由器时,Router的install方法被调用,完成Vue组件的全局注册和提供路由实例。
简易版 Vue-Router 源码
router/grouter/index.js
import RouterLink from "./RouterLink.vue"
import RouterView from "./RouterView.vue"
import { ref,inject } from 'vue'
const ROUTER_KEY = '__router__'
// 在任何地方,就可以拿到 router 对象(简洁方便)
const useRouter = () => {
return inject(ROUTER_KEY)
}
// 封装 VueRouter
const createRouter = (options) => {
return new Router(options)
}
// 返回一个 hash 路由对象
// url #/about
// hashChange
const createWebHashHistory = () => {
function bindEvents(fn) {
window.addEventListener('hashchange', fn)
}
return {
url: window.location.hash.slice(1) || '/',
bindEvents
}
}
class Router {
constructor(options) {
this.history = options.history
this.routes = options.routes
console.log(options, '////');
// 当前的 url 状态,是 router-view component 计算属性的依赖
this.current = ref(this.history.url)
this.history.bindEvents(() => {
// this 指向 Router
this.current.value = window.location.hash.slice(1)
console.log(this.current.value);
})
}
install(app) {
console.log(app);
// console.log('vue 要对接 vue-router');
// 全局组件的声明
// 全局提供 router对象
app.provide(ROUTER_KEY,this)
app.component('router-link', RouterLink)
app.component('router-view', RouterView)
}
}
export {
createRouter,
createWebHashHistory,
useRouter
}
RouterLink.vue 页面
<template>
<a :href="'#' + props.to">
<slot></slot>
</a>
</template>
<script setup>
import { defineProps } from 'vue';
let props = defineProps({
to: {
type: String,
required: true // 这个参数是必须要传递的
}
})
</script>
RouterView.vue 页面
<template>
<component :is="component"></component>
</template>
<script setup>
import { computed, } from 'vue';
import { useRouter } from './index';
// 怎么拿到的 component?
// router -> routes / -> component:About
let router = useRouter()
console.log(router,'?????????');
// 这里的 component 是动态的,响应式的,用计算属性实现的
const component = computed(() => {
const route = router.routes.find(
(route) => route.path === router.current.value
)
return route ? route.component : null
})
</script>
grouter/index.js 页面
import { createRouter,createWebHashHistory } from "./grouter/index";
import Home from '../views/Home.vue'
import About from '../views/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.js 页面
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
// vue 是一个非常优秀的框架,只做自己 mvvm 等一些功能
// 生态 vue-router 规定install方法
const app = createApp(App)
app
.use(router)
.mount('#app')
App.vue 页面
<script setup>
</script>
<template>
<router-link to="/">首页</router-link>
<router-link to="/about">About</router-link>
<router-view />
</template>
<style scoped></style>
Home.vue 页面
<template>
<div>
home
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
About.vue 页面
<template>
<div>
about
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>