手把手教你实现一个简单的vue-router

242 阅读4分钟

前言

众所周知,vue实现的是单页应用,整个项目只有一个html文件,其他的都是组件,页面的跳转实际上就是各个组件的来回切换,一般都是用vue-router来实现页面跳转,接下来就带大家去剖析vue-router的原理,并且实现一个简单的vue-router

思路

import { createRouter, createWebHashHistory } from './myRouter'
import home from '../views/Home.vue'
import about from '../views/About.vue'


const routes = [
    {
        path: '/',
        name: 'name',
        component: home
    },
    {
        path: '/about',
        name: 'about',
        component: about
    }
]

const router = createRouter({
    history: createWebHashHistory(),    // history模式,hash模式
    routes
})

export default router

根据原版的来看,最终实现的效果就是打造两个函数,createRoutercreateWebHashHistorycreateRouter接受一个对象作为参数,对象里面包含两个属性,一个是路由的模式history,也就是createWebHashHistory,另一个就是路由的配置信息,结果是返回一个路由的实例对象。

createWebHashHistory

这里我们先写createWebHashHistory方法,因为这个函数的返回结果是作为createRouter参数的属性值要写入进去的,所以就先实现这个路由模式的功能。我们在里面声明了一个函数bindEvents,用来监听哈希值的变化,如果浏览器的哈希值发生变更就会执行回调函数fn,然后将它抛出,到时候给createRouter用,同时我们还用了window.location.hash.slice(1)方法把url地址一起抛出给createRouter使用。

function createWebHashHistory() {
    function bindEvents(fn) {
        window.addEventListener('hashchange', fn)
    }
    return {
        bindEvents,
        // 浏览器自带的获取哈希地址的方法
        url: window.location.hash.slice(1) || '/'
    }
}

RouterLink和RouterView

我们使用router进行路由跳转需要使用到RouterLinkRouterView,现在我们用的是我们自己打造的router,所以RouterLinkRouterView也要我们自己打造。

RouterLink实际上就是a标签,所以我们用a标签来打造,里面有一个插槽,<RouterLink>标签里面包裹的内容就是放在插槽里的

<template>
    <a :href="'#'+to">
        <slot></slot>
    </a>
</template>

<script setup>
defineProps({
    to: {
        type: String,
        // 表示该属性为必传项,否则就会报错
        required: true
    }
})
</script>

RouterView作为路由的入口,它的核心是用component标签打造的,component标签其实就是组件的意思,动态绑定is属性,component代表的url是谁,就显示对应的组件,这就是路由跳转的原理,至于componentcompouted的那些操作,看一下router的打印结果就知道那些操作的作用是什么了。

image.png

<template>
    <!-- 就是一个组件的意思,vue自带的 -->
    <component :is="component"></component>
</template>

<script setup>
import { computed } from 'vue';
import { useRouter } from '../myRouter/index'

const router = useRouter()  // 在当前组件注入了router
const component = computed(() => {
    // 如果当前的url是'/',就返回Home.vue
    const route = router.routes.find((route) => {
        return route.path === router.current.value
    })
    return route ? route.component : null
})
</script>

<style lang="scss" scoped>

</style>

createRouter

我们在使用vue-router的时候都要去main.js文件里use掉router,而vue的要求很严苛,我们想要让我们自己写的router被vue给use掉,就需要使用install方法,这是vue内定的,所有能够被vue给use掉的第三方插件,里面都需要有install方法,它自带一个参数app,是vue的上下文对象。我们使用它的provide方法把自己写的Router对象提供给它,这样就可以被vue给use掉了,provide规定要接受key,value作为参数,所以我们使用ROUTER_KEY作为标记。app.component的作用是全局注册RouterLinkRouterView这两个组件,这样它们就可以直接使用了,就不需要在各个地方用import引入。

import RouterLink from './RouterLink.vue'
import RouterView from './RouterView.vue'
import { inject,ref } from 'vue'

function createRouter(options) {
    return new Router(options)
}

const ROUTER_KEY = '_router_'

function useRouter() {
    return inject(ROUTER_KEY)
}

class Router {
    constructor(options) {
        this.history = options.history
        this.routes = options.routes
        // 响应式的变量,当history.url发生变化时,current就会发生变化,否则current就只执行一次
        this.current = ref(this.history.url)   //当前的路径

        this.history.bindEvents(() => {
            // 当页面的哈希值发生变更时,current里的值就会实时变更
            this.current.value = window.location.hash.slice(1)
        })
    }
    // 想被vue给use掉就必须要有install方法
    install(app) {
        // app是vue的上下文对象
        console.log(app);
        app.provide(ROUTER_KEY,this)
        // 注册全局组件router-link
        app.component('router-link', RouterLink)
        app.component('router-view', RouterView)
    }
}

这里我们还用到了vue自带的一个叫inject方法,它的作用是注入,一般我们在其他组件里使用useRouter实际上就是把整个router的实例对象搬过来用,而使用inject方法就可以实现,useRouter的结果就是返回inject注入的对象,这样我们在其他组件中调用useRouter就可以得到router的实例对象了。

结语

到这里一个简单的vue-router就打造好了,看上去是不是也没有想象中的那么复杂吧,vue由于是单页应用,所以它的核心就是组件,路由跳转实际上就是组件之间的切换,把这个理解了,相信你对vue会有更深刻的认识。

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!