持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
前言
大家好,上篇文章(路由前置守卫beforeEach)[xxx]中我们分享的路由前置守卫beforeEach的使用方法、使用场景及云源码解读。前面的几篇关于VueRouter的分享都是与路由初始化和配置相关的,那么除了这些初始化和配置操作外,还有路由的具体使用。在前面我们分享VueRouter的install方法时曾有提到在install的最后会注册两个全局组件RouterLink和RouterView,这也是在组将中使用路由的两个非常常用的组件。RouterLinek主要实现路由的切换,RouterView则负责页面的加载。下面我们先来分析一下RouterLink。
RouterLink的使用
RouterLinke的使用也非常简单,跟其它组件一样RouterLink也是一个组件,并且还是一个全局组件,所以我们可以在各个组件的模板中直接使用即可。RouterLink定义了11个自定义属性(props)其中to
是最常用也是必须要传递的一个属性。下面我们把几个常用的属性简单介绍一下:
to
- 类型:to是一个routelocationraw或
string
类型 - 默认值:to是必填项,无默认值
- 用途:用来表示目标路由的链接,当RouterLink被点击后,它的值会被传递给router.push从而实现路由的切换,因此to的值还可以是一个字符串。
<!-- 字符串 -->
<router-link to="/home">Home</router-link>
<!-- 使用 v-bind 的 JS 表达式 -->
<router-link :to="{ path: '/home' }">Home</router-link>
<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: '123' }}">User</router-link>
<!-- 带查询参数,下面的结果为 `/register?plan=private` -->
<router-link :to="{ path: '/register', query: { plan: 'private' }}">
Register
</router-link>
replace
- 类型:Boolean
- 默认值:false
- 用途:上面说到to会把值传递给router.push实现路由切换,而这里如果指定了replace属性为true时则会将to的值传递个router.replace而不是router.push,因此导航不会留下历史记录
<router-link to="/home" replace>home</router-link>
active-class
- 类型:string
- 默认值:router-link-active
- 用途:用于链接激活时,应用于渲染组件的class。
<router-link to="/home" active-class="xxx">home</router-link>
以上就是RouterLink的基本用法和用途介绍,其实最终RouterLink会被渲染成一个a标签,那么既然最终用的也是a标签,为什么不一开始就使用a标签而是要用个自定义的RouterLink组件呢?这是因为:
如果直接使用a标签,在点击链接实现路由切换时整个页面都会刷新一下,而VueRouter之所以为我们封装了自定义组件RouterLink,目的就是为了实现在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。如下图
<router-link to="/">RouterLink</router-link>
<a href="/">a标签</a>
源码解读
了解了RouterLink的用法及用途,下面我们再来看下它的源码是如何实现的,为什么它可以在不重新加载页面的情况下实现url切换的呢?
var Link = {
name: 'RouterLink',
props: {
to: {
type: toTypes,
required: true
},
tag: {
type: String,
default: 'a'
},
...省略部分props
},
render: function render (h) {
var this$1 = this;
var router = this.$router;
var current = this.$route;
var ref = router.resolve(
this.to,
current,
this.append
);
var classes = {};
var activeClassFallback =
globalActiveClass == null ? 'router-link-active' : globalActiveClass;
var exactActiveClassFallback =
globalExactActiveClass == null
? 'router-link-exact-active'
: globalExactActiveClass;
...
var handler = function (e) {
if (guardEvent(e)) {
if (this$1.replace) {
router.replace(location, noop);
} else {
router.push(location, noop);
}
}
};
var on = { click: guardEvent };
if (Array.isArray(this.event)) {
this.event.forEach(function (e) {
on[e] = handler;
});
} else {
on[this.event] = handler;
}
if (scopedSlot) {
}
if (this.tag === 'a') {
data.on = on;
data.attrs = { href: href, 'aria-current': ariaCurrentValue };
} else {
// find the first <a> child and apply listener and href
var a = findAnchor(this.$slots.default);
if (a) {
...
} else {
// doesn't have <a> child, apply listener to self
data.on = on;
}
}
return h(this.tag, data, this.$slots.default)
}
};
首先我们看到这里的RouterLink组件跟其它vue组件的定义方式有所不同,这里的RouterLink是一个包含了三个属性(name、props和render)的Link对象,实际上这是一种函数式的组件声明,其中name和props的定义方式与我们在vue文件中自定义组件的方式是一样的,唯一不同的也是该组件的核心所在就是这个render函数,下面我们详细解读一下:
- name属性的值为“RouterLink”,这个就是组件的名称
- props,前面我们提到RouterLink组件有11个自定义属性,其实就是来自这个props,其中
to
属性是一个必填项就是通过这里定义的。另外还有个字符串类型的tag
属性,这个属性就是用来指示最终渲染在页面上的标签,默认值为a,我们还可以手动指定为div,span等等任何想要的标签。 - render函数是该组件实现的核心所在
- render函数接收一个形参h,实际上h也是一个函数,是用来将虚拟dom转换成真是dom的createElement函数
- 在render内部,首先会声明并定义一些变量,如路由实例router、当前路由信息current等等
- 然后处理自定义属性,比如上面定义的activeClass和exactActiveClass,其中默认值
router-link-active
和router-link-exact-active
就是在这里设置的 - 接着下面定义了一个handler方法,当在组件中点击RouterLink时就会触发这个handler方法实现路由的切换,我们可以看到在这个方法中最核心的就是调用了
router.replace
或router.push
从而实现在不重新加载页面的情况下实现url的切换 - 下面定义的
on
对象中包含一个click属性,其实对应的就是RouterLink的点击事件,默认值是guardEvent,最终会被替换为handler函数 - data对象存储的是标签的样式列表,最终会被应用到标签元素上
- scopedSlot是用于处理RouterLink的作用域插槽的,在前面使用时没有提到,其实RouterLink还可以通过
v-slot
指令来暴露底层的定制能力,这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,大多数情况下用在一个类似 NavLink 这样的组件里。关于作用域插槽就是通过scopedSlot相关的代码进行处理的 - 接下来会判断自定义属性
tag
的值是否是a,也就是a标签,如果是则添加对应的click事件,href和aria-current属性 - 如果不是则查找它的子元素中是否有a标签,如果有则进行相关的绑定操作,如果仍然没有则直接将click事件绑定到当前组件本身
- 最后也是重点就是调用h函数,上面说到h函数其实就是createElement,目的将上将虚拟dom转换成真实dom
总结
本次分享我们介绍了vuerouter中RouterLink组件的用法用途及源码解读,简单总结如下:
RouterLink通过to属性接收路由信息,当用户点击RouterLink时会触发一个handler的函数,在handler中会将路由路径传递给router.push或router.replace从而实现在不重新加载页面的情况下实现url的切换。然后通过render函数对自定义属性进行处理最后通过调用createElement函数将虚拟dom(js模拟的dom对象)转换成真实dom进行渲染
好了关于RouterLink组件的分享就到这里了。欢迎大佬的指点!!!