尝试来手写一个vue-router 小白也能看懂前端路由的底层逻辑

前言

在之前Vue的实践中,我们已经有了使用前端路由种种特性的经验,比如通过路由实现单页应用的开发、通过路由实现URL与组件的映射关系,通过路由传值实现组件通讯.etc,那么用了这么久的vue-router究竟是怎么开发的呢,今天我们就像自己尝试着写一个vue-router!

草船借箭 举一反三

Vue Router是Vue.js框架的官方路由管理器,它提供了灵活的导航控制、路由嵌套和命名视图等功能。

那么今天我们就可以通过一个简单的小Demo来演示一下vue-router的使用以及我们今天要手写实现的功能。

1. 初始化一个vite+vue项目

这里我们有多种方式来初始化一个vite+vue项目,具体可以根据官方文档来选择自己喜欢的方式。

vite脚手架与vue cli脚手架不同之处我在之前的文章中也有提到过,其中一点就是vite不会帮我们自动安装vue-router等依赖,所以下一步我们还需要手动安装一下vue-router的依赖

2. 安装 vue-router 依赖

同样,我们可以使用不同的包管理工具来安装

npm 复制代码
npm install vue-router@4
yarn 复制代码
yarn add vue-router@4
pnpm 复制代码
pnpm add vue-router@4

3. 基本页面

今天这个Demo很简单,主要就是三个页面 App.vue、About.vue、Home.vue

html 复制代码
<!-- App.vue -->
<template>
  <div class="nav">
    <router-link to="/home">去Home</router-link> |
    <router-link to="/about">去About</router-link>
  </div>
  <router-view />
</template>
html 复制代码
<!-- views/About.vue -->
<template>
    <div>
        This is About
    </div>
</template>
html 复制代码
<!-- views/Home.vue -->
<template>
    <div>
        This is Home
    </div>
</template>

4. 配置路由

首先我们安装好了vue-router之后,需要在src目录下新建一个router文件夹,并且在其之下新建一个index.js文件用于配置路由。

js 复制代码
// src/router/index.js
import { createRouter,createWebHashHistory } from 'vue-router'

import Home from '../views/Home.vue'
import About from '../views/About.vue'

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

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

export default router

在main.js文件中我们引入配置好的路由并且在全局挂载点上use掉使其生效

js 复制代码
// src/main.js文件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app
    .use(router)
    .mount('#app')

4. 使用vue-router

到这里,我们的路由配置过程就完成了,接下来我们就可以通过vue-router实现下面这个功能

手写Vue-router

在上面这个例子里面,我们回顾巩固了一下vue-router的使用,首先,我们选择使用的是hash路由模式,其与普通路由的区别就是url后会接一个'#',这个符号虽然看起来不美观,但是官方在打造它的时候为其赋予了一些独有的功能,比如url为#或#后面直接接字符串的话页面并不会发生跳转等等。

今天我们选择使用手写哈希路由,原因就是其相对于普通路由手写起来更加简单一点,为了方便理解其原理,我们就来看看如何手写一个哈希路由实现上面的Demo相同的功能。

创建RouterLink.vue

首先,我们创建一个 RouterLink.vue 组件,用于生成带有链接的文本。该组件的模板如下:

html 复制代码
<template>
  <a :href="'#'+to">
    <slot />
  </a>
</template>

<script setup>
defineProps({
  to: {
    type: String,
    required: true
  }
})
</script>
  1. <template> 部分:

    • <a :href="'#'+to">: a标签内通过动态绑定 href 属性,将其设置为 '#' + to,这样可以通过点击链接时改变url的哈希部分来触发路由变化。

    • <slot />: 插槽------这个标签允许在组件标签之间插入内容,使得包裹在这个组件中的内容成为链接的文本内容。

  2. <script setup> 部分:

    • defineProps: 这是 Vue 3 中的一个新特性,用于定义组件的 props。在这里,我们使用 defineProps 定义了一个名为 to 的 prop,它的类型是字符串,而且是必需的,即必须传递这个 prop。

    • to: { type: String, required: true }: 这是通过 props 传递进来的路由路径参数,其中 to prop 的类型为字符串,且设置它为必需。

一言蔽之,这个组件接受一个 to 属性,将其拼接到链接的 href 中。通过使用插槽(slot),然后就可以让组件能够包含在链接中的任何文本。

创建自定义Router

在使用vue-router中,我们需要对路由进行注册以及一些功能的引入,那么现在,我们来创建我们自定义的路由器 myRouter

js 复制代码
// myRouter/index.js
import { inject, ref } from 'vue';

const ROUTER_KEY = '_router_';

function useRouter() {
  return inject(ROUTER_KEY);
}

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

function createWebHashHistory() {
  function bindEvents(fn) {
    window.addEventListener('hashchange', fn);
  }
  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);

    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 {
  createRouter,
  createWebHashHistory,
  useRouter
};
  1. useRouter 函数:

    • 通过 inject 获取在应用中提供的路由实例,其 key 为 ROUTER_KEY,这个 key 在后面的代码中定义为 '_router_'
  2. createRouter 函数:

    • 创建并返回一个新的路由器实例,使用传入的 options 参数进行配置。
  3. createWebHashHistory 函数:

    • 创建一个基于哈希的路由历史管理对象。

    • 使用了一个内部函数 bindEvents 来绑定 hashchange 事件,以便在哈希发生变化时触发回调函数。

    • 返回一个对象,其中包含了 bindEvents 函数和当前 URL 的哈希部分。

  4. Router 类:

    • 构造函数接受一个包含 historyroutesoptions 对象。

    • 初始化 this.historythis.routesthis.currentthis.current 使用 Vue 3 的 ref 函数创建,用于响应式地保存当前路由路径。

    • 在构造函数中调用 this.history.bindEvents 绑定哈希变化事件,以更新当前路由路径。

    • install 方法用于在 Vue 应用中注册路由器实例、router-link 组件和 router-view 组件。

我们这个自定义路由器实现了Hash路由模式,同时通过 install 方法将 router-linkrouter-view 注册为全局组件,并提供了路由信息的全局访问。

创建RouterView.vue

接下来,我们创建 RouterView.vue 组件,用于映射url与组件之间关系。模板如下:

html 复制代码
<template>
  <component :is="component"></component>
</template>

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

const router = useRouter();

const component = computed(() => {
  const route = router.routes.find((route) => {
    return route.path === router.current.value;
  });
  
  return route ? route.component : null;
});
</script>
  1. <template> 部分:

    • <component :is="component"></component>: 通过将<component> 元素的 :is 属性绑定到一个名为 component 的变量。使得我们运行页面时可以动态的选择要渲染的组件。
  2. <script setup> 部分:

    • import { computed } from 'vue';: 引入 Vue 3 的 computed 函数,用于创建计算属性。

    • import { useRouter } from '../myRouter/index.js';: 引入自定义的路由管理器(myRouter)中的 useRouter 函数,用于获取路由实例。

    • const router = useRouter();: 使用 useRouter 函数获取当前路由实例。

    • const component = computed(() => { ... });: 使用 computed 创建一个计算属性 component,该计算属性通过查找路由配置中与当前路径匹配的路由来获取相应的组件。具体过程是在 router.routes 中找到 pathrouter.current.value 匹配的路由,并返回对应的组件。如果找到匹配的路由,则返回该路由的组件,否则返回 null

总而言之,这个组件使用 computed 计算属性获取当前路由对应的组件,并通过 <component :is="component"></component> 实现动态组件的渲染。使用 useRouter 函数从路由器中获取实例,使得组件能够访问路由信息。

记得在router/index.js文件下引入我们自己的router

js 复制代码
// src/router/index.js
import { createRouter,createWebHashHistory } from './myRouter'

最后我们来看效果:

总结

到这里,我们成功地搭建了一个简单的Vue Router的Hash路由模式,并实现了组件与URL的映射关系。 要打造一个vue-router,我们首先要

1. 打造 createRouter createWebHashHistory 函数

2. 打造的router要能被 vue 给 use 成功

3. 被 use 后,就可以到拿到vue的实例对象, 也就可以用vue自带的方法

手写这样一个简单的路由器有助于理解Vue Router的原理,并提高对Vue.js框架的深入理解。希望这篇文章对你手写Vue Router的过程有所帮助!

那么到了这里我们今天的文章就结束啦~

更多内容【前端面试】当面试官叫你手写一个JavaScript发布订阅模式

创作不易,如果感觉这个文章对你有帮助的话,点个赞吧♥

相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom8 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰8 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪8 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪8 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy9 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github