WEB架构师学习笔记_Vue_02 手写一个路由

知识导航:

  • 路由重要知识点
  • 手写一个路由

知识点(建议最后看,放到是为了方便复习)
在这里插入图片描述

1. 路由重要知识点

vue-router对于写过vue的人想必是很熟悉了,所以这里忽略它的一些基本用法了。

1.1 动态路由

所谓动态路由就是类似那种restful接口对接形式,即以/demo/:id,即可匹配到demo路由同时可在demo路由下的组件中拿到传过来的id值。
示例:

路由配置中:
{
path: "/",
component: Home,
children: [
{ path: "", name: "home", component: List },
{ path: "/demo/:id", component: Demo},
]
}
发出链接组件中的router-link
<router-link to="/demo/1">...</router-link>

接收组件
{{$route.params.id}}
或者
将路由routes配置中更改为{ path: "/demo/:id", component: Demo,props:true}
这时候接收组件便可以通过props来接收参数了,例如{ props: ['id'] }

1.2 路由守卫

1.2.1 全局守卫

就是一些钩子,通过运行这些钩子的执行时期来做一些我们想要的控制。即每次发生路由的跳转变化执行一下我们所设置的某些逻辑

1.2.1.1 前置钩子(即前置守卫)

//to:即要进入的路由
//from:即要离开的路由
//next:它是一个函数,即可控制是否能够进入下个路由
//它的参数形式(1)next(); //默认路由(2)next(false); //阻止路由跳转(3)next({path:'/'}); //阻止默认路由,跳转到指定路径
router.beforeEach((to, from, next) = {
    if (to.path == "/login") {
        next();
    } else {
        next("/login")
    }
})

1.2.1.1 后置钩子(即后置守卫)

后置钩子用法和前置钩子基本一样,只不过它所接受的函数参数中没有了next

router.afterEach((to, from) => {
  //逻辑代码
})

1.2.2 路由独享

使用和全局类似。
示例:

        //登录模块
        path: '/login',
        component:Login,
        beforeEnter: (to, from, next) => {
            if (to.meta.needLogin && !$store.state.isLogin) {
        }

1.2.3 组件内的守卫

直接放进组件中的路由钩子。
即:(说明放在代码中)

export default {
  //出去时触发
  beforeRouteLeave(to, from, next) {
    next();
  },
 //进来时触发
  beforeRouteEnter(to, from, next) {
    next();
  },
   //它的场景是那种动态路由,如此组件匹配的是/demo/:id,即虽路由发生了变化但还是进到当前组件
  beforeRouteUpdate(to, from, next) {
    next();
  },
  data: {},
  method: {}
};

2. 手写一个路由插件

思路:回想我们在使用路由的时候进行了哪些配置。(ps只是基础实现功能)
观察

路由配置js文件中:
在这里插入图片描述

入口main.js文件:
在这里插入图片描述
通过观察可以知道我们接下来要做什么了。

  1. 写一个路由类
  2. 构造函数需要接受传过来的参数
  3. 要有一个path和组件信息字典即需要把参数整成{path:组件名}
  4. 需要有个响应式的东西用于存放url中的哈希值
  5. 需要监听url中哈希值的变化
  6. 需要做两个组件,router-link和 router-view
  7. 需要把它做成插件

1.开始

current用于存放哈希值借用vue的响应式数据,map用于存放path和组件信息的映射

class MyRouter {
    constructor(routes) {
        this.$routes = routes;
        this.map = {};
        this.app = new Vue({
            data: {
                current: '/'
            }
        })

    }

2.接下来先把它整成一个组件吧
首先我们要理解在路由配置js文件中Vue.use()。它的作用是安装插件,如果插件是一个对象则该对象必须提供install方法。如果插件是一个方法,那么这个方法便会被做为install方法。并且在调用install方法时,Vue会作为参数传进去。

即我们想要把上面写的东西搞成一个组件,就得实现一个install方法。

下面我们用了一下混入,即Vue.mixin({})。简单说一下它,它的作用是全局注册一个混入,即它会影响注册之后创建的每一个vue实例。既然可以影响每个vue实例,那我们可以搞一个vue的钩子。控制vue实例在它的生命周期中做些我们让它做的事情

MyRouter.install = function(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
        //判当前vue实例化是的初始选项是否有router,有就调用router的初始化方法
            if (this.$options.router) {
                this.$options.router.init();
            }
        }
    })
}

3.实现init方法

init() {
        // 初始化要做的事情:
        // 1.进行哈希值的监听
      	this.addEvent();
        // 2.做好字典映射
        this.creatMap(this.$routes);
        // 3.整好路由的两个专有组件
        this.creatCom();
    }

4.实现监听addEvent

  addEvent() {
  			哈希值给到current,current是响应式的即哈希值一变,所有的用到current的地方便 跟着变化
        window.addEventListener("hashchange", function() {
            // 哈希值一改变对应的current便需要改变,注意这里的this指向为window
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));
        window.addEventListener('load', function() {
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));

    }

5.实现path与组件信息的映射creatMap(routes);

   creatMap(routes) {
        routes.routes.forEach(item => {
            this.map[item.path] = item;
        });

    }

6.创键两个专有组件creatCom()

creatCom() {

        Vue.component("router-link", {


            props: {
                to: String
            },
            //就是渲染一个a标签
            render(h) {
                return h("a", {
                    attrs: {
                        href: "#" + this.to
                    }
                }, this.$slots.default)
            },
        });
        
        Vue.component("router-view", {
        //将组件渲染成真正的DOM视图
            render: (h) => {
                return h(this.map[this.app.current].component)
            }
        })
    }

测试:引用我们写的路由
在这里插入图片描述
效果:

在这里插入图片描述
在这里插入图片描述
路由完整代码:

// 思路
// 1.接收一个map的配置信息
// 2.监听哈希值的变化
// 3.哈希值一旦法师变化,重新渲染页面
let Vue;
class MyRouter {
    constructor(routes) {
        this.$routes = routes;
        this.map = {};
        this.app = new Vue({
            data: {
                current: '/'
            }
        })

    }
    init() {
        // 初始化要做的事情:
        // 1.进行哈希值的监听
        // 2.做好字典
        // 3.整好路由的两个专有组件
        this.addEvent();
        this.creatMap(this.$routes);
        this.creatCom();
    }
    addEvent() {
        window.addEventListener("hashchange", function() {
            // 哈希值一改变对应的current便需要改变,注意这里的this指向为window
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));
        window.addEventListener('load', function() {
            this.app.current = window.location.hash.slice(1) || "/";
        }.bind(this));

    }


    creatMap(routes) {
        routes.routes.forEach(item => {
            this.map[item.path] = item;
        });

    }
    creatCom() {

        Vue.component("router-link", {


            props: {
                to: String
            },
            render(h) {
                return h("a", {
                    attrs: {
                        href: "#" + this.to
                    }
                }, this.$slots.default)
            },
        });
        Vue.component("router-view", {
            render: (h) => {
                return h(this.map[this.app.current].component)
            }
        })
    }
}


// 写成一个插件
// 即实现一个install方法

MyRouter.install = function(_vue) {
    Vue = _vue;
    Vue.mixin({
        beforeCreate() {
            if (this.$options.router) {          
                this.$options.router.init();
            }
        }
    })
}

export default MyRouter

猜你喜欢

转载自blog.csdn.net/qq_41086511/article/details/106032235