likes
comments
collection
share

百度面试官:Vue-router源码看过吗?看过的话讲一下你会怎么实现

作者站长头像
站长
· 阅读数 9

前言

早期在牛客上看到的一道题,下面是笔者的实现思路~

Vue-router的基本使用


// 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
// app.vue
<script setup>

</script>
<template>

<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view/>

</template>
<style> 

</style>
// 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')

我们就可以完成了一个简单的单页应用。

简单版本的实现

注册两个全局组件


// 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 事件监听
}
// 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>
// 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 组件动态绑定来实现新组件内容的渲染

完善

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 事件监听
}