VueRouter

Vue


# vue-router

# mode

  • hash
  • history

# 跳转

this.$router.push()
1
<router-link to=""></router-link>
1

# 占位

<router-view></router-view>
1

# vue-router 源码实现

作为一个插件存在:

  • 实现 VueRouter 类和 install 方法

  • 实现两个全局组件: router-view 用于显示匹配组件内容,router-link 用于跳转

  • 监控 url 变化: 监听 hashchange 或 popstate 事件

  • 响应最新 url:创建一个响应式的属性 current,当它改变时获取对应组件并显示

let Vue

class VueRouter {
  // 核心任务:
  // 1.监听url变化
  constructor(options) {
    this.$options = options

    // 缓存path和route映射关系
    // 这样找组件更快
    this.routeMap = {}
    this.$options.routes.forEach((route) => {
      this.routeMap[route.path] = route
    })

    // 数据响应式
    // 定义一个响应式的current,则如果他变了,那么使用它的组件会rerender
    Vue.util.defineReactive(this, 'current', '')

    // 请确保onHashChange中this指向当前实例
    window.addEventListener('hashchange', this.onHashChange.bind(this))
    window.addEventListener('load', this.onHashChange.bind(this))
  }

  onHashChange() {
    // console.log(window.location.hash);
    this.current = window.location.hash.slice(1) || '/'
  }
}

// 插件需要实现install方法
// 接收一个参数,Vue构造函数,主要用于数据响应式
VueRouter.install = function (_Vue) {
  // 保存Vue构造函数在VueRouter中使用
  Vue = _Vue

  // 任务1:使用混入来做router挂载这件事情
  Vue.mixin({
    beforeCreate() {
      // 只有根实例才有router选项
      if (this.$options.router) {
        Vue.prototype.$router = this.$options.router
      }
    },
  })

  // 任务2:实现两个全局组件
  // router-link: 生成一个a标签,在url后面添加#
  // <a href="#/about">aaaa</a>
  // <router-link to="/about">aaa</router-link>
  Vue.component('router-link', {
    props: {
      to: {
        type: String,
        required: true,
      },
    },
    render(h) {
      // h(tag, props, children)
      return h('a', { attrs: { href: '#' + this.to } }, this.$slots.default)
      // 使用jsx
      // return <a href={'#'+this.to}>{this.$slots.default}</a>
    },
  })
  Vue.component('router-view', {
    render(h) {
      // 根据current获取组件并render
      // current怎么获取?
      // console.log('render',this.$router.current);
      // 获取要渲染的组件
      let component = null
      const { routeMap, current } = this.$router
      if (routeMap[current]) {
        component = routeMap[current].component
      }
      return h(component)
    },
  })
}

export default VueRouter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
Last Updated: 10/21/2024, 4:15:17 PM