# router-link 和 router-view
router-link 和 router-view 是 Vue-Router 中定义的两个全局组件,定义的位置位于 src/components 文件夹下。
# router-link
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 的路由对象对应的组件。