手撕Vue中的RouterLink和RouterView,深入理解其底层原理(二)

news2024/12/26 23:52:32

前言

在之前我们已经讲过了如何手撕本文RouterLink,深入讲解了RouterLink的基本原理

手撕Vue中的RouterLink和RouterView,深入理解其底层原理(一) - 掘金 (juejin.cn)

接下来我们就继续手撕RouterView吧!!!

RouterView

RouterView是根据url地址去显示对应的页面

所以RouterView实现的核心就是动态组件<component :is="component"> </component>

那我们渲染页面的流程应该是怎么样的?

  • 拿到url
  • 看看配置,/、/homr对应的谁
  • 加载

接下来我们需要修改@/router/index.js中的代码,为其添加router的配置数组

import { createRouter } from '@/router/grouter/index';

const router = createRouter();

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/HomeView.vue'),
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('@/views/AboutView.vue'),
  },
];

export default router;

修改好这个以后,我们就想,既然是创建router,那createRouter()方法是不是一定需要接收配置参数

既然是这样,我们就需要继续修改代码

  • @/router/index
import { createRouter, createWebHistory } from "@/router/grouter/index";

const routes = [
  {
    path: "/",
    name: "Home",
    component: () => import("@/views/HomeView.vue"),
  },
  {
    path: "/about",
    name: "About",
    component: () => import("@/views/AboutView.vue"),
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

可以看到我们在构建router的时候传入了两个数据,一个是配置参数和规则

并且规则是在@/router/grouter/index里面的

显然我们就需要对应的去修改@/router/grouter/index的代码了

  • @/router/grouter/index
import RouterLink from "./RouterLink.vue";
import RouterView from "./RouterView.vue";

class Router {
  constructor(options) {
    console.log(options,"/");
  }
  install(app) {
    console.log(app);
    console.log("vue对接router");
    app.component("RouterLink", RouterLink);
    app.component("RouterView", RouterView);
  }
}

const createWebHistory = () => {};

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

export { createRouter,createWebHistory };

这里面我们在构造函数constructor中添加了一个参数,并且定义了一个createWebHistory规则

同时我们还修改了对应的createRouter方法,让他将接收的参数传递给构造函数

接下来我们可以看到页面的打印效果为

请添加图片描述

可以看到我们已经在构造函数中拿到了数据了

接下来我们就去修改构造函数,给这个类赋值

constructor(options) {
    console.log(options, "/");
    this.routes = options.routes;
    this.history = options.history;
    // 当前的url状态 我们只要改变this.history.url的值,current就会改变,并更新页面
    this.current = ref(this.history.url);
}

接下来我们继续修改一下createWebHistory函数

const createWebHistory = () => {
  const bindEvents = (fn) => {
    window.addEventListener("hashchange", fn);
  };
  return {
    url: window.location.hash.slice(1) || "/",
    bindEvents,
  };
};

函数内部定义了一个 bindEvents 函数,用于给 window 对象的 hashchange 事件添加一个事件处理函数 fn

当调用 createWebHistory 函数时,它返回一个对象,这个对象有两个属性:

  • url:获取当前 window.location.hash 去除 # 符号后的内容,如果没有 hash 部分则默认为 '/'
  • bindEvents:用于添加 hashchange 事件处理函数的方法。

接下来我们修改构造函数

constructor(options) {
    this.routes = options.routes;
    this.history = options.history;
    console.log(options, "/");
    // 当前的url状态 我们只要改变this.history.url的值,current就会改变,并更新页面
    this.current = ref(this.history.url);
    this.history.bindEvents(() => {
      this.current.value = window.location.hash.slice(1) || "/";
      console.log(this.current.value);
    });
  }

接收一个 options 对象作为参数,并从这个对象中提取 routerhistory 属性进行赋值。

然后,使用 ref 创建了一个响应式数据 current,并初始化为 history.url 的值。

接着,通过调用 history 对象的 bindEvents 方法,为 hashchange 事件添加了一个事件处理函数。当 hashchange 事件触发时,会更新 current 的值为新的 URL 的 hash 部分(去除 # 符号),如果没有 hash 部分则设置为 '/',并打印出更新后的 current 值。

我们可以看到这段代码的执行结果为

请添加图片描述

接下来我们就要在编写一个useRouter方法,在任何地方,使用 useRouter() 获取路由对象

const ROUTER_KEY = "__router__";
// 在任何地方,使用 useRouter() 获取路由对象
const useRouter = () =>{
  return inject(ROUTER_KEY);
}

然后就需要在install方法里面全局提供

 install(app) {
    console.log(app);
    console.log("vue对接router");
    // 全局提供
    app.provide(ROUTER_KEY, this);
    app.component("RouterLink", RouterLink);
    app.component("RouterView", RouterView);
  }

在一个应用程序(app)中,使用 provide 方法来提供一个具有键 ROUTER_KEY 且值为 this 的内容。

然后我们来编写一下RouterView.vue的代码

<template>
  <component :is="component"> </component>
</template>

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

let router = useRouter(); // router对象
console.log(router);
const component = computed(() => {
  const route = router.routes.find(
    route => route.path === router.current.value
  );
  return route ? route.component : null;
});
</script>

<style lang="scss" scoped></style>

在模板 <template> 部分,使用了动态组件 <component :is="component">,意味着要根据 component 的值来决定渲染的具体组件。

在脚本 <script setup> 部分,从自定义的 ./index 中导入了 useRouter 来获取路由对象 router。通过计算属性 component ,根据当前路由路径在 router.routes 中查找匹配的路由,并返回对应的组件,如果没有找到匹配的路由则返回 null

接下来运行项目就可以看到效果

请添加图片描述

我们的router对象已经成功打印出来了

之所以可以打印结果就是因为在@/router/grouter/index中将useRouter抛出,useRouter是可以获得__router__的

const ROUTER_KEY = "__router__";
// 在任何地方,使用 useRouter() 获取路由对象
const useRouter = () =>{
  return inject(ROUTER_KEY);
}

此时的组件树为

请添加图片描述

此时我们还没有办法渲染页面

这是由于我们之前采用的按需导入组件

  • @/router/index
import { createRouter, createWebHistory } from "@/router/grouter/index";

const routes = [
  {
    path: "/",
    name: "Home",
    component: () => import("@/views/HomeView.vue"),
  },
  {
    path: "/about",
    name: "About",
    component: () => import("@/views/AboutView.vue"),
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

将其修改为直接导入组件之后我们就能看到效果了

import { createRouter, createWebHistory } from "@/router/grouter/index";
import HomeView from "@/views/HomeView.vue";
import AboutView from "@/views/AboutView.vue";

const routes = [
  {
    path: "/",
    name: "Home",
    component: HomeView,
  },
  {
    path: "/about",
    name: "About",
    component: AboutView,
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

效果如下

请添加图片描述

自此我们就成功的实现了手搓一个RouterView

总结

本文讲解了如何手搓一个RouterView,结合代码一步一步自己手动实现RouterView可以让我们更加透彻的理解其底层的原理,以及其设计思想,希望对看到这里的你能够有所帮助!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1928944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建个人智能家居 7 - 空气颗粒物检测

搭建个人智能家居 7 - 空气颗粒物检测 前言说明PMS5003ESPHomeHomeAssistant结束 前言 到目前为止&#xff0c;我们这个只能家居系统添加了4个外设&#xff0c;分别是&#xff1a;LED灯、RGB灯、DHT11温度传感器和SGP30。今天继续添加环境测量类传感器“PMS5003空气颗粒物检测…

【数据集处理工具】根据COCO数据集的json标注文件实现训练与图像的文件划分

根据COCO数据集的json标注文件实现训练与图像的文件划分 一、适用场景&#xff1a;二、COCO数据集简介&#xff1a;三、场景细化&#xff1a;四、代码优势&#xff1a;五、代码 一、适用场景&#xff1a; 适用于一个常见的计算机视觉项目应用场景&#xff0c;特别是当涉及到使…

python数据可视化(6)——绘制散点图

课程学习来源&#xff1a;b站up&#xff1a;【蚂蚁学python】 【课程链接&#xff1a;【【数据可视化】Python数据图表可视化入门到实战】】 【课程资料链接&#xff1a;【链接】】 Python绘制散点图查看BMI与保险费的关系 散点图: 用两组数据构成多个坐标点&#xff0c;考察…

PXE、Kickstart和cobbler

一.系统装机 1.1 三种引导方式 启动操作系统 1.硬盘 2.光驱(u盘) 3.网络启动 pxe 1.2 系统安装过程 1.加载boot loader: Boot Loader 是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设 备、建立内存空间的映射图,从而将系统的软硬…

详解曼达拉升级:如何用网络拓扑结构扩容BSV区块链

​​发表时间&#xff1a;2024年5月24日 BSV曼达拉升级是对BSV基础设施的战略性重塑&#xff0c;意在显著增强其性能&#xff0c;运行效率和可扩容。该概念于2018年提出&#xff0c;其战略落地将使BSV区块链顺利过渡&#xff0c;从现有的基于单一集成功能组件的网络拓扑结构&am…

使用Elasticsearch Python SDK 查询Easysearch

随着数据分析需求的不断增长&#xff0c;能够高效地查询和分析大数据集变得越来越重要。Elasticsearch作为一种强大的分布式搜索和分析引擎&#xff0c;被广泛应用于各种场景。Easyearch 支持原生 Elasticsearch 的 DSL 查询语法&#xff0c;确保原业务代码无需调整即可无缝迁移…

【数据结构与算法 经典例题】判断二叉树是否对称

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法 经典例题》C语言 期待您的关注 目录 一、问题描述 二、解题思路 三、C语言实现代码 一、问题描述 给你一个二…

MySQL NaviCat 安装及配置教程(Windows)【安装】

文章目录 一、 MySQL 下载1. 官网下载2. 其它渠道 二、 MySQL 安装三、 MySQL 验证及配置四、 NaviCat 下载1. 官网下载2. 其它渠道 五、 NaviCat 安装六、 NaviCat 激活 软件 / 环境安装及配置目录 一、 MySQL 下载 1. 官网下载 安装地址&#xff1a;https://www.mysql.com/…

Python | Leetcode Python题解之第237题删除链表中的节点

题目&#xff1a; 题解&#xff1a; class Solution:def deleteNode(self, node):node.val node.next.valnode.next node.next.next

OrangePi Aipro Ai计算测试

开发板配置 http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-AIpro.html CPU4核64位处理器 AI处理器GPU集成图形处理器AI算力8-12TOPS算力内存LPDDR4X&#xff1a;8GB/16GB&#xff08;可选&#xff09;&#xff0c;速率&#xff1a;3200…

让AI语言模型自由飞翔:LangChain框架的奇妙世界

今天&#xff0c;我将为大家揭开一项令人激动的技术——LangChain。想象一下&#xff0c;如果能将人工智能的强大能力与我们日常使用的数据和工具无缝连接&#xff0c;那将开启怎样崭新且无限的可能&#xff01; LangChain&#xff0c;一个专为大型语言模型设计的框架&#xf…

Linux FFmpeg安装教程

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Windows安装Pycharm及汉化教程

在安装好了Python之后呢&#xff0c;我们需要更方便的进行编写代码&#xff0c;使用Python自带的IDLE和命令行是不太友好的。 那么有没有一款免费好用的写代码工具呢&#xff1f;答案是有的&#xff01; PyCharm 是由 JetBrains 打造的一款 Python IDE&#xff0c;提供代码分析…

【Django】网上蛋糕项目商城-注册,登录,修改用户信息,退出功能

概念 通过以上多篇文章的讲解&#xff0c;对该项目的功能已经实现了很多&#xff0c;本文将对该项目的用户注册&#xff0c;登录&#xff0c;修改用户信息&#xff0c;以及退出等功能的实现。 注册功能实现 点击head.html头部页面的注册按钮&#xff0c;触发超链接跳转至use…

K8s集群初始化遇到的问题

kubectl describe pod coredns-545d6fc579-s9g5s -n kube-system 找到原因1&#xff1a;CoreDNS Pod 处于 Pending 状态的原因是集群中的节点都带有 node.kubernetes.io/not-ready 污点 journalctl -u kubelet -f 14:57:59.178592 3553 remote_image.go:114] "PullIma…

火车行驶动态特效404单页源码

源码介绍 火车行驶动态特效404单页源码,白云飘飘,小火车带着not page found行驶远方,适合做网站错误页,将下面的代码放到空白的html文件里面,鼠标双击即可查看效果,或者上传到服务器,设置好重定向即可 效果预览 完整代码 <!DOCTYPE html> <html><head…

【ROS2】高级:实现自定义内存分配器

目标&#xff1a;本教程将展示在编写 ROS 2 C 代码时如何使用自定义内存分配器。 教程级别&#xff1a;高级 时间&#xff1a;20 分钟 目录 背景 编写分配器编写一个示例主程序将分配器传递到进程内管道测试和验证代码 TLSF 分配器 本教程将教您如何为发布者和订阅者集成自定义…

【个人笔记】685. 冗余连接 II 的解释(并查集)

一棵树有n个点和n条边&#xff0c;返回一条能删除的边&#xff0c;使得剩下的图是有 n 个节点的有根树。 解释&#xff1a; 注意不冗余的有根树的特性&#xff01;**根节点入度为0&#xff0c;其余结点只有一个入度&#xff01;**所以冗余的两种情况如下&#xff1a; &#xff…

jmeter持续学习之---控制器

IF控制器 下面这种写法jmeter不推荐有性能的问题 jmeter推荐勾选上的这种写法 使用"Interpret Condition as Variable Expression"工具的性能要好一些 循环控制器 ForEach控制器 与用户定义的变量或者正则表达式提取器配合使用,循环读取。用户定义的变量或者正则…

彻底改变时尚:使用 GAN 实现 AI 的未来

彻底改变时尚&#xff1a;使用 GAN 实现 AI 的未来 一、介绍 想象一下&#xff0c;在这个世界里&#xff0c;时装设计师永远不会用完新想法&#xff0c;我们穿的每一件衣服都是一件艺术品。听起来很有趣&#xff0c;对吧&#xff1f;好吧&#xff0c;我们可以在通用对抗网络 &a…