vue中router路由的原理?两种路由模式如何实现?(vue2) -(上)
平时我们编写路由时,通常直接下载插件使用,在main.js文件中引入直接通过引入vue-router中的Router通过Vue.use使用以后定义一个routeMap数组,里边是我们编写路由的地方,最后通过实例化一个 Router实例 将routes=我们定义的routeMao路由数组。
但是我们并不知道它是如何实现的,因此我们可以通过自己编写插件的形式,实现一个vue-router
常用路由步骤:
//router/index.js
import Vue from 'vue'import Router from 'vue-router'Vue.use(Router)export const routeMap= [{path: '/home',component: () => import('../components/Home/Home.vue')},{path: '/list',component: () => import('../components/List/List.vue')},
]const router = new Router({routes: constantRouterMap
})export default router//main.js
import router from './router'new Vue({router,store,render: h => h(App),
}).$mount('#app')
我们来实现一下如何实现一个hash模式的路由
由于我们不适用插件形式实现,因此我们使用步骤与平常实现有一些差异。
router/index.js是我们的路由文件,里面放置我们的路由
plugin/router.js是我们的自定义插件文件,里面放置我们自定义插件内容
components是我们组件目录,放置我们的路由组件(通常使用view定义路由组件)
main.js是我们的入口文件,需要在该文件引入router并且实例化vue的时候需要将引入的router使用
1、首先我们在App.vue文件中,通过<router-view/>以及<router-link/>定义声明式导航和路由占位
<div><router-link to="/home">首页</router-link><router-link to="/list">列表</router-link><hr><router-view></router-view>
</div>
这里我们会发现使用以后会报错,因为我们没有下载插件,因此没有这两个组件。
我们需要配置plugin插件
由于没有这两个全局组件因此我们需要配置两个全局组件
install方法是为了将我们的路由挂载在我们的组件实例上,通过mixin全局混入,将我们的实例上挂载$router属性
然后定义两个全局组件,router-link由于它渲染出来相当于html中的a标签,使用render方法,参数为一个createElement方法接收三个参数(第一个参数创建的元素,第二个参数元素具有的属性, 第三个参数元素的内容)
router-view由于是一个路由占位符,因此返回的是个组件,渲染组件。
通过routesMap是我们存放的路由,current是我们的当前的path路由,因此可以通过查找路由中key值为我们当前path路径的,查找到我们的组件。通过return渲染我们的组件
VueRouter.install = function (_Vue) {//1、保存VueVue = _Vue//2、将以下代码延迟到vue实例初始化完毕执行Vue.mixin({beforeCreate() {//判断如果有router属性就执行下方代码if (this.$options.router) {Vue.prototype.$router = this.$options.router;}}})//创建全局组件router-link和router-view//创建router-link组件Vue.component('router-link', {props: {to: {type: String | Object,required: true}},render(h) {// h 有三个参数,第一个参数创建的元素,第二个参数元素具有的属性, 第三个参数元素的内容return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)}})//创建router-view组件Vue.component('router-view', {// 组件要重新渲染,必须要让数据发生改变的时候,重新去执行render函数render(h) {const router = this.$routerconsole.log(router.routesMap, 'routesMap');let component = router.routesMap[router.current];//组件//获取到外部传递的路由表return h(component)}})
}
2、我们定义一个路由类,该类会获取到我们的路由信息从而进行路由监听以及组件渲染。
(1)、该类中constructor是我们通过实例化vueRouter传入的数据,我们需要通过将数据保存在vueRouter实例上,获取到当前的url path路径。
(2)、通过监听方法hashchange监听我们hash值的变化,通过方法进行监听,但是一定要注意这里方法的this指向必须是vueRouter实例不可以是window,因此需要通过bind改变this指向
(3)、通过defineReactive来讲我们的path路径变为响应式,这样每次路径发生变化可以监测到变化。
(4)、定义routesMap为我们的路由对象,遍历我们传入的路由数组,将我们每个path路径对应组件,为一组一组的键值对。
这样可以实现一套hash路由模式的路由跳转。
全部代码如下:
//plugin/router.js
let Vue;class VueRouter {constructor(options) {//保存选项this.options = options;// console.log(options, '路由数据');// 定义一个响应式的变量current,保存当前的hash值let url = location.hash.slice(1,) || '/';// defineReactive定义响应式的对象Vue.util.defineReactive(this, 'current', url)// hashchange事件来监听hash值的变化, 注意this指向问题addEventListener('hashchange', this.changeHash.bind(this))this.routesMap = {};//对routes中的对象做一个映射:routesMap = {'/home':component,'/list': component}this.options.routes.forEach(route => {this.routesMap[route.path] = route.component})}//监听hash值变化的函数changeHash() {//通过location.hash来获取到hash值this.current = location.hash.slice(1,)console.log(this.routesMap, '555');// console.log(this.current);//当前path路径}
}VueRouter.install = function (_Vue) {//1、保存VueVue = _Vue//2、将以下代码延迟到vue实例初始化完毕执行Vue.mixin({beforeCreate() {//判断如果有router属性就执行下方代码if (this.$options.router) {Vue.prototype.$router = this.$options.router;}}})//创建全局组件router-link和router-view//创建router-link组件Vue.component('router-link', {props: {to: {type: String | Object,required: true}},render(h) {// h 有三个参数,第一个参数创建的元素,第二个参数元素具有的属性, 第三个参数元素的内容return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)}})//创建router-view组件Vue.component('router-view', {// 组件要重新渲染,必须要让数据发生改变的时候,重新去执行render函数render(h) {const router = this.$routerconsole.log(router.routesMap, 'routesMap');let component = router.routesMap[router.current];//组件console.log(component, 'component');//获取到外部传递的路由表return h(component)}})
}export default VueRouter;
接下来实现历史路由模式
我们知道历史路由模式和hash路由模式区别是域名是否存在#因此我们可以通过在路由文件中定义mode属性判断是历史路由模式还是hash路由模式。
先大白话来说一下大概的实现思路(hash和history实现区别):
1、我们通过我们定义的mode判断是hash路由模式还是history路由模式,router-link点击跳转的时候如果是hash路由模式,我们通过render方法返回我们渲染到浏览器域名的数据,hash模式是带#的,因此我们定义href属性需要+#,但是history模式是不带#的,因此我们不需要加#。其次,因为hash模式自动检测我们的域名,因此我们实现历史模式需要手动加一个点击事件,并且需要阻止a链接默认跳转的行为(preventDefault取消默认行为),并且通过pushState方法,实现跳转,这是历史路由模式的方法pushState(obj,title,url),最后设置我们的路由当前path路径为我们跳转的路径。
2、当我们定义好它的router-link全局组件以后,这时我们hash路由模式和history路由模式已经可以基本实现,hash模式带#,history路由模式不带#。但是我们仍要定义一个监听历史路由模式变化的监听事件。这里我们通过mode判断是什么模式从而监听不同的事件,历史路由模式监听需要通过监听popState方法,来判断是否发生变化,它坚挺的是我们浏览器的前进后退的变化。
历史路由模式监听事件为hashchange方法,这个事件是用来监听我们hash值的变化,通过设置两个方法从而设置我们当前路径为对应的path路径。hash模式会通过slice剪切到#,历史路由模式则通过location.pathname获取到当前path路径。
实现如下:
let Vue;class VueRouter {constructor(options) {//保存选项this.options = options;// console.log(options, '路由数据');// 定义一个响应式的变量current,保存当前的hash值let url = location.hash.slice(1,) || '/';// defineReactive定义响应式的对象Vue.util.defineReactive(this, 'current', url)//判断当前的路由模式if (this.options.mode === 'hash') {// hashchange事件来监听hash值的变化, 注意this指向问题addEventListener('hashchange', this.changeHash.bind(this))} else if (this.options.mode === 'history') {// history模式监听的事件叫做:popstate, 监听的是浏览器左上角的两个小箭头的变化addEventListener('popstate', this.changeHistory.bind(this))}this.routesMap = {};//对routes中的对象做一个映射:routesMap = {'/home':component,'/list': component}this.options.routes.forEach(route => {this.routesMap[route.path] = route.component})}//监听hash值变化的函数changeHash() {//通过location.hash来获取到hash值this.current = location.hash.slice(1,)console.log(this.routesMap, '555');// console.log(this.current);//当前path路径}// historychangeHistory() {// console.log(location.pathname)this.current = location.pathname;}}
VueRouter.install = function (_Vue) {//1、保存VueVue = _Vue//2、将以下代码延迟到vue实例初始化完毕执行Vue.mixin({beforeCreate() {//判断如果有router属性就执行下方代码if (this.$options.router) {Vue.prototype.$router = this.$options.router;}}})//创建全局组件router-link和router-view//创建router-link组件Vue.component('router-link', {props: {to: {type: String | Object,required: true}},render(h) {// h 有三个参数,第一个参数创建的元素,第二个参数元素具有的属性, 第三个参数元素的内容const router = this.$router;if (router.options.mode === 'hash') {return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)} else if (router.options.mode === 'history') {return h('a', {attrs: { href: this.to },on: {'click': ev => {// 1. 阻止a链接的默认跳转行为ev.preventDefault()// 2. 调用pushState方法来实现跳转: pushState(obj, title, url)history.pushState({}, '', this.to)// 3. 设置current的值router.current = this.to;}}}, this.$slots.default)}}})//创建router-view组件Vue.component('router-view', {// 组件要重新渲染,必须要让数据发生改变的时候,重新去执行render函数render(h) {const router = this.$routerconsole.log(router.routesMap, 'routesMap');let component = router.routesMap[router.current];//组件console.log(component, 'component');//获取到外部传递的路由表return h(component)}})
}
这一期我们讲解了,如何实现一个基本的hash路由模式以及history路由模式,但是我们通常使用情况下路由嵌套通过一级路由下定义children属性来定义二级路由,这并没有实现,下一期来实现路由嵌套。
相关文章:
vue中router路由的原理?两种路由模式如何实现?(vue2) -(上)
平时我们编写路由时,通常直接下载插件使用,在main.js文件中引入直接通过引入vue-router中的Router通过Vue.use使用以后定义一个routeMap数组,里边是我们编写路由的地方,最后通过实例化一个 Router实例 将routes我们定义的routeMao…...
消息队列(3) -封装数据库的操作
前言 上一篇博客我们写了, 关于交换机, 队列,绑定, 写入数据库的一些建库建表的操作 这一篇博客中,我们将建库建表操作,封装一下实现层一个类来供上层服务的调用 , 并在写完该类之后, 测试代码是否完整 实现封装 在写完上述的接口类 与 xml 后, 我们想要 创建一个类 ,来调用…...
PostgreSQL中根据时间段范围查询数据,如19:29:10到20:29:10范围内的数据,排除年月日
数据格式如下 问题描述 我的SQL语句条件是 WHERE (TO_CHAR(cti.binder_gen_time, YYYY-MM-DD HH:mm:ss) > 19:29:10 AND TO_CHAR(cti.binder_gen_time, YYYY-MM-DD HH:mm:ss) < 20:29:10)为什么我数据的时间是2023-07-20 17:58:29也能被查出来? 问题解决…...
【二分+贪心】CF1665 C
Problem - C - Codeforces 题意: 思路: 一开始想太简单wa6了 只想到先感染大的分量,然后最后把最大的分量剩下的染色 但是可能会有别的分量更大(因为最后给最大的染色之后可能不再是最大的) 可以用堆维护…...
【Wamp】安装 | 局域网内设备访问
安装教程: https://wampserver.site/article/1.html 下载 https://www.wampserver.com/en/ 安装路径上不能有中文 安装好之后图标呈绿色 放入网页文件 将网页文件放置于wamp文件夹的www子文件夹 例如:\Wamp\program\www 修改http端口 WAMP服务器…...
【golang】类型推断和变量重声明
类型推断是一种编程语言在编译期自动解释表达式类型的能力。 1.Go语言的类型推断可以带来哪些好处? 在写代码时,我们通过使用Go语言的类型推断会节省敲击次数,而节省下来的键盘敲击次数几乎可以忽略不记。但它真正的好处,往往会…...
“算法详解”系列第3卷贪心算法和动态规划出版
“算法详解”系列图书共有4卷,目前1到3卷已经出版。最新出版的是第3卷—贪心算法和动态规划。 算法详解 卷3 贪心算法和动态规划 “算法详解”系列图书共有4卷,本书是第3卷—贪心算法和动态规划。其中贪心算法主要包括调度、最小生成树、集群、哈夫曼编…...
CSS前端开发指南:创造精美的用户界面
简介: 《CSS前端开发指南:创造精美的用户界面》是一本旨在帮助读者掌握CSS技术,实现令人惊叹的前端用户界面的实用指南。无论您是初学者还是有经验的开发者,本书都将为您提供全面的知识和实用技巧,帮助您创建引人注目…...
代数学与理论物理中常见的群
代数学与理论物理中常见的群 代数学与理论物理中常见的群 四阶群 六阶群 对称群 二维转动群 三维转动群 三维正交群 群 O3群...
解析xml文件,获取需要的数据并写入txt文件中
_ 话不多说!直接上代码!_ 1、XmlUtil.java xml解析工具类 public class XmlUtil {private static String dicName "";private static String dicValue "";// 用于存储需要的数据private static List<Map<String, Str…...
JavaScript基础 第三天
1.for循环 2.数组的基本使用和操作 3.数组排序 一.for循环 ① 语法:把声明起始值,循环条件,变量值写到一起,让人一目了然 for(变量起始值;终止条件;变量变化量) {// 循环体 }举例: for (let i 0; i < 100; i)…...
2.Redis部署到Windows服务器
1.下载安装包 Redis官网没有提供Windows的安装包,可以去gitHub或者网上去下载Redis的Windows安装包。 2.Redis部署到服务器 将Redis整个文件夹放到服务器的指令目录,然后进行修改Redis的配置文件 redis.windows.conf,修改里面的配置项 1.b…...
【修正-高斯拉普拉斯滤波器-用于平滑和去噪】基于修正高斯滤波拉普拉斯地震到达时间自动检测研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Go语言基础: 有参函数Func、Map、Strings详细案例教程
目录标题 一、Variadic Functions1.Syntax2.Examples and understanding how variadic functions work3.Slice arguments vs Variadic arguments 仅改变可变参数4.Gotcha 二、Map1.Create a Map2.Retrieving value for a key from a map3.Checking if a key exists4.Iterate ov…...
JDBC连接数据库如何实现你会吗???
1.首先建立一个maven项目。。。详细过程来了哇 还没有安装maven的童鞋可以看这里:maven的下载安装与配置环境变量!!!(全网最详细)_明天更新的博客-CSDN博客 有很多小伙伴就有疑问啦,难道我直接…...
C#与C++交互(2)——ANSI、UTF8、Unicode文本编码
【前言】 我们知道计算机上只会存储二进制的数据,无论文本、图片、音频、视频等,当我们将其保存在计算机上时,都会被转成二进制的。我们打开查看的时候,二进制数据又被转成我们看得懂的信息。如何将计算机上的二进制数据转为我们…...
SQLSTATE[42000]: this is incompatible with sql_mode=only_full_group_by in
执行 SELECT *FROM test WHERE id>1 GROUP BY name having AVG(age)>10 ORDER BY id desc limit 1 提示错误 Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause…...
企业权限管理(五)-订单分页
订单分页查询 PageHelper介绍 PageHelper是国内非常优秀的一款开源的mybatis分页插件,它支持基本主流与常用的数据库,例如mysql、oracle、mariaDB、DB2、SQLite、Hsqldb等。 PageHelper使用 集成 引入分页插件有下面2种方式,推荐使用 Maven …...
Blender如何给fbx模型添加材质贴图并导出带有材质贴图的模型
推荐:使用 NSDT场景编辑器快速助你搭建可二次编辑的3D应用场景 此教程适合新手用户,专业人士直接可直接绕路。 本教程中介绍了利用Blender建模软件,只需要简单几步就可以为模型添加材质贴,图,并且导出带有材质的模型文…...
MySQL不走索引的情况分析
未建立索引 当数据表没有设计相关索引时,查询会扫描全表。 create table test_temp (test_id int auto_incrementprimary key,field_1 varchar(20) null,field_2 varchar(20) null,field_3 bigint null,create_date date null );expl…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
