前言
众所周知,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
根据原版的来看,最终实现的效果就是打造两个函数,createRouter
和 createWebHashHistory
,createRouter
接受一个对象作为参数,对象里面包含两个属性,一个是路由的模式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进行路由跳转需要使用到RouterLink
和RouterView
,现在我们用的是我们自己打造的router,所以RouterLink
和RouterView
也要我们自己打造。
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是谁,就显示对应的组件,这就是路由跳转的原理,至于component
里compouted
的那些操作,看一下router的打印结果就知道那些操作的作用是什么了。
<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
的作用是全局注册RouterLink
和RouterView
这两个组件,这样它们就可以直接使用了,就不需要在各个地方用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,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!