Vue-Router 介绍及路由原理分析

news2025/4/5 14:20:57

文章目录

      • Vue-Router 路由模式
        • 单页面与传统页面跳转的区别
        • Hash 模式
        • History 模式
        • abstract 模式
      • 原理解析
        • Hash 模式原理
        • History 模式原理
      • 路由使用
        • 引入 Vue-Router
        • 获取全局路由跳转参数的变化
        • 获取路由中带的参数
        • 重定向页面

Vue-Router 路由模式

单页面与传统页面跳转的区别

  • 单页面

Vue 的单页面开发模式是基于组件和路由的配合,所有的页面均可视为组件,路由控制着访问路径,而每个路径映射一个组件。在单页模式中使用 a 标签做路由跳转是行不通的,因为本质上单页模式就只有一个 index.html 页面,所有的页面组件都在打包好的 JS 文件中,所以要使用 Vue-Router 的路由组件来跳转,这里的原因在后文中会有具体的例子解析。

  • 传统页面

传统页面的开发模式,路由一般是由超链接(a 标签)来控制页面的跳转与切换,每次都会刷新整个页面,体验上不如单页模式。

Vue-Router 为我们提供了三种路由模式:

  • Hash 模式
  • History 模式
  • abstract 模式

Hash 模式

Hash 模式是 Vue-Router 的默认模式,具体的体现是在浏览器地址栏上 URL 路径永远带着一个「#」号。在浏览器支持度上面,Hash 模式是比较强势的,甚至能兼容低版本的 IE 浏览器。「#」号后面内容的改变,不会引起页面对服务端的请求,所以也就不会重新加载页面。在部署服务器方面,个人认为 Hash 模式比 History 模式来得更为方便,因为 History 有 URL 重定向问题,需要在服务端去配置 url 重定向,否则会报 404 错误。市面上也有不少大厂是使用 Hash 模式进行开发的,比如网易云音乐:

img

History 模式

HTML5 History API 提供了一个 history.pushState 和 history.reolaceState 方法(浏览器支持情况不是很乐观),它能让开发人员在不刷新网页的情况下改变站点的 URL。因为 Hash 模式会带上一个「#」号,会让 URL 地址变得比较难看,所以很多开发者都会选择 History 模式开发。但有个缺点,前端的 URL 必须和实际向服务端发起的请求的 URL 保持一致,如果服务端没有对相应的路由做处理,则会返回 404 错误页面。

abstract 模式

abstract 模式针对的是没有浏览器环境的情况,比如 Weex 客户端开发,内部是没有浏览器 API 的,那么 Vue-Router 自身会对环境做校验,强制切换到 abstract 模式,如果默认在 Vue-Router 的配置项中不写 mode 的值,在浏览器环境下会默认启用 Hash 模式,在移动客户端下使用 abstract 模式。

原理解析

Hash 模式原理

大家常常会在浏览器上看到这样一种场景,点击某个文字,网页会跳转到某一个固定的位置,并且页面不会刷新。这便是浏览器的 a 标签锚点。Hash 模式被运用在了单页面开发的路由模式上,下面我们来简单实现一个通过 Hash 去控制页面组件的展示。 浏览器原生方法为我们提供了一个监听事件 hashchange,它能监听到的改变如下:

  • 点击 a 标签改变 URL 地址;
  • 浏览器的前进后退行为;
  • 通过 window.location 方法改变地址栏。

以上三种情况都会触发 hashchange 监听事件,通过这个事件我们可以获取到 localtion.hash,继而去匹配相应的组件,下面是简易代码实现:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Hash 模式</title>
  </head>
  <body>
    <div>
      <ul>
        <li><a href="#/page1">page1</a></li>
        <li><a href="#/page2">page2</a></li>
      </ul>
      <div id="route-view"></div>
    </div>
    <script type="text/javascript">
      // 下面为hash的路由实现方式
      // 第一次加载的时候,不会执行hashchange监听事件,默认执行一次
      
      window.addEventListener('DOMContentLoaded', Load);
      window.addEventListener('hashchange', HashChange);
      var routeView = null;
      function Load() {
        routeView = document.getElementById('route-view');
        HashChange();
      }
      function HashChange() {
        console.log('location.hash', location.hash);
        switch (location.hash) {
          case '#/page1':
            routeView.innerHTML = 'page1';
            return;
          case '#/page2':
            routeView.innerHTML = 'page2';
            return;
          default:
            routeView.innerHTML = 'page1';
            return;
        }
      }
    </script>
  </body>
</html>

当初始的 HTML 文档被完全加载和解析完成之后, DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。 hashchange 不会被默认触发,所以网页首次加载完成后,需要默认执行一次 hashchange 监听方法要执行的函数 HashChange,当我点击代码中的两个 a 标签时,URL 地址栏改变触发 hashchange 事件, HashChange 方法通过拿到 location.hash 去匹配相应的组件(这里假设 page1、page2 为页面容器组件)。

在浏览器中打开HTML查看效果:

img

这样我们就实现了一个简易版的 Hash 路由模式,大家可以自己手动实现一下,加深记忆。

History 模式原理

下面来介绍 History 模式。

通过 History 模式去控制路由,会遇到一些麻烦,根本原因是 History 模式利用的时候 popstate 监听事件无法监听到 pushStatereplaceState、a 标签这三种形式的变化,浏览器的前进后退是可以监听到。那么有什么好的解决方案吗?

小知识:pushState 和 replaceState 都是 HTML5 的新 API,他们的作用很大,可以做到改变浏览器地址却不刷新页面。

我们可以通过遍历页面上的所有 a 标签,阻止 a 标签的默认事件的同时,加上点击事件的回调函数,在回调函数内获取 a 标签的 href 属性值,再通过 pushState 去改变浏览器的 location.pathname 属性值。然后手动执行 popstate 事件的回调函数,去匹配相应的路由。逻辑上可能有些饶,我们用代码来解释一下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>History 模式</title>
  </head>
  <body>
    <div>
      <ul>
        <li><a href="/page1">page1</a></li>
        <li><a href="/page2">page2</a></li>
      </ul>
      <div id="route-view"></div>
    </div>
    <script type="text/javascript">
      // 下面为history的路由实现方式
      window.addEventListener('DOMContentLoaded', Load);
      window.addEventListener('popstate', PopChange);
      var routeView = null;
      function Load() {
        routeView = document.getElementById('route-view');
        PopChange();
        // 获取所有带 href 属性的 a 标签节点
        var aList = document.querySelectorAll('a[href]');
        // 遍历 a 标签节点数组,阻止默认事件,添加点击事件回调函数
        aList.forEach((aNode) =>
          aNode.addEventListener('click', function (e) {
            e.preventDefault(); //阻止a标签的默认事件
            var href = aNode.getAttribute('href');
            //  手动修改浏览器的地址栏
            history.pushState(null, '', href);
            // 通过 history.pushState 手动修改地址栏,
            // popstate 是监听不到地址栏的变化,所以此处需要手动执行回调函数 PopChange
            PopChange();
          })
        );
      }
      function PopChange() {
        console.log('location', location);
        switch (location.pathname) {
          case '/page1':
            routeView.innerHTML = 'page1';
            return;
          case '/page2':
            routeView.innerHTML = 'page2';
            return;
          default:
            routeView.innerHTML = 'page1';
            return;
        }
      }
    </script>
  </body>
</html>

同样将上面代码复制到 index.js 文件下替换,效果如下所示:

img

这就是我们上述提到的 History 模式下,会遇到的麻烦。想要解决这个问题需要配合项目的 Web 服务器如 Express、Koa 等配置服务端返回的内容。

路由使用

接下来我们采用 ES2015 的形式简单讲解 Vue Router 的使用。

引入 Vue-Router

首先是 HTML,我们引入 Vue 和 Vue Router 的静态资源,大家可以去 BootCDN 搜索自己需要的静态资源,下面是 HTML 代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
    <title>Vue Router</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <!-- 使用 router-link 组件来导航. -->
        <!-- 通过传入 to 属性指定链接. -->
        <!-- <router-link> 默认会被渲染成一个 <a> 标签 -->
        <router-link to="/page1">Go to Page1</router-link>
        <br />
        <router-link to="/page2">Go to Page2</router-link>
      </ul>
      <!-- 路由出口 -->
      <!-- 路由匹配到的组件将渲染在这里 -->
      <router-view></router-view>
    </div>
    <script type="text/javascript">
      // 1. 定义 (路由) 组件。
      // 可以从其他文件 import 进来,这里简单写
      const Page1 = { template: '<div>Page1</div>' };
      const Page2 = { template: '<div>Page2</div>' };

      // 2. 定义路由
      // 每个路由应该映射一个组件。 其中"component" 可以是
      // 通过 Vue.extend() 创建的组件构造器,
      // 或者,只是一个组件配置对象。
      const routes = [
        { path: '/page1', component: Page1 },
        { path: '/page2', component: Page2 },
      ];

      // 3. 创建 router 实例,然后传 routes 配置
      // 你还可以传别的配置参数, 不过先这么简单着吧。
      const router = new VueRouter({
        routes: routes,
      });

      // 4. 创建和挂载根实例。
      // 记得要通过 router 配置参数注入路由,
      // 从而让整个应用都有路由功能
      const app = new Vue({
        router: router,
      }).$mount('#app');
    </script>
  </body>
</html>

标签和 标签是引入 Vue-Router 后才能使用的,路由匹配到的组件都会渲染在 标签上, 标签用于路由的跳转。

Vue-Router 默认启动 Hash 模式,同样复制到 index.js,重启 node app.js 启动页面的时候,会看到如下所示:

img

获取全局路由跳转参数的变化

想要全局监听路由的变化,可以在入口页面通过 watch $router对象来实现,修改上述代码:

<script>
  ...
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能。
const app = new Vue({
  router: router,
  watch: {
    $route(to, from) {
      console.log('to', to)
      console.log('from', from)
    }
  }
}).$mount('#app')
  ...
</script>

img

添加监听之后,可以拿到 to 和 from 两个参数,to 代表你跳转后的页面参数,from 代表你从哪个页面跳转来的,通过拿到这两个参数,我们就能设置一级二级页面,用于制作过场动画。也可以添加页面顶部的页面加载进度条。

获取路由中带的参数

Page1 页面在跳转的时候带上参数到 Page2,我们如何在 Page2 拿到参数呢?首先我们要给 router-link 标签添加参数,如下所示:

<router-link :to="{path: '/page1', query: { id: 1111 }}">
  Go to Page1
</router-link>

在 Page1 的模板页面拿到参数:

const Page1 = { template: '<div>Page1 {{ $route.query.id }}</div>' };

img

重定向页面

这点还是比较重要的,当你输入一个不存在的路由,Vue-Router 无法匹配到的时候,需要默认回到首页,这就要用到重定向匹配,代码如下所示:

const routes = [
  { path: '/page1', component: Page1 },
  { path: '/page2', component: Page2 },
  { path: '*', redirect: '/page1' },
];

这里我们默认 Page1 为首页,当 * 上面的路径都没有被匹配到的时候,就会重新回到 /page1 对应的组件。

img

以上是 Vue-Router 的一些基础用法,更多深度使用请移步 官方文档详细阅读。

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

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

相关文章

Spark Catalyst 查询优化器原理

这里我们讲解一下SparkSQL的优化器系统Catalyst&#xff0c;Catalyst本质就是一个SQL查询的优化器&#xff0c;而且和 大多数当前的大数据SQL处理引擎设计基本相同&#xff08;Impala、Presto、Hive&#xff08;Calcite&#xff09;等&#xff09;。了解Catalyst的SQL优化流程&…

Spring 入门教程详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Web3中文|1月数据显示复苏迹象,涉及NFT、DeFi、Dapp、链游……

本期看点 1、Dapp行业概述 2、DeFi的TVL增长26.8%&#xff0c;有回暖迹象 3、NFT市场数据飙升&#xff0c;交易额达9.46亿美元 4、链游使用量占行业48% 5、与去年相比&#xff0c;1月份区块链漏洞损失最低 区块链领域正在多元化发展&#xff0c;2023年1月&#xff0c;从各…

从一致性角度考虑推荐冷启动长尾推荐问题(二)

前言&#xff1a;在推荐系统中user&item emb往往是最重要的特征之一&#xff0c;在冷启动和长尾优化的工作中&#xff0c;往往也是优化的重点&#xff0c;相当一部分工作是围绕着emb优化展开&#xff0c;所以这里单独开了一章。4)emb分布一致性主要思路在于冷启内容emb和高…

PTP GPTP芯片资料翻译88E6352

88E6352应用 网关 车载信息娱乐 车身域控制器 PTP PTP通过周期型地交换控制包实现 选择其中网络最佳质量时钟元素&#xff0c;作为PTP网络中Grand Master.没有Grand Master 节点变成PTP slave节点。PTP节点从Grand Master节点获得他们驱动频率和时间信息。 基本观念是PTP帧…

21- 朴素贝叶斯 (NLP自然语言算法) (算法)

朴素贝叶斯要点 概率图模型算法往往应用于NLP自然语言处理领域。根据文本内容判定 分类 。 概率密度公式&#xff1a; 高斯朴素贝叶斯算法: from sklearn.naive_bayes import GaussianNB model GaussianNB() model.fit(X_train,y_train) 伯努利分布朴素贝叶斯算法 fro…

袋鼠云产品功能更新报告04期丨2023年首次,产品升级“狂飙”

新的一年我们加紧了更新迭代的速度&#xff0c;增加了数据湖平台EasyLake和大数据基础平台EasyMR&#xff0c;超40项功能升级优化。我们将继续保持产品升级节奏&#xff0c;满足不同行业用户的更多需求&#xff0c;为用户带来极致的产品使用体验。 以下为袋鼠云产品功能更新报…

java学习----网络编程

网络编程入门 网络编程概述 计算机网络 ​ 计算机网络是指地理位置不同的具有独立功能的计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理协调下&#xff0c;实现资源共享和信息传递的计算机系统…

如何在CSDN中使用ChatGPT

简介ChatGPT是OpenAI公司开发的一种大型语言模型。它是一种基于Transformer架构的深度学习模型&#xff0c;可以对语言进行建模和生成。它可以处理问答、对话生成、文本生成等多种任务。它诞生于2018年&#xff0c;并在随后的几年里不断改进和提高。OpenAI是一家人工智能研究实…

微服务项目【分布式锁】

创建Redisson模块 第1步&#xff1a;基于Spring Initialzr方式创建zmall-redisson模块 第2步&#xff1a;在zmall-redisson模块中添加相关依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</a…

java程序设计-ssm博客管理系统

博客管理系统是一个用于创建、管理和发布博客文章的应用程序。它通常包括一个后台管理界面&#xff0c;用于管理用户、文章、评论、标签等数据。同时&#xff0c;它还包括一个前端界面&#xff0c;用于展示博客文章并提供交互功能&#xff0c;例如评论和分享。 博客管理系统可…

Linux/Ubuntu安装部署Odoo15仓管系统,只需不到十步---史上最成功

sudo apt-get update sudo apt install postgresql -y sudo apt-get -f install sudo dpkg -i /home/ubuntu/odoo_15.0.latest_all.deb —报错再次执行上一条命令再执行 —安装包地址&#xff1a;http://nightly.odoo.com/15.0/nightly/deb/–翻到最下面 sudo apt-get ins…

NIFI大数据进阶_内嵌ZK模式集群2_实际操作搭建NIFI内嵌模式集群---大数据之Nifi工作笔记0016

然后我们开始来搭建nifi集群,可以看到之前我们上传上来的安装包 然后我们因为当前目录有了,我先去解压到其他目录 这里解压到/export/soft下面去 然后进去soft去看看,可以看到已经有了 然后我们说我们要搭建3个nifi的集群,那么这个时候,需要复制3份,但是 我们为了方便这里先…

SpringBoot 日志文件

(一)日志文件有什么用&#xff1f;除了发现和定位问题之外&#xff0c;我们还可以通过日志实现以下功能&#xff1a;记录用户登录日志&#xff0c;以便分析用户是正常登录还是恶意破解用户。记录系统的操作日志&#xff0c;以便数据恢复和定位操作 。记录程序的执行时间&#x…

字节码指令

目录 2.1 入门 2.2 javap 工具 2.3 图解方法执行流程 1&#xff09;原始 java 代码 2&#xff09;编译后的字节码文件 3&#xff09;常量池载入运行时常量池 4&#xff09;方法字节码载入方法区 5&#xff09;main 线程开始运行&#xff0c;分配栈帧内存 6&#xff09;…

百度地图API添加自定义标记解决单html文件跨域

百度地图API添加自定义标记解决单html文件跨域 因为要往百度地图上添加一些标注点&#xff0c;而且这些标注点要用自定义的图片&#xff0c;而且只能使用单html文件&#xff0c;不能使用服务器&#xff08;也别问为什么&#xff0c;就是这么个需求&#xff09;&#xff0c;做起…

互联网大厂测开面试记,二面被按地上血虐,所幸Offer已到手

在互联网做了几年之后&#xff0c;去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好&#xff0c;更重要的是&#xff0c;它是对你专业能力的背书&#xff0c;大厂工作背景多少会给你的简历增加几分竞争力。 如何备战面试的&#xff1f; 第一步&#xff1a;准备简历…

第三方软件测试机构▏软件性能测试的测试流程和指标简析

软件性能是衡量软件产品质量的重要指标之一&#xff0c;性能测试也是软件测试中不可或缺的重要流程&#xff0c;主要测试软件性能方面的质量&#xff0c;它是一种非功能性的测试。进行性能测试是为了保障软件能够在期望的负载下运行良好&#xff0c;并且通过发现性能问题来消除…

【IP课堂】Ip地址如何进行精准定位?

通过Ip地址定位&#xff0c;是目前网络上最常见的定位方式。当然&#xff0c;也是最简单的定位方式。其实方法大多都是雷同的&#xff0c;通过Ip定位&#xff0c;就目前网上公开的技术。如通过搜索关键词“定位&#xff0c;定位查询&#xff0c;Ip定位”等&#xff0c;只能查询…

《狂飙》壁纸太帅,Python自动切换太酷(8)

小朋友们好&#xff0c;大朋友们好&#xff01;我是猫妹&#xff01;要说最近什么电视剧最火&#xff1f;非《狂飙》莫属。《狂飙》剧名来自毛主席诗词“国际悲歌歌一曲&#xff0c;狂飙为我从天落”。导演借用“狂飙”二字来比喻剧中的扫黑除恶大风暴。据了解&#xff0c;《狂…