前言
早期在牛客上看到的一道题,下面是笔者的实现思路~
Vue-router的基本使用
js
// index.js
// 本例子为了简化,使用 hash 路由模式
import { createRouter,createWebHashHistory } from 'vuex'
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
js
// app.vue
<script setup>
</script>
<template>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view/>
</template>
<style>
</style>
js
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
const app = createApp(App)
app
.use(router)
.mount('#app')
我们就可以完成了一个简单的单页应用。
简单版本的实现
注册两个全局组件
js
// index.js
import { inject, ref } from 'vue'
import RouterLink from './RouterLink.vue'
import RouterView from './RouterView.vue'
// 定义一个常量,用于全局访问路由对象
const ROUTER_KEY = '__router__'
// 返回路由实例
const createRouter = (options) => {
return new Router(options)
}
// 使得全局可以获取路由对象
const useRouter = () => {
return inject(ROUTER_KEY)
}
// 返回 hash 模式下的路由历史记录对象
const createWebHashHistory = () => {
}
// 路由类工厂
class Router{
constructor(options) {
}
// 注册路由相关组件和提供全局路由对象
install(app) {
// 路由对象,提供给全局访问
app.provide(ROUTER_KEY, this)
// 注册全局组件
app.component('router-link', RouterLink)
app.component('router-view', RouterView)
}
}
// 导出模块需要的变量和方法
export {
useRouter,
createRouter, // 返回路由实例
createWebHashHistory // 返回hash 事件监听
}
js
// RouterLink.vue
<template>
<a :href="'#' + props.to">
<!-- 插槽 -->
<slot />
</a>
</template>
<script setup>
import { defineProps } from 'vue'
let props = defineProps({
to: {
type: String,
required: true
}
})
</script>
<style scoped>
</style>
js
// RouterView.vue
<template>
<!-- 动态渲染组件 -->
<component :is="component"></component>
</template>
<script setup>
// 引入 useRouter 和 computed
import { useRouter } from './index'
import { computed } from 'vue'
// 获取当前路由
const router = useRouter()
// 计算属性 component,根据当前路由找到对应的组件
const component = computed(() => {
const route = router.routes.find(
(item) => item.path === router.current.value
)
return route ? route.component : null
})
</script>
<style scoped>
</style>
RouterLink 组件实现的流程:
- 通过 props 组件通信的方式传递要重新指向的锚点
- 通过 a 标签来进行路由的转换
- 监听了 hashchange 事件,然后进行当前路由信息的更新
RouterView 组件实现的流程:
- 通过路由信息来获取当前路由的内容
- 调用 component 组件动态绑定来实现新组件内容的渲染
完善
js
import { inject, ref } from 'vue'
import RouterLink from './RouterLink.vue'
import RouterView from './RouterView.vue'
// 定义一个常量,用于全局访问路由对象
const ROUTER_KEY = '__router__'
// 返回路由实例
const createRouter = (options) => {
return new Router(options)
}
// 使得全局可以获取定义的路由对象
const useRouter = () => {
return inject(ROUTER_KEY)
}
// 返回 hash 模式下的路由历史记录对象
const createWebHashHistory = () => {
// 内部方法,用于绑定事件监听
function bindEvents(fn) {
window.addEventListener('hashchange', fn)
}
// 返回 hash 值和事件监听的方法
return {
bindEvents,
url: window.location.hash.slice(1)
}
}
// 路由类工厂
class Router{
constructor(options) {
this.history = options.history // 路由历史记录对象
this.routes = options.routes // 路由的配置数组
this.current = ref(this.history.url) // 响应式的当前路由,自动更新
// 给路由历史记录对象绑定事件监听器,当 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)
}
}
// 导出模块需要的变量和方法
export {
useRouter,
createRouter, // 返回路由实例
createWebHashHistory // 返回 hash 事件监听
}