前言

路由是用来跟后端服务器进行交互的一种方式,通过不同的路径来请求不同的资源,请求不同的页面是路由的其中一项功能。VueRouter则是Vue的路由管理器

VueRouter本质

根据"不同的hash值"或者"不同的路径地址", 将不同的内容渲染到router-view中 所以实现VueRouter的核心关键点就在于如何监听'hash'或'路径'的变化, 再将不同的内容写到router-view

popstate监听历史记录点

vue-router 的 history 模式是使用浏览器的 history state 来实现的,history state 是通过 History 对象来操作的。 popstate 事件是通过 window.addEventListener('popstate') 进行注册的。但触发条件需要满足下面两点:

  1. 点击浏览器的【前进】【后退】按钮,或者调用 history 对象的 backforwardgo 方法
  2. 之前调用过 history 对象的 replaceState 或 pushState 方法
<a onclick="go('/home')">首页</a>
<a onclick="go('/about')">关于</a>
<div id="html"></div>
function go(path) {// console.log(path);history.pushState(null, null, path);document.querySelector('#html').innerHTML = path;
}
window.addEventListener('popstate', ()=>{console.log('点击了前进或者后退', location.pathname);document.querySelector('#html').innerHTML = location.pathname;
})

hashchange 事件

  • 当URL的片段标识符更改时,将触发hashchange事件(跟在#符号后面的URL部分,包括#符号)
  • hashchange事件触发时,事件对象会有hash改变前的URL(oldURL)hash改变后的URL(newURL)两个属性
window.addEventListener('hashchange', ()=>{// console.log('当前的hash值发生了变化');let currentHash = location.hash.slice(1);document.querySelector('#html').innerHTML = currentHash;
})

VueRouter结构

src-> router-> index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'Vue.use(VueRouter)const routes = [{path: '/home',name: 'Home',component: Home},{path: '/about',name: 'About',component: About}
]const router = new VueRouter({mode: 'history', base: process.env.BASE_URL,routes
})export default router

提取路由信息

创建一个新的js文件(myVue-Router.js),搭建基本的路由信息框架

class VueRouter {constructor(options){this.mode = options.mode || 'hash';this.routes = options.routes || [];this.routesMap = this.createRoutesMap();}createRoutesMap(){return  this.routes.reduce((map, route)=>{map[route.path] = route.component;return map;}, {})}
}
VueRouter.install = (Vue, options)=>{}
export default VueRouter;

初始化路由信息

class VueRouteInfo {constructor(){this.currentPath = null;}
}
class VueRouter {constructor(options){this.mode = options.mode || 'hash';this.routes = options.routes || [];// 提取路由信息this.routesMap = this.createRoutesMap();// 记录当前路由this.routeInfo = new VueRouteInfo();// 初始化默认的路由信息this.initDefault();}initDefault(){if(this.mode === 'hash'){// 1.判断打开的界面有没有hash, 如果没有就跳转到#/if(!location.hash){location.hash = '/';}// 2.加载完成之后和hash发生变化之后都需要保存当前的地址window.addEventListener('load', ()=>{this.routeInfo.currentPath = location.hash.slice(1);});window.addEventListener('hashchange', ()=>{this.routeInfo.currentPath = location.hash.slice(1);console.log(this.routeInfo);});}else{// 1.判断打开的界面有没有路径, 如果没有就跳转到/if(!location.pathname){location.pathname = '/';}// 2.加载完成之后和history发生变化之后都需要保存当前的地址window.addEventListener('load', ()=>{this.routeInfo.currentPath = location.pathname;});window.addEventListener('popstate', ()=>{this.routeInfo.currentPath = location.pathname;console.log(this.routeInfo);});}}createRoutesMap(){return  this.routes.reduce((map, route)=>{map[route.path] = route.component;// { /home: Home }return map;}, {})}
}
VueRouter.install = (Vue, options)=>{}
export default VueRouter;

注入全局属性

VueRouter.install = (vm, options)=>{vm.mixin({beforeCreate(){if(this.$options && this.$options.router){this.$router = this.$options.router;this.$route = this.$router.routeInfo;// 实时监听路由变化vm.util.defineReactive(this, 'xxx', this.$router);}else{this.$router = this.$parent.$router;this.$route = this.$router.routeInfo;}}});
}

自定义RouterLink

vm.component('router-link', {props: {to: String},render(){// console.log(this._self.$router.mode);let path = this.to;if(this._self.$router.mode === 'hash'){path = '#' + path;}return <a href={path}>{this.$slots.default}</a>}
});

自定义RouterView

vm.component('router-view', {render(h){let routesMap = this._self.$router.routesMap;let currentPath = this._self.$route.currentPath;let currentComponent = routesMap[currentPath];return h(currentComponent);}
});

完整示例

App.vue

<template><div id="app"><div id="nav"><router-link to="/home">Home</router-link> |<router-link to="/about">About</router-link></div><router-view></router-view></div>
</template>

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'Vue.config.productionTip = falsenew Vue({router,render: h => h(App)
}).$mount('#app')

src-> router-> index.js 看上面的VueRouter结构

src-> router-> myVue-Router.js

class VueRouteInfo {constructor(){this.currentPath = null;}
}
class VueRouter{constructor(options){this.mode = options.mode || 'hash';this.routes = options.routes || [];// 1、提取路由信息this.routesMap = this.createRoutesMap();this.routeInfo = new VueRouteInfo();// 2、初始化默认的路由信息this.initDefault();}createRoutesMap(){return this.routes.reduce((map, route) =>{// 组件作为key返回map[route.path] = route.component;return map;},{})}initDefault(){if (this.mode === 'hash'){// 1) 判断打开的界面有没有hash, 如果没有就跳转到#/if (!location.hash){location.hash = '/'}// 2) 加载完成之后和hash发生变化之后都需要保存当前的地址window.addEventListener('load', ()=>{this.routeInfo.currentPath = location.hash.slice(1);});window.addEventListener('hashchange', ()=>{this.routeInfo.currentPath = location.hash.slice(1)})} else {// 1) 判断打开的界面有没有路径, 如果没有就跳转到/if (!location.pathname){location.pathname = '/'}// 2)加载完成之后和history发生变化之后都需要保存当前的地址window.addEventListener('load', ()=>{this.routeInfo.currentPath = location.pathname});window.addEventListener('popstate', ()=>{this.routeInfo.currentPath = location.pathname;});}}
}
VueRouter.install = (vm, options) =>{// 3、全局注入属性vm.mixin({beforeCreate() {if (this.$options && this.$options.router){this.$router = this.$options.router;this.$route = this.$router.routeInfo;// 实时监听路由变化vm.util.defineReactive(this, 'xxx', this.$router);} else {this.$router = this.$parent.$router;this.$route = this.$router.routeInfo;}}});// 4、自定义路由组件vm.component('router-link', {props: {to: String},render() {let path = this.to;if(this._self.$router.mode === 'hash'){path = '#' + path;}return<a href={path}>{this.$slots.default}</a>}})vm.component('router-view', {render(h) {let routerMap = this._self.$router.routesMap;let currentPath = this._self.$route.currentPath;let currentComponent = routerMap[currentPath];return h(currentComponent)}})
};
export default VueRouter

手写简易VueRouter相关推荐

  1. 手写简易版链表及原理分析

    好多人都觉得为什么要自己写这样的数据结构,变成里面不是有吗?为什么要去写,有这个疑问,其实这个疑问这我的脑海中也存在了很长一段时间,本人是学习java编程的,直接看java的集合框架不行吗?这个时候如 ...

  2. 解鞍卸甲——手写简易版Spring框架(终):使用三级缓存解决循环依赖问题

    什么是三级缓存 按照目前我们实现的 Spring 框架,是可以满足一个基本需求的,但如果你配置了A.B两个Bean对象互相依赖,那么立马会抛出 java.lang.StackOverflowError ...

  3. React,手写简易redux(二)- By Viga

    React,手写简易redux(二) 本章节会完成一个简易的redux实现 该系列内容会逐步实现简易的redux 使用技术栈:vite+react 该系列感谢@方应杭 的react教学视频 目录 实现 ...

  4. spring源码分析01-(前期准备)spring核心原理解析和手写简易spring

    1.本文主要介绍内容 本文会把Spring中核心知识点大概解释下.可以对Spring的底层有一个整体的大致了解.主要内容包括: 手写简易spring框架,帮助更好理解spring. 代码点击链接自取 ...

  5. 手写简易WEB服务器

    手写简易WEB服务器 今天我们来写一个类似于Tomcat的简易服务器.可供大家深入理解一下tomcat的工作原理,本文仅供新手参考,请各位大神指正! 首先我们要准备的知识是: Socket编程 HTM ...

  6. 5 拦截器拦截请求路由_手写简易版axios拦截器,实现微信小程序wx.request的封装与拦截...

    前言: axios是一个功能强大的网络请求库,其中拦截器又是axios的精髓.在小程序的开发或者需要手动实现ajax的时候,没有实现对请求的拦截,开发的时候非常不方便,因此手写一个简易版的axios拦 ...

  7. 手写简易版 React 来彻底搞懂 fiber 架构

    React 16 之前和之后最大的区别就是 16 引入了 fiber,又基于 fiber 实现了 hooks.整天都提 fiber,那 fiber 到底是啥?它和 vdom 是什么关系? 与其看各种解 ...

  8. 手写简易Spring

    项目描述 项目描述: 手写Spring启动以及扫描流程 手下getBean()流程 手写Bean生命周期流程 手写依赖注入流程 手写BeanPOSTProcessor机制 手写AOP机制 0. Spr ...

  9. 手写简易版Vue源码之数据响应化的实现

    当前,Vue和React已成为两大炙手可热的前端框架,这两个框架都算是业内一些最佳实践的集合体.其中,Vue最大的亮点和特色就是数据响应化,而React的特点则是单向数据流与jsx. 笔者近期正在研究 ...

最新文章

  1. 【HDOJ】4343 Interval query
  2. [Leetcode] Flatten Binary Tree to Linked List 整平二叉树
  3. 图论--一般带花树匹配
  4. python3-matplotlib绘制散点图、绘制条形图
  5. 【操作系统】—内存的基本知识
  6. J.U.C - AQS
  7. Point to Raster 工作原理
  8. Windows Server 2012 R2磁盘分区
  9. TCP segment of a reassembled PDU,就这么简单!
  10. 测试自动化成本及投资回报率
  11. linux格式化硬盘 中断,linux格式化硬盘【调解方案】
  12. java中的坦克大战游戏设计报告论文_基于Android平台坦克大战游戏的设计与实现...
  13. 面试题,你为什么想做产品经理?
  14. C语言——计算阶层求和
  15. 线程的启动暂停和终止
  16. js文件 本地 上传服务器地址,js 本地文件同步服务器地址
  17. pandas 数据怎样实现行间计算
  18. windows服务器署站点,Windows Server配置学习心得-web服务器的搭建和部署,配置一个BLOG站点...
  19. 字节跳动技术岗官方解析 · 客户端篇:业务、中台、infra,这里的客户端究竟是怎样的?...
  20. 精灵图(sprite)CSS动画实现

热门文章

  1. 浅谈单工,半双工和全双工有何区别和联系?
  2. BMZCTF baby_dsa
  3. gta5nat严格怎么办_如何解决在游玩 GTA 在线模式时出现的 NAT 类型为“严格”(Strict)的错误...
  4. openwrt上用stun实现NAT类型检测
  5. 结合数学分析、复变函数重新看待指数函数e^{x}
  6. A.M. Best确认中国再保险(集团)股份有限公司及其子公司信用评级
  7. pull request
  8. Office 2007 Proofing 拼写检查失效解决方法
  9. matlab中gad,10大经典算法matlab代码以及代码详解【数学建模、信号处理】
  10. 幕维动画为你解答沉管三维动画有什么用