zoukankan      html  css  js  c++  java
  • 手写vue-router(未实现嵌套路由)

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

    需求分析:

    • 页面无刷新跳转 => ① hash模式,监听onhashchange事件 ; ② history api ,利用H5的history api,来实现页面不刷新跳转
    • 路由变化 => 页面更新 ==> 当前路由是个响应式数据
    • Vue.use(VueRouter) => 这是一个插件,需要实现一个静态方法 install
    • new VueRouter(options) => 需要实现一个class, VueRouter
    • 在页面中可以通过 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})是一定执行了的,不然进不了这个钩子函数

    代码实现

    1. KRouter类的实现
    // 分析下 这个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;
    
    1. KRouter类的使用。index.js
    import Vue from 'vue'
    import VueRouter from './KRouter.js'
    import Home from '../views/Home.vue'
    
    Vue.use(VueRouter)
    
    const routes = [
      {path: "/",name: "Home",component: Home},
      {path: "/about",name: "About",component: () => import("../views/About.vue")},
    ]
    
    const router = new VueRouter({ routes })
    
    export default router
    
    1. main.js引用
    import Vue from "vue";
    import App from "./App.vue";
    import router from "./kRouter/index.js";
    
    new Vue({
      router,
      render: (h) => h(App),
    }).$mount("#app");
    
  • 相关阅读:
    分别改动Cube每一个面的贴图UV(Unity3D开发之十八)
    解决MAC下PHP连接MYSQL错误Warning: mysql_connect(): No such file or directory in conn.php
    什么是IaaS,PaaS和SaaS及其区别
    /lib/dracut/hooks/shutdown/30-dm-shutdown.sh
    openstack instance bootmgr is missing 问题 修复
    openStack aio nova service-list neutron ext-list
    CentOS7 iso封装语句
    開始EEPlat之旅
    重构摘要4_构筑測试体系
    线程及其创建的方式
  • 原文地址:https://www.cnblogs.com/shine-lovely/p/14863877.html
Copyright © 2011-2022 走看看