深入了解vue-router原理并实现一个小demo

目录
  • 插件编写的基本方法
  • 需求分析
    • 我们先看看vue-router的使用步骤
    • 由此我们看看vue-router内部做了什么?
  • 实现思路
    • 首先我们看看如何将$router挂载到组件上​
    • ​如何实现那两个路由组件
      • 先来看看router-link
  • 完整demo代码
    • 总结

      插件编写的基本方法

      推荐大家先看看官方给出的插件使用和开发方法

      https://vuejs.bootcss.com/guide/plugins.html​

      需求分析

      我们先看看vue-router的使用步骤

      1.use

      Vue.use(VueRouter)

      注意⚠️:

      Vue.use()主要是调用插件内部的install方法,并将Vue实例作为参数传入​

      2.new 一个router实例

      const router = new VueRouter({
        // 实例化router传入的参数
        mode: 'history',
        base: process.env.BASE_URL,
        routes
      })
      

      3.new Vue() ,把实例放在vue的配置项里面

      new Vue({
        router, // 注意router的实例也往里传
        render: h => h(App)
      }).$mount('#app')
      

      4.使用路由组件<router-view/><router-link></router-link>或者在组件中使用this.$router

      由此我们看看vue-router内部做了什么?

      将$router挂载到全局上实现并声明了两个组件:<router-view/><router-link></router-link>

      实现思路

      首先我们看看如何将$router挂载到组件上​

      let Vue; // 保存vue的构造函数,避免打包将其打进去
      VueRouter.install = function (_Vue) {
        Vue = _Vue;
        console.log("options", Vue.$options);
        Vue.mixin({
          beforeCreate() {
            console.log("inner", this);
            console.log(" this.$options.router", this.$options.router);
            if (this.$options.router) {
              Vue.prototype.$router = this.$options.router;
            }
          },
        });
        console.log("end");
      };
      

      可以看到:

      1、第一次执行的时候,即在Vue.use(Router)时,还没有实例化vue(因为Vue.use()发生在 new Vue()之前),所以Vue.$option本身是拿不到的(ps: option就是new Vue()时传入的参数,router也往里面传),此时既然拿不到router的实例,所以不能直接在install方法里面挂载;

      ​2、我们可以在use的时候做一个全局混入,在合适的时间点,获取到Vue根实例配置项中的router实例, 执行挂载。紧接着在new Vue()根实例创建的时候,因为注入了router实例,所以再执行全局混入(mixin)中的生命周期时,这个时候根实例的配置项this.$options已经包含了router实例,可以此时把router挂载到Vue的原型上。之后所有Vue实例扩展来的VueCompont都可以通过this.$router访问到这个属性

      ​如何实现那两个路由组件

      先看看路由组件如何使用

      <div id="app">
        <div id="nav">
          <!-- a标签控制跳转 -->
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>
        </div>
        <!-- 路由出口 -->
        <router-view />
      </div>
      
      

      由上面可以看出,点击router-link,就相当于点了a标签,然后a标签的href属性控制页面路由发生了变化;监听路由变化,然后仔router-view里面输出不同的模板;​

      先来看看router-link

      class VueRouter {
        constructor(options) {
          // 接受传入的参数
          this.$options = options;
          const initial = "/";
          // 将current变成响应式数据,
          //这样在hashchange的回掉中修改curent时,
          //用到current的router-view的render函数就会重新渲染
          Vue.util.defineReactive(this, "current", initial);
          // 监听路由变化
          window.addEventListener("hashchange", () => {
            // 获取当前url中的hash
            this.current = window.location.hash.slice(1);
          });
        }
      }
      
      VueRouter.install = function (_Vue) {
        Vue = _Vue;
        Vue.component("router-view", {
          render(h) {
            // 获取当前路由所对应的组件,然后把它渲染出来
            const { current, $options } = this.$router;
            // 这里要注意 我们传进来的routes是一个路由表,如下图一
            // 所以这里我们是找出匹配到当前current路由的项,然后直接渲染组件
            const route = $options.routes.find((item) => {
              return item.path === current;
            });
            let component = route ? route.component : null;
      
            return h(component);
          },
        });
      }
      
      

      ​再来看看router-view

      class VueRouter {
        constructor(options) {
          // 接受传入的参数
          this.$options = options;
          const initial = "/";
          // 将current变成响应式数据,
          //这样在hashchange的回掉中修改curent时,
          //用到current的router-view的render函数就会重新渲染
          Vue.util.defineReactive(this, "current", initial);
          // 监听路由变化
          window.addEventListener("hashchange", () => {
            // 获取当前url中的hash
            this.current = window.location.hash.slice(1);
          });
        }
      }
      
      VueRouter.install = function (_Vue) {
        Vue = _Vue;
        Vue.component("router-view", {
          render(h) {
            // 获取当前路由所对应的组件,然后把它渲染出来
            const { current, $options } = this.$router;
            // 这里要注意 我们传进来的routes是一个路由表,如下图一
            // 所以这里我们是找出匹配到当前current路由的项,然后直接渲染组件
            const route = $options.routes.find((item) => {
              return item.path === current;
            });
            let component = route ? route.component : null;
      
            return h(component);
          },
        });
      }
      
      

      图一

      完整demo代码

      // 我们要实现什么
      // 1、插件
      // 2、两个组件
      
      // 保存vue的构造函数,避免打包将其打进去
      let Vue;
      class VueRouter {
        constructor(options) {
          this.$options = options;
          const initial = "/";
          Vue.util.defineReactive(this, "current", initial);
          this.current = "/";
          window.addEventListener("hashchange", () => {
            // 获取当前url中的hash
            this.current = window.location.hash.slice(1);
          });
        }
      }
      
      // 参数1在Vue.use()调用时传进来,
      VueRouter.install = function (_Vue) {
        Vue = _Vue;
        console.log("options", this);
      
        // 全局混入
        // 目的:延迟下面的逻辑 到 router创建完毕并且附加到选项上时才执行
        Vue.mixin({
          // 在每个组件创建实例时都会执行
          beforeCreate() {
            // this.$options.router ;即new Vue时放进去的router实例
            if (this.$options.router) {
              Vue.prototype.$router = this.$options.router;
            }
          },
        });
      
        // 注册并且实现两个组件
        Vue.component("router-link", {
          props: {
            to: {
              required: true,
            },
          },
          render(h) {
            return h(
              "a",
              {
                attrs: { href: "#" + this.to },
              },
              this.$slots.default
            );
          },
        });
        Vue.component("router-view", {
          render(h) {
            // 获取当前路由所对应的组件,然后把它渲染出来
            const { current, $options } = this.$router;
            const route = $options.routes.find((item) => {
              return item.path === current;
            });
            let component = route ? route.component : null;
      
            return h(component);
          },
        });
      };
      
      export default VueRouter;
      
      

      总结

      本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!     

      本文转自网络,如有侵权请联系客服删除。