搞定一个简单的vue-router

148 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

前言

接触vue,总有一个绕不开的一个话题,就是它vue-router。要知道,现在的路由都是前端来做,而vue这种SPA项目的路由工作就是由vue-router来完成的。套用官网的一句话:它是和vue.js的核心深度集成,让单页面应用变得易如反掌,经过工作一年的实践,确实如此。

认识vue-router

vue项目中,由于是单页面应用,就是只有一个html文件,内容都是以组件的粒度去展示的。而页面中肯定不只一个地址,那么就需要将地址和组件联系起来,vue-router就是干这件事的。我们在vue-router中将路径和组件绑定在一起,然后告诉它在哪里渲染组件。当然也额外带了一些功能,例如:带参。

实现

vue-router接受一个object作为参数,里面包含了路由的模式(mode)和路由数组。所以我们需要在路由构造类里面有一个数组去保存路由数组。其次我们还得用一个变量去保存当前的地址。随后就是模式了,官网默认是hash模式。总结一下:

  • routes:用于保持传入到构造类中的路由数组
  • current:用于保存当前的地址
  • mode:指定当前的路由模式

第一步:我们先创建一个myRouter类和上述的基本变量:


class myRouter{
   constructor(options){
      this.current = null;
      this.routes = options.routes;
      this.mode = options.mode || "hash";
   }
}

第二步:刚开始访问的网址的hash#/,所以我们得监听load事件,并将当前的hash值赋给current,同时将components渲染到页面上:


class myRouter{
   constructor(options){
      this.current = null;
      this.routes = options.routes;
      this.mode = options.mode || "hash";
      this.init()
   }
   init(){
      if(this.mode === 'hash'){
          window.addEventListener('load',this.setCurrent);
          window.addEventListener("hashchange",this.setCurrent);
      }
   }
   setCurrent(){
     this.current = location.hash.slice(1)
     console.log(this.current)//1
   }

}
myRouter.install = function(vue){
     
}

export default myRouter

现在我们做到了在页面初始化的时候和hash改变时将地址hashcurrent,但是并没有渲染组件呢?接下来就做一个router-view,而它也是一个组件,所以直接在全局注册一个组件:

  • 拿到current
  • 从routes中依据current找到component
  • 使用render函数渲染组件

let vue=null;
class myRouter {
    constructor(options) {
        this.current = null;
        vue.util.defineReactive(this,"current",'/')
        this.routes = options.routes;
        this.init();
        this.mode = options.mode || "hash";
    }
    init() {
        if (this.mode === 'hash') {
            window.addEventListener('load', this.setCurrent);
            window.addEventListener("hashchange", this.setCurrent);
        }
    }
    setCurrent() {
        this.current = location.hash.slice(1);
        console.log(this.current)
    }

}
myRouter.install = function (_vue) {
    vue = _vue;
    vue.mixin({
        beforeCreate() {
            console.log(this)
            // 如果是跟组件的话将this和router取出来
            if (this.$options?.router) {
                this._root = this;
                this._router = this.$options.router
                console.log(this._router)
            } else {
                // 不是的话就去找
                this._root = this.$parent?._root;
            }
            Object.defineProperty(this, "$router", {
                get() {
                    return this._root._router
                }
            })
        }
    })
    vue.component("router-link", {
        render() {
            return
        }
    })
    // router-view
    vue.component("router-view", {
        render(h) {
            console.log(this.$router.routes)
            const current = this.$router.current;
            console.log(current)
            const component = this.$router.routes.find(item => item.path === current);
            console.log(component)
            return h(component.component)

        }
    })
}

export default myRouter

现在页面能够渲染组件了,但是跳转还不行,router-link的本质是a标签,所以的注册的一个全局组件render``a标签。

.......
 vue.component("router-link", {
        props: ['to'],
        render(h) {
            return h("a", { attrs: { href: `#${this.to}` } }, this.$slots.default)
        }
    })
.......

end

到此一个基本的路由就搞定了,但是还是缺少的好多,比如history模式,带上参数之类的。这些下次再搞定吧,休息休息。