• 手写简易VueRouter


    前言

    路由是用来跟后端服务器进行交互的一种方式,通过不同的路径来请求不同的资源,请求不同的页面是路由的其中一项功能。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 方法
    1. <a onclick="go('/home')">首页a>
    2. <a onclick="go('/about')">关于a>
    3. <div id="html">div>
    1. function go(path) {
    2. // console.log(path);
    3. history.pushState(null, null, path);
    4. document.querySelector('#html').innerHTML = path;
    5. }
    6. window.addEventListener('popstate', ()=>{
    7. console.log('点击了前进或者后退', location.pathname);
    8. document.querySelector('#html').innerHTML = location.pathname;
    9. })

    hashchange 事件 

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

    VueRouter结构

    src-> router-> index.js 

    1. import Vue from 'vue'
    2. import VueRouter from 'vue-router'
    3. import Home from '../views/Home.vue'
    4. import About from '../views/About.vue'
    5. Vue.use(VueRouter)
    6. const routes = [
    7. {
    8. path: '/home',
    9. name: 'Home',
    10. component: Home
    11. },
    12. {
    13. path: '/about',
    14. name: 'About',
    15. component: About
    16. }
    17. ]
    18. const router = new VueRouter({
    19. mode: 'history',
    20. base: process.env.BASE_URL,
    21. routes
    22. })
    23. export default router

    提取路由信息 

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

    1. class VueRouter {
    2. constructor(options){
    3. this.mode = options.mode || 'hash';
    4. this.routes = options.routes || [];
    5. this.routesMap = this.createRoutesMap();
    6. }
    7. createRoutesMap(){
    8. return this.routes.reduce((map, route)=>{
    9. map[route.path] = route.component;
    10. return map;
    11. }, {})
    12. }
    13. }
    14. VueRouter.install = (Vue, options)=>{
    15. }
    16. export default VueRouter;

    初始化路由信息 

    1. class VueRouteInfo {
    2. constructor(){
    3. this.currentPath = null;
    4. }
    5. }
    6. class VueRouter {
    7. constructor(options){
    8. this.mode = options.mode || 'hash';
    9. this.routes = options.routes || [];
    10. // 提取路由信息
    11. this.routesMap = this.createRoutesMap();
    12. // 记录当前路由
    13. this.routeInfo = new VueRouteInfo();
    14. // 初始化默认的路由信息
    15. this.initDefault();
    16. }
    17. initDefault(){
    18. if(this.mode === 'hash'){
    19. // 1.判断打开的界面有没有hash, 如果没有就跳转到#/
    20. if(!location.hash){
    21. location.hash = '/';
    22. }
    23. // 2.加载完成之后和hash发生变化之后都需要保存当前的地址
    24. window.addEventListener('load', ()=>{
    25. this.routeInfo.currentPath = location.hash.slice(1);
    26. });
    27. window.addEventListener('hashchange', ()=>{
    28. this.routeInfo.currentPath = location.hash.slice(1);
    29. console.log(this.routeInfo);
    30. });
    31. }else{
    32. // 1.判断打开的界面有没有路径, 如果没有就跳转到/
    33. if(!location.pathname){
    34. location.pathname = '/';
    35. }
    36. // 2.加载完成之后和history发生变化之后都需要保存当前的地址
    37. window.addEventListener('load', ()=>{
    38. this.routeInfo.currentPath = location.pathname;
    39. });
    40. window.addEventListener('popstate', ()=>{
    41. this.routeInfo.currentPath = location.pathname;
    42. console.log(this.routeInfo);
    43. });
    44. }
    45. }
    46. createRoutesMap(){
    47. return this.routes.reduce((map, route)=>{
    48. map[route.path] = route.component;
    49. // { /home: Home }
    50. return map;
    51. }, {})
    52. }
    53. }
    54. VueRouter.install = (Vue, options)=>{
    55. }
    56. export default VueRouter;

    注入全局属性

    1. VueRouter.install = (vm, options)=>{
    2. vm.mixin({
    3. beforeCreate(){
    4. if(this.$options && this.$options.router){
    5. this.$router = this.$options.router;
    6. this.$route = this.$router.routeInfo;
    7. // 实时监听路由变化
    8. vm.util.defineReactive(this, 'xxx', this.$router);
    9. }else{
    10. this.$router = this.$parent.$router;
    11. this.$route = this.$router.routeInfo;
    12. }
    13. }
    14. });
    15. }

    自定义RouterLink

    1. vm.component('router-link', {
    2. props: {
    3. to: String
    4. },
    5. render(){
    6. // console.log(this._self.$router.mode);
    7. let path = this.to;
    8. if(this._self.$router.mode === 'hash'){
    9. path = '#' + path;
    10. }
    11. return <a href={path}>{this.$slots.default}a>
    12. }
    13. });

     自定义RouterView

    1. vm.component('router-view', {
    2. render(h){
    3. let routesMap = this._self.$router.routesMap;
    4. let currentPath = this._self.$route.currentPath;
    5. let currentComponent = routesMap[currentPath];
    6. return h(currentComponent);
    7. }
    8. });

     完整示例

    App.vue

    main.js 

    1. import Vue from 'vue'
    2. import App from './App.vue'
    3. import router from './router'
    4. Vue.config.productionTip = false
    5. new Vue({
    6. router,
    7. render: h => h(App)
    8. }).$mount('#app')

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

    src-> router-> myVue-Router.js

    1. class VueRouteInfo {
    2. constructor(){
    3. this.currentPath = null;
    4. }
    5. }
    6. class VueRouter{
    7. constructor(options){
    8. this.mode = options.mode || 'hash';
    9. this.routes = options.routes || [];
    10. // 1、提取路由信息
    11. this.routesMap = this.createRoutesMap();
    12. this.routeInfo = new VueRouteInfo();
    13. // 2、初始化默认的路由信息
    14. this.initDefault();
    15. }
    16. createRoutesMap(){
    17. return this.routes.reduce((map, route) =>{
    18. // 组件作为key返回
    19. map[route.path] = route.component;
    20. return map;
    21. },{})
    22. }
    23. initDefault(){
    24. if (this.mode === 'hash'){
    25. // 1) 判断打开的界面有没有hash, 如果没有就跳转到#/
    26. if (!location.hash){
    27. location.hash = '/'
    28. }
    29. // 2) 加载完成之后和hash发生变化之后都需要保存当前的地址
    30. window.addEventListener('load', ()=>{
    31. this.routeInfo.currentPath = location.hash.slice(1);
    32. });
    33. window.addEventListener('hashchange', ()=>{
    34. this.routeInfo.currentPath = location.hash.slice(1)
    35. })
    36. } else {
    37. // 1) 判断打开的界面有没有路径, 如果没有就跳转到/
    38. if (!location.pathname){
    39. location.pathname = '/'
    40. }
    41. // 2)加载完成之后和history发生变化之后都需要保存当前的地址
    42. window.addEventListener('load', ()=>{
    43. this.routeInfo.currentPath = location.pathname
    44. });
    45. window.addEventListener('popstate', ()=>{
    46. this.routeInfo.currentPath = location.pathname;
    47. });
    48. }
    49. }
    50. }
    51. VueRouter.install = (vm, options) =>{
    52. // 3、全局注入属性
    53. vm.mixin({
    54. beforeCreate() {
    55. if (this.$options && this.$options.router){
    56. this.$router = this.$options.router;
    57. this.$route = this.$router.routeInfo;
    58. // 实时监听路由变化
    59. vm.util.defineReactive(this, 'xxx', this.$router);
    60. } else {
    61. this.$router = this.$parent.$router;
    62. this.$route = this.$router.routeInfo;
    63. }
    64. }
    65. });
    66. // 4、自定义路由组件
    67. vm.component('router-link', {
    68. props: {
    69. to: String
    70. },
    71. render() {
    72. let path = this.to;
    73. if(this._self.$router.mode === 'hash'){
    74. path = '#' + path;
    75. }
    76. return<a href={path}>{this.$slots.default}a>
    77. }
    78. })
    79. vm.component('router-view', {
    80. render(h) {
    81. let routerMap = this._self.$router.routesMap;
    82. let currentPath = this._self.$route.currentPath;
    83. let currentComponent = routerMap[currentPath];
    84. return h(currentComponent)
    85. }
    86. })
    87. };
    88. export default VueRouter

     

     

  • 相关阅读:
    【刷题笔记10.6】LeetCode:汉明距离
    MyBatis-Plus演绎:数据权限控制,优雅至极!
    如何逐步安装 AlmaLinux 9操作系统
    【智能优化算法】基于凌日算法求解单目标优化问题附matlab代码Transit Search Optimization Algorithm
    23-properties文件和xml文件以及dom4j的基本使用操作
    Spring Cloud Gateway:打造可扩展的微服务网关
    【Arduino板接口及应用】外部中断接口及应用:中断的概念,中断服务程序,中断优先级,中断嵌套,Arduino的外部中断引脚
    Java变量的特殊传递机制和实现细节
    node 之 express 框架(初级)
    Python 多线程 DNS 搜索性能优化
  • 原文地址:https://blog.csdn.net/weixin_45785873/article/details/126642262