vue-route实现原理

vue-router实战


先列一段最简单的router代码

1.Vue.use安装路由插件
2.对外暴露了一个new Router的对象,里面包含所有路由的配置

import VueRouter from 'vue-router'
Vue.use(VueRouter)

export default new VueRouter({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      beforeEnter(from,to,next){
          console.log(`beforEnterHome from ${from} to ${to}`)
          setTimeout(()=>{
            next()
          },1000)
          // next()
      }
    },
    {
      path: '/about',
      name: 'about',
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    }
  ]
})

初始化的时候,直接把这个返回的对象传递到new Vue中

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

模板层自带了router-link和router-view两个组件

<div id="nav">
      <button @click="about">about</button>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>

实现自己的vue-router


我们新建vue-router文件夹 新建index.js, 大家都已经是黄金水平了,就不介绍太多基础知识了,基本上迷你的router,这几块就可以构成

export default class Router{
    constructor(){

    }
    init(){
        // 初始化
    }
    bindEvents(){
        // 绑定事件
    }
    createRouteMap(){
        // 初始化路由表
    }
    initComponent(){
        // 注册router-link和router-view路由
    }
}

插件机制


我们使用Vue.use(VueRouter)来注册和启动路由, 这个咱们整vuex源码的时候整过了,带上一个install方法就可以,先注册一个简单的调试信息看下

let Vue
class Router {
    static install(_Vue) {
        Vue = _Vue
        Vue.mixin({
            beforeCreate() {
                if(this.$options.router){
                    // new Vue的时候传递的
                    Vue.prototype.$routerMsg = '路由安装完毕'
                }
            }
        })
    }
}

app.vue使用$routerMsg可以直接显示出信息,bingo

单页应用原理


1.hash模式,改变锚点
2.history模式,利用了html的popState和pushState方法 ,url发生变化,不会刷新页面 两者做路由跳转和监听的事件削微的有些不一样,这里我们只演示一下hash,实际情况hash和history用两个class就成
我们在install的时候,执行init 启动整个路由, 监听onload和hashchange事件,触发后,根据当前的hash,找到需要渲染的组件,然后去渲染router-view的内容就欧克了

hash变化后,通知到router-view渲染,需要借用Vue本身的响应式能力

static install(_Vue) {
        Vue = _Vue
        Vue.mixin({
            beforeCreate() {
                if(this.$options.router){
                    // new Vue的时候传递的
                    Vue.prototype.$routerMsg = '路由安装完毕'
                    Vue.prototype.$router = this.$options.router
                    this.$options.router.init()
                }
            }
        })
    }
    init() {
        this.bindEvents()
        this.createRouteMap(this.$options)
        this.initComponent(Vue)
    }
    bindEvents(){
        window.addEventListener('load', this.onHashChange.bind(this), false)
        window.addEventListener('hashchange', this.onHashChange.bind(this), false)
    }

路由映射表


上面说的有一步,就是根据当前hash路由,找到需要渲染的组件,咱们传递进来的事数组,查找起来费劲,转成对象,方便查找和定位组件,我们称之为路由映射表

constructor(options) {
        this.$options = options
        this.routeMap = {}
    }
    createRouteMap(options) {
        options.routes.forEach(item => {
            this.routeMap[item.path] = item
        })
    }

注册组件router-view


在组件的render函数里,根据this.app.current里面存储的路由,查找到组件 然后渲染即可

这里的h,就是React里面的createElement一个概念,以后给大家写虚拟dom源码的时候,大家会有更深刻的理解

Vue.component('router-view', {
    render:h=>{
        var component = this.routeMap[this.app.current].component
        return h(component)
    }
})

注册组件router-link


router-link整成a标签就可以,记得带上插槽

这里面的写法 就是传说中的JSX

Vue.component('router-link', {
    props: {
        to: String
    },
    render(h){
        return <a href={this.to}>{this.$slots.default}</a>
    }
})

onHashchange


具体处理hash变化的逻辑,其实很easy,因为我们利用Vue的响应式原理来存储当前路由,我们获取当前的hash,然后直接利用Vue响应式机制通知router-view即可

constructor(options) {
        this.$options = options
        this.routeMap = {}
        this.app = new Vue({
            data: {
                current: '/'
            }
        })

    }
    onHashChange(e) {
        let hash = this.getHash()
        let router = this.routeMap[hash]
        this.app.current = this.getHash()
    }

路由守卫

注册路由的时候,可以带上生命周期 并且生命周期的参数next,执行后才跳转,可以做路由守卫的工作,比如咱们小小的模拟一下,2秒后再跳转

{
      path: '/',
      name: 'home',
      component: Home,
      beforeEnter(from,to,next){
          console.log(`beforEnterHome from ${from} to ${to}`)
          setTimeout(()=>{
            next()
          },1000)
          // next()
      }
    },

实际的Router中,路由守卫的逻辑很复杂,是一个异步队列的依次执行,有点像koa或者redux的中间件执行逻辑,咱们只考虑一个 简化一下逻辑

    if(router.beforeEnter){
        router.beforeEnter(from, to, ()=>{
            this.app.current = this.getHash()

        })
    }else{
        this.app.current = this.getHash()
    }

扩展


实际的vue-router代码,要复杂很多,很多容错的处理,有几个扩展点大家可以重点关注

  1. 路由嵌套
    1. 递归注册+ 层级查找渲染router-view
  2. 路由变量 (使用path-to-regexp)
  3. 路由守卫队列
    1. 异步队列
  4. addRouter动态添加路由
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容