美文网首页
手写vue-router

手写vue-router

作者: 邓立_全栈UncleLi | 来源:发表于2021-06-12 18:23 被阅读0次

    今天刚好有时间,最近也在观察vue3新特性,抽空玩一玩嵌套路由的vue-router,直接上代码

    项目目录结构

    目录结构

    代码展示

    • app.vue
    <template>
      <div id="app">
        <div>
          <router-link to="/">Index</router-link> |
          <router-link to="/person">Person</router-link> |
          <router-link to="/person/info">PersonInfo</router-link>
        </div>
        <!-- 一级路由 -->
        <router-view />
      </div>
    </template>
    
    <style>
    #app{
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    </style>
    
    • Index.vue
    <template>
      <div class="index">
        <h1>this is index page</h1>
      </div>
    </template>
    
    • Person.vue
    <template>
      <div class="person">
        <h1>this is person page</h1>
         <!-- 二级路由 -->
        <router-view />
      </div>
    </template>
    
    • PersonInfo.vue
    <template>
      <div class="personInfo">
        <h2>this is personInfo page</h2>
      </div>
    </template>
    

    js文件

    • main.js
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    
    • babel.config.js
    • 需要添加babel依赖支持新语法,如可选链
    • npm install --save-dev @babel/core @babel/cli
    • npm install --save-dev @babel/plugin-proposal-optional-chaining
    module.exports = {
      presets: [
        '@babel/preset-env'
      ],
      plugins: ['@babel/plugin-proposal-optional-chaining']
    }
    
    • router目录下文件
    • index.js
    import Vue from "vue";
    import VueRouter from "./vue-router";
    import Index from "../views/Index.vue";
    import Person from "../views/Person.vue";
    import PersonInfo from "../views/PersonInfo.vue";
    
    Vue.use(VueRouter);
    
    const routes = [
      {
        path: "/",
        name: "Index",
        component: Index
      },
      {
        path: "/person",
        name: "Person",
        component: Person,
        children:[
          {
            path: "/person/info",
            name: "PersonInfo",
            component: PersonInfo
          }
        ]
      }
    ];
    
    const router = new VueRouter({
      routes
    });
    
    export default router;
    
    • vue-router.js
      这里先借助Vue的工具Vue.util.defineReactive实现数据响应式,后续再手撕这个库
    import routerLink from "./router-link";
    import routerView from "./router-view";
    
    let Vue;
    class VueRouter {
      constructor(options) {
        this.$options = options
    
        this.current = window.location.hash.slice(1) || "/"
    
        // 设置响应式数组数据
        Vue.util.defineReactive(this, "routerArray", [])
    
        // 监听hash值变化
        window.addEventListener("hashchange", this.hashChange.bind(this))
    
        this.getRouterArray()
      }
    
      hashChange() {
        this.current = window.location.hash.slice(1) || "/"
        this.routerArray = []
        this.getRouterArray()
      }
    
      getRouterArray(routes) {
        routes = routes || this.$options.routes
        for (const route of routes) {
          if (this.current === '/' && route.path === '/') {
            this.routerArray.push(route)
            return
          }
    
          if (this.current.indexOf(route.path) !== -1 && route.path !== '/') {
            this.routerArray.push(route)
            if (route.children) {
              // 递归子路由
              this.getRouterArray(route.children)
            }
            return
          }
        }
      }
    }
    
    VueRouter.install = function(_Vue) {
      Vue = _Vue
    
      // Vue全局混入,等new Vue中的router实例创建之后挂载到Vue上
      Vue.mixin({
        beforeCreate() {
          if (this.$options.router) {
            Vue.prototype.$router = this.$options.router
          }
        },
      });
    
      // 注册router-link和router-view全局组件
      Vue.component("router-link", routerLink)
      Vue.component("router-view", routerView)
    }
    
    export default VueRouter
    
    • router-link.js
    export default {
      props: {
        to: {
          type: String,
          required: true
        }
      },
      render(h) {
        return h(
          "a",
          {
            attrs: {
              href: "#" + this.to,
            },
          },
          this.$slots.default
        );
      }
    };
    
    • router-view.js
    export default {
      render(h) {
        // 设置嵌套路由标识
        this.$vnode.data.rv = true
    
        // 嵌套路由设置深度
        let depth = 0
        let parent = this.$parent
        while (parent) {
    
          // 上级还有嵌套路由标识rv为true的,深度加一
          if (parent.$vnode?.data?.rv) {
            depth++
          }
          parent = parent.$parent
        }
    
        // 简单处理
        const route = this.$router.routerArray[depth]
        return h(route?.component);
      }
    };
    
    • 效果图


      效果图

    好了,今天就玩到这里了,下次再玩别的哈

    相关文章

      网友评论

          本文标题:手写vue-router

          本文链接:https://www.haomeiwen.com/subject/uaaveltx.html