首页 > 其他 > 详细

手写vue-router(未实现嵌套路由)

时间:2021-06-08 20:36:19      阅读:24      评论:0      收藏:0      [点我收藏+]

手写vue-router(未实现嵌套路由)

需求分析:

  • 页面无刷新跳转 => ① hash模式,监听onhashchange事件 ; ② history api ,利用H5的history api,来实现页面不刷新跳转
  • 路由变化 => 页面更新 ==> 当前路由是个响应式数据
  • Vue.use(VueRouter) => 这是一个插件,需要实现一个静态方法 install
  • new VueRouter(options) => 需要实现一个class, Store
  • 在页面中可以通过 this.$router.xxx => 需要在页面上挂载$router
  • <router-link to="/about"></router-link><router-view></router-view>=> 需要实现2个全局组件: router-link,router-view
    • router-link组件,其实就是一个a标签,<a href="#/about">xxxxx</a>
    • router-view组件,是一个容器,直接把需要展示的组件塞进去就行。 路由跳转,router-view更新 => 这里的存储路由的变量是响应式的
  • 在main.js中,先①引入router实例的代码,再②new VueRouter()传入store参数
  • ① import router时,先进行Vue.use(VueRouter),即执行install方法,在该方法中进行$router挂载: this.$options.router && (Vue.prototype.$router = this.$options.router), 如果this.$options.router存在,就进行挂载。this.$options.router 这里是new Vue({router})传入的options,这个传参是在②中执行的,现在还在执行①的代码,就想把②的代码拿来用
  • 解决方案:
    + ① 挂载$router放在setTimeout中,延迟执行 => 代码low、延迟时间不可控
    + ② 使用Vue.mixin,在beforeCreate钩子函数中进行执行挂载,在beforeCreate执行时,new Vue({router})是一定执行了的,不然进不了这个钩子函数

代码实现

// 分析下 这个kRouter
// const router = new VueRouter({
//   routes,
// });
// 1. Vue.use(kRouter) => 插件,是个class,需要加入install方法
// 2. 在页面中可以使用 this.$router => 需要挂载到Vue根实例的原型上
// 3. 需要实现页面跳转,且页面不刷新 => 1. hash模式    2. history Api 是H5的方法
// 4. 需要实现2个自定义组件 router-link 、 router-view
// 5. router-link => a标签 <router-link to="/home"/>
// 6. router-view => 一个容器,可以自动刷新组件的容器,根据值不同,自动进行视图更新 => 响应式(数据变化=> 页面更新, 页面更新=> 数据变化)
// 7. 在main.js中,先①引入router实例的代码,再②new VueRouter()传入store参数
// => ①import router时,先进行Vue.use(VueRouter),即执行install方法,在该方法中进行$router挂载: this.$options.router && (Vue.prototype.$router = this.$options.router), 如果this.$options.router存在,就进行挂载。this.$options.router 这里是new Vue({router})传入的options,这个传参是在②中执行的,现在还在执行①的代码,就想把②的代码拿来用
// 解决方案:
// ① 挂载$router放在setTimeout中,延迟执行 => 代码low、延迟时间不可控
// ② 使用Vue.mixin,在beforeCreate钩子函数中进行执行挂载,在beforeCreate执行时,new Vue({router})是一定执行了的,不然进不了这个钩子函数

// 在kRouter类中需要使用传入的形参Vue,所以需要定义变量保存一下
let Vue;

class KRouter {
  constructor(options) {
    this.$options = options;
    // 实现页面不刷新跳转:用hash,监听hashchange事件
    // ① 获取当前hash值; ② 监听hashchange事件
    // 定义一个变量this.current,用来 1. 存储当前链接的hash值  2. 该current值发生变化,要求router-view组件内部的组件在页面是哪个随之更新 => 该current应该是个响应式数据
    // this.current = window.location.hash.slice(1) || "/";

    //Vue.util.defineReactive  是Vue隐藏的方法,是定义对象的某个属性为响应式数据
    Vue.util.defineReactive(this, "current", {
      get() {
        return window.location.hash.slice(1) || "/";
      },
    });

    // 获取routes对象,并将路由映射关系缓存起来
    this.routeMap = {};
    this.$options.routes.forEach((route) => {
      this.routeMap[route.path] = route;
    });

    window.addEventListener("hashchange", this.onHashchange.bind(this)); // 这里的回调函数的this指向window,需要改变this的指向
    window.addEventListener("load", this.onHashchange.bind(this));
  }

  onHashchange() {
    this.current = window.location.hash.slice(1) || "/";
  }

  static install(_Vue) {
    Vue = _Vue;
    // 在页面中可以使用 this.$router => 需要挂载到Vue根实例的原型上
    // 但是回到main.js中查看。先 import router, 然后再 用new Vue({router})进行vue实例化
    // 细看具体代码,import router时,router的index.js中,① 先进行 vue.use(VueRouter),即 kRouter的静态方法 install,install方法中要将 KRouter的实例挂载到Vue构造函数的原型上,即Vue.prototype.$router上,② 但main.js中,是先import,后才将KRouter实例router传入new Vue({router})中。 =>  先将 KRouter类的实例 router挂载在Vue实例上, 后才将KRouter类的实例 router传入Vue构造器中 => 这就不符合常理,先使用实例,后才将实例传入参数 =>  解决方法 : ① 使用实例的时候(即将实例挂载到Vue原型上),用setTimeout进行延迟 => 有点low,且这个延迟时间也不好控制     ② 使用mixin的beforeCreate的方法,KRouter这个插件,也是vue的一个组件,同样可以使用mixin进行混入,在beforeCreate的生命周期中进行挂载的话,new Vue()一定是已经进行实例化的了。
    Vue.mixin({
      beforeCreate() {
        // if (this.$options.router) 这个判断条件,是用来判断时Vue的跟组件实例化,还是,vue组件的实例化。 挂载$router,只需要在跟组件实例化的时候进行挂载就可以了,不需要所有的vue组件都进行挂载
        if (this.$options.router) {
          Vue.prototype.$router = this.$options.router;
        }
      },
    });

    // Vue.componet(‘组件名‘, 组件配置对象)
    // <router-link to="/about">About</router-link>  =>  <a href="#/about">xxxxxx</a>
    // to=to="/about" 是组件的props,需要在自定义组件上添加props字段
    Vue.component("router-link", {
      // options: ① data(){return {}},props: [],template:‘<div></div>‘ 数据属性data 必须是一个返回值的函数。因为组件可能被用来创建多个实例,如果data是一个纯粹的对象,则所有的实例将会共享引用同一个数据对象!而通过data函数返回对象,则每次创建一个新实例以后,能调用data函数,返回一个全新的data对象
      // options: ② render:h=>h(tag, props, children)
      props: {
        to: {
          type: String,
          required: true,
        },
      },
      // props: [‘to‘],
      render(h) {
        // 注意: 这里不能使用箭头函数。 ? render:h => {} 使用箭头函数,这里的this是指向KRouter这个构造函数。而我们要使用的是,this能指向使用这个组件的组件实例
        // children: this.$slots.default 默认插槽的内容
        return h("a", {attrs: {href: "#" + this.to}}, this.$slots.default);
      },
    });

    // <router-view></router-view>
    // 1. hash改变 => router-view内的视图跟着改变
    Vue.component("router-view", {
      render: function (h) {
        // 注意: 这里不能使用箭头函数。 ? render:h => {} 使用箭头函数,这里的this是指向KRouter这个构造函数。而我们要使用的是,this能指向使用这个组件的组件实例
        // console.log(this);
        const {current, routeMap} = this.$router;
        const comp = (!!routeMap[current] && routeMap[current].component) || null;
        // const comp = (!!this.$options.router && this.routeMap[this.current]) || null;
        return h(comp);
      },
    });
  }

}

export default KRouter;

手写vue-router(未实现嵌套路由)

原文:https://www.cnblogs.com/shine-lovely/p/14863877.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!