# router-link 和 router-view

router-link 和 router-view 是 Vue-Router 中定义的两个全局组件,定义的位置位于 src/components 文件夹下。

router-link 用于切换组件,默认情况下会被渲染为 <a> 标签,点击时会调用 push 方法改变路由,跳转的路由通过 to 属性指定。

// 默认渲染为 a 元素
if (this.tag === 'a') {
      data.on = on
      data.attrs = { href }
    } else {
      const a = findAnchor(this.$slots.default)
      if (a) {
        a.isStatic = false
        const aData = (a.data = extend({}, a.data))
        aData.on = on
        const aAttrs = (a.data.attrs = extend({}, a.data.attrs))
        aAttrs.href = href
      } else {
        data.on = on
      }
    }

// 点击切换路由
const handler = e => {
  if (guardEvent(e)) {
    if (this.replace) {
      // replace路由
      router.replace(location)
    } else {
      // push 路由
      router.push(location)
    }
  }
}

# router-view

router-view 是一个函数式组件,渲染路径匹配到的视图组件。其本身不会被渲染为真实的节点,更多的是做一个占位标记的作用,标识组件应该挂载的位置。我们来看 mini-vue-router3 中对 router-view 的简单实现:

// 定义 <router-view> 全局组件
export default {
    // 此组件为函数式组件
    functional: true,
    render(h, { parent, data }) {
        //标记位,标记此组件已渲染过
        data.routerView = true
        // 获取当前路由信息 
        let route = parent.$route

        // 当前渲染组件的层级
        let depth = 0

        // 计算当前需要渲染组件的层级
        while (parent) {
            if (parent.$vnode && parent.$vnode.data.routerView) {
                // 如果发现父组件上有 routerView 属性,则深度++
                depth++
            }
            // 继续递归向上查找
            parent = parent.$parent
        }

        // 获取到当前要渲染的组件信息
        let record = route.matched[depth]

        if (!record) {
            // 没匹配到组件,返回一个空节点
            return h()
        }

        // this.$slots 获取到所有的插槽
        return h(record.component, data)
    }
}

首先,设置了此组件为一个函数式组件,当当前路由没有匹配到组件时,返回一个空节点。

其次就是如何确定需要渲染哪个组件?比如当前路径为 /a/b/c ,那么就对应 /a、/a/b、/a/b/c 三个组件,这三个组件对应的路由对象是按顺序放置在 route.matched 数组中的。所以,要渲染哪个组件,主要是确定好层级的问题

对于 Vue 父子组件间的渲染顺序,是先父组件初始化,在父组件挂载前,进行子组件的初始化。所以,是先父再子的顺序。要确定当前渲染组件的层级,我们就要去其祖先组件上寻找 router-view 属性,若存在,则将层级+1。一直找到最顶层的组件为止。若最后层级为 x,则渲染 metched 中下标为 x 的路由对象对应的组件。