vue-router 源码:前端路由

2023-11-13

前言

在学习 vue-router 的代码之前,先来简单了解一下前端路由。

前端路由主要有两种实现方法:

  1. Hash 路由
  2. History 路由

先来看看这两种方法的实现原理。

接着我们将用它们来简单实现一个自己的前端路由。

前端路由

Hash 路由

url 的 hash 是以 # 开头,原本是用来作为锚点,从而定位到页面的特定区域。当 hash 改变时,页面不会因此刷新,浏览器也不会向服务器发送请求。

http://www.xxx.com/#/home
复制代码

同时,hash 改变时,并会触发相应的 hashchange 事件。所以,hash 很适合被用来做前端路由。当 hash 路由发生了跳转,便会触发 hashchange 回调,回调里可以实现页面更新的操作,从而达到跳转页面的效果。

window.addEventListener('hashchange', function () {
  console.log('render');
});
复制代码

History 路由

HTML5 规范中提供了 history.pushStatehistory.replaceState 来进行路由控制。通过这两个方法,可以实现改变 url 且不向服务器发送请求。同时不会像 hash 有一个 #,更加的美观。但是 History 路由需要服务器的支持,并且需将所有的路由重定向到根页面。

History 路由的改变不会去触发某个事件,所以我们需要去考虑如何触发路由更新后的回调。

有以下两种方式会改变 url:

  1. 调用 history.pushState 或 history.replaceState;
  2. 点击浏览器的前进与后退。

第一个方式可以封装一个方法,在调用 pushState(replaceState)后再调用回调。

function push (url) {
  window.history.pushState({}, null, url);
  handleHref();
}

function handleHref () {
  console.log('render');
}
复制代码

第二个方式,浏览器的前进与后退会触发 popstate 事件。

window.addEventListener('popstate', handleHref);
复制代码

路由实现

我们通过 <a> 标签来进行切换路由,通过一个 <div> 标签来装载各路由对应的页面内容。

参考 vue-router 的调用,我们会这么地调用一个 Router,将路由与对应组件作为参数传入:

const router = new Router([
  {
    path: '/',
    component: 'home'
  },
  {
    path: '/book',
    component: 'book'
  },
  {
    path: '/movie',
    component: 'movie'
  }
]);
复制代码

数组里是各路由对应的要显示的内容,接下来就来开始实现这个 Router

Hash 路由实现

Hash 路由 <a> 标签都需要带上 #

<div>
  <a href="#/">home</a>
  <a href="#/book">book</a>
  <a href="#/movie">movie</a>
    
  <div id="content"></div>
</div>
复制代码

Router 的代码实现如下:

class Router {
  constructor (options) {
    this.routes = {};
    
    this.init();
    
    // 遍历,绑定视图更新
    options.forEach(item => {
      this.route(item.path, () => {
      	document.getElementById('content').innerHTML = item.component;
      });
    });
  }
  
  // 绑定监听事件
  init () {
    window.addEventListener('load', this.updateView.bind(this), false);
    window.addEventListener('hashchange', this.updateView.bind(this), false);
  }
  
  // 更新试图
  updateView () {
    const currentUrl = window.location.hash.slice(1) || '/';
    this.routes[currentUrl] && this.routes[currentUrl]();
  }
  
  // 将路由与回调函数关联
  route (path, cb) {
    this.routes[path] = cb;
  }
}
复制代码

实现效果如下:

History 路由实现

History 路由需要服务器的支持,可以点击 这里 的代码参考。

<div>
  <a href="javascript:void(0);" data-href="/">home</a>
  <a href="javascript:void(0);" data-href="/book">book</a>
  <a href="javascript:void(0);" data-href="/movie">movie</a>
    
  <div id="content"></div>
</div>
复制代码

Router 的代码实现如下:

class Router {
  constructor (options) {
    this.routes = {};

    this.init();
    this.bindEvent();

    // 遍历,绑定视图更新
    options.forEach(item => {
      this.route(item.path, () => {
        document.getElementById('content').innerHTML = item.component;
      });
    });
  }

  // 绑定点击事件
  bindEvent () {
    const _this = this;
    const links = document.getElementsByTagName('a');

    [].forEach.call(links, link => {
      link.addEventListener('click', function () {
        const url = this.getAttribute('data-href');
        _this.push(url);
      });
    });
  }

  // 绑定监听事件
  init () {
    window.addEventListener('load', this.updateView.bind(this), false);
    window.addEventListener('popstate', this.updateView.bind(this), false);
  }

  push (url) {
    window.history.pushState({}, null, url);
    this.updateView();
  }

  // 更新试图
  updateView () {
    const currentUrl = window.location.pathname || '/';
    this.routes[currentUrl] && this.routes[currentUrl]();
  }

  // 将路由与回调函数关联
  route (path, cb) {
    this.routes[path] = cb;
  }
}
复制代码

实现效果如下:

最后

前端路由实现方式有两种,分别是:

  1. Hash 路由
  2. History 路由

原理都是修改 url 的同时不刷新页面,不向服务器发送请求,通过监听特殊的事件来更新页面。

以上实现全部源码参考 这里

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue-router 源码:前端路由 的相关文章

  • 使用 vscode 调试器调试 next.js

    我已经使用安装了一个项目创建下一个应用程序 https github com segmentio create next app 我需要使用我的编辑器 vscode 调试服务器端渲染 所以我访问过vscode recipes 如何调试 ne
  • 关闭选项卡时要求确认[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 当我在某些浏览器上关闭页面时 我希望出现一个消息框 并询问我是否真的要关闭页面 有两个按钮 如果我单击No那么这个标签就不会被关闭 我怎样
  • 不和谐机器人 |不和谐.js |类型错误:无法读取未定义的属性“长度”

    我正在制作一个 Discord 机器人 并且正在使用 CodeLyon 的视频作为参考 该错误位于我的 message js 文件中 该文件包含以下内容 require dotenv config create cooldowns map
  • 为什么 JavaScript base-36 转换看起来不明确

    我目前正在编写一段使用 Base 36 编码的 JavaScript 我遇到了这个问题 parseInt welcomeback 36 toString 36 看来要回归了 welcomebacg 我在 Chrome 开发者控制台和 Nod
  • jquery.find() 可以只选择直接子项吗?

    我应该向 jQuery find 提供什么参数来选择元素子元素而不选择其他元素 我不能用 gt 引导选择器 而用 将选择所有后代 而不仅仅是直接子代 我知道 jQuery children 但这是一个库 因此用户能够提供自己的选择器 并且我
  • 在 Vue.js 中从父组件执行子方法

    目前 我有一个 Vue js 组件 其中包含其他组件的列表 我知道使用 vue 的常见方式是将数据传递给孩子 并从孩子向父母发出事件 但是 在这种情况下 我想在子组件中的按钮出现时执行子组件中的方法 parent被点击 哪种方法最好 一种建
  • 如何重定向到 instagram://user?username={username}

    我的 html 页面上有这个链接 可以在特定用户上打开 Instagram 应用程序 a href Link to Instagram Profile a 我一直在寻找自动运行 url instagram user username USE
  • 使用 useReducers 调度函数发送多个操作?

    使用时是否可以通过调度函数发送多个动作useReducer挂钩反应 我尝试向它传递一组操作 但这会引发未处理的运行时异常 明确地说 通常会有一个初始状态对象和一个减速器 如下所示 const initialState message1 nu
  • 我想检查 $('#td1').text() === "x" 是否?

    我想检查innerHtml是否有X或O 所以我不能再次添加任何其他东西 但它不起作用 添加检查代码后它就停止了 我在这里尝试做一个简单的XO游戏来更熟悉javascript和jquery 我也不确定是否可以用 jQuery 做到这一点
  • Google App Engine:修改云运行环境

    我正在尝试部署一个使用自定义 Node js 服务器的 Next js 应用程序 我想将自定义构建变量注入应用程序 next config js const NODE ENV process env NODE ENV const envTy
  • 可以使用 jQuery 或 Javascript 将图片的特定部分用作链接吗?

    我有这个想法 将图片 而不是文本 的各个部分链接到不同的页面或网站 并且我想在不实际创建不同的照片并将它们彼此靠近的情况下完成 这样看起来就像是一张完整的图片 这里有人知道如何使用 JavaScript 的变体 例如 jQuery 或纯 J
  • 从未用 @flow 标记的导入文件中获取类型定义

    TL DR我怎么告诉flow从未声明的导入模块导入类型定义 flow 加长版 流接缝能够从不使用流语法的文件中派生类型 请参阅示例 示例文件 flow js if Math random lt 0 5 var y hello else va
  • 通过 CDN 使用 Dojo 时如何加载自定义 AMD 模块?

    我正在使用 google 的 CDN 并尝试使用他们的加载程序加载我自己的 AMD 模块 我知道我做错了什么 但我被困住了 有任何想法吗
  • 如何在react-native中获取Text组件的onPress值

    我是一名新的 React Native 开发人员 我想使用 onPress 获取 Text 组件的值并将其传递给函数
  • 如何使输入字段和提交按钮变灰

    我想变灰这两件事 http doorsplit heroku com 歌曲输入字段和提交按钮 直到用户输入艺术家 有没有一种简单的方法可以通过 JQuery 来做到这一点 艺术家输入字段的id是 request artist 你可以这样做
  • Grails 在 javascript 内的 GSP 站点中使用 grails var

    我有一个在 GSP 文件中的 javascript 代码中使用 grails 变量值的问题 例如 我有一个会话值session getAttribute selectedValue 我想在 javascript 代码部分使用这个值 我现在的
  • Laravel 中只向登录用户显示按钮

    如果我以 John 身份登录 如何才能只显示 John 的红色按钮而不显示 Susan 的红色按钮 测试系统环境 Win10 Laravel5 4 Mysql5 7 19 table class table table responsive
  • 为什么在 Internet Explorer 中访问 localStorage 对象会引发错误?

    我正在解决一个客户端问题 Modernizr 意外地没有检测到对localStorageInternet Explorer 9 中的对象 我的页面正确使用 HTML 5 文档类型 并且开发人员工具报告该页面具有 IE9 的浏览器模式和 IE
  • 模块构建失败(来自 ./node_modules/babel-loader/lib/index.js)Vue Js

    我从 GitHub 下载了一个我和我的朋友正在开发的项目 但是当我尝试运行时 npm run serve 我收到这个错误 src main js 中的错误 Module build failed from node modules babe
  • Javascript转换时区问题

    我在转换当前时区的日期时间时遇到问题 我从服务器收到此日期字符串 格式为 2015 10 09T08 00 00 这是中部时间 但是当我使用 GMT 5 中的 new Date strDate 转换此日期时间时 它返回给我的信息如下 这是不

随机推荐

  • Mysql——压缩包方式安装教程

    一 Mysql压缩包下载方式 zip版 5 7及8 0 的下载需到官方网站下载 不同版本对应能安装在不同的操作系统下 本次介绍的是mysql 8 0 30 winx64在win10下的安装方式 下载网址 MySQL Download MyS
  • android模拟器与宿主机通讯

    android模拟器与PC的端口映射 一 概述 Android系统为实现通信将PC电脑IP设置为10 0 2 2 自身设置为127 0 0 1 而PC并没有为Android模拟器系统指定IP 所以PC电脑不能通过IP来直接访问Android
  • Mysql增强半同步模式_MySQL增强半同步的搭建实验,和一些参数的个人理解

    环境信息 role ip port hostname master 192 168 188 101 4306 mysqlvm1 slave 192 168 188 201 4306 mysqlvm1 1 5306 6306 7306 MyS
  • eclipse搜索类快捷键

    习惯的编辑器可以提高编程效率 熟悉的快捷键可以提高工作效率 本文更新eclipse中常用的搜索快捷键 打开资源快捷键 Ctrl Shift R 通过在搜索框中输入名字可以很方便的在项目或工作空间中找某个文件 支持模糊查询功能 例如输入文件的
  • Linux防火墙

    关于linux系统防火墙 centos5 centos6 redhat6系统自带的是iptables防火墙 centos7 redhat7自带firewall防火墙 ubuntu系统使用的是ufw防火墙 必要操作 linux系统防火墙开放相
  • AOP之5种增强方法应用范例

    林炳文Evankaka原创作品 转载请注明出处http blog csdn net evankaka Spring AOP 提供了 5 种类型的通知 它们分别是 Before Advice 前置通知 After Returning Advi
  • PyTorch 手把手搭建神经网络 (MNIST)

    推荐下我自己建的Python学习群 856833272 群里都是学Python的 如果你想学或者正在学习Python 欢迎你加入 大家都是软件开发党 不定期分享干货 还有免费直播课程领取 包括我自己整理的一份2021最新的Python进阶资
  • python写入文件后换行_python写入文件自动换行问题的方法

    现在需要一个写文件方法 将selenium的脚本运行结果写入test result log文件中 首先创建写入方法 def write result str writeresult file r D eclipse4 4 1 script
  • 一些文件头

    由这些文件头即使文件后缀被乱改也可以通过查看二进制文件查出文件的匹配格式 当然这就是一些播放器识别文件的方法 1 从Ultra edit 32中提取出来的 JPEG jpg 文件头 FFD8FF PNG png 文件头 89504E47 G
  • 浅析进程与线程之间的区别

    文章目录 浅析进程与线程之间的区别 从最普遍的答案出发 什么是计算机资源 计算资源 存储资源 I O设备资源 什么是进程 线程 操作系统怎样给进程分配资源的 操作系统怎样调度进 线程的 进程的上下文切换 为什么需要线程 参考链接 浅析进程与
  • mybatis generator

    文章目录 generatorConfig xml GeneratorSqlmap java log4j properties lib maven pom generatorConfig xml
  • 【deep_thoughts】30_PyTorch LSTM和LSTMP的原理及其手写复现

    文章目录 LSTM API 手写 lstm forward 函数 LSTMP 修改 lstm forward 函数 视频链接 30 PyTorch LSTM和LSTMP的原理及其手写复现 哔哩哔哩 bilibili PyTorch LSTM
  • Ubuntu 下安装 OpenSSH Server

    Ubuntu 下安装 OpenSSH Server 是无比轻松的一件事情 需要的命令只有一条 sudo apt get install openssh server 查看返回的结果 如果没有出错 则用putty SecureCRT SSH
  • Deprecated usages detected, please refer to the el-pagination documentation for more details

    遇到这个问题 说明你用el pagination分页器参数传递不正确 在这里插入图片描述 https img blog csdnimg cn 5952bad428654dda8d956181f45183d5 png 我的是由于total没有
  • 在tinymce富文本中插入本地视频解决方案

    前言 最近在改一个别人的项目时候 遇到一个需求 要在tinymce富文本中添加本地视频 tinymce富文本本身是不具备添加本地视频的功能的 需要使用一些其他手段来添加本地视频 小demo截图 方法 1 在富文本的外面写一个添加视频的按钮
  • 第二十九章 Unity关节Joint

    关节组件将刚体连接到另一个刚体或空间中的固定点 关节施加使刚体移动的力 而关节限制功能可以限制该移动 Unity 提供的以下关节可以对刚体组件施加不同的力和限制 从而使这些刚体具有不同的运动 Hinge Joint铰链关节 使两个刚体像被连
  • 软件工程——结构化设计

    一 结构化软件设计的任务 在结构化设计方法中 概要设计阶段将软件需求转化为数据结构和软件的系统结构 概要设计阶段要完成体系结构设计 数据设计及接口设计 详细设计阶段要完成过程设计 二 结构化设计与结构化分析的关系 结构化设计方法的实施要点
  • android 旋转屏幕导致Activity重建解决方法

    横竖屏切换时候activity的生命周期 1 不设置Activity的android configChanges时 切屏会重新调用各个生命周期 切横屏时会执行一次 切竖屏时会执行两次 2 设置Activity的android configC
  • python 根据当前时间创建文件

    在当前目录中批量创建文件 文件名为 Y m d H M S格式的当前时间 精确到秒 为防止出现重名文件 在每创建一个文件后 让线程休眠一秒 import time def create global name localTime time
  • vue-router 源码:前端路由

    前言 在学习 vue router 的代码之前 先来简单了解一下前端路由 前端路由主要有两种实现方法 Hash 路由 History 路由 先来看看这两种方法的实现原理 接着我们将用它们来简单实现一个自己的前端路由 前端路由 Hash 路由