Vue-Router 源码解析(五) router-link组件的用法及原理
程序员文章站
2022-03-26 22:25:59
该组件支持用户在具有路由功能的应用中(点击)导航,默认渲染成带有正确链接的标签,可以通过tag属性生成别的标签。 它本质上是通过在生成的标签上绑定了click事件,然后执行对应的VueRouter实例的push()实现的,对于router-link组件来说,可以传入以下props: to 表示 ......
该组件支持用户在具有路由功能的应用中(点击)导航,默认渲染成带有正确链接的<a>标签,可以通过tag属性生成别的标签。
它本质上是通过在生成的标签上绑定了click事件,然后执行对应的vuerouter实例的push()实现的,对于router-link组件来说,可以传入以下props:
- to 表示目标路由的链接,当被点击后,内部会立刻把to的值传到router.push(),所以这个值可以是一个字符串或者是描述目标位置的对象
- tag router-link组件渲染的标签名,默认为a
- exact 布尔类型,“是否激活”默认类名的依据是包含匹配
- append 布尔类型,设置append属性后,则在当前(相对)路劲前添加基路劲
- replace 布尔类型,设置replace后,当点击时会调用router.replace()而不是router.push(),这样导航后不会留下history记录
- activeclass 链接激活时使用的css类名
- exactactiveclass 配置当链接被精确匹配的时候应该激活的 class
- event 声明可以用来触发导航的事件。可以是一个字符串或是一个包含字符串的数组
举个栗子:
writer by:大沙漠 qq:22969969
<div id="app"> <router-link to="/info/1">详情页1</router-link> <router-link to="/info/2" event="mouseover">详情页2</router-link> <router-link to="/info/3" tag="p">详情页3</router-link> <hr/> <router-view></router-view> </div> <script> const info = { template:'<div>id:{{this.$route.params.id}}</div>'} const routes = [ {path:'/info/:id',component:info,name:'info'}, ] const app = new vue({ el:'#app', router:new vuerouter({routes}) }) </script>
渲染后的dom如下:
可以看到第二个routerlink组件渲染成了p标签,我们点击时效果如下:
可以看到详情页3上并没有点击,鼠标移动上去,就会自动切换路由了,这是因为我们传入了event,在上面绑定了onmouseover事件。
router-link组件自定义了render函数,如下:
var link = { name: 'routerlink', props: { //可以传入的props to: { type: totypes, required: true }, tag: { type: string, default: 'a' }, exact: boolean, append: boolean, replace: boolean, activeclass: string, exactactiveclass: string, event: { type: eventtypes, default: 'click' //默认为click事件 } }, render: function render (h) { //router-link组件对应的render函数 h等于大vue上的$createelement函数 var this$1 = this; var router = this.$router; //获取$router实例 var current = this.$route; //当前的路由对象 var ref = router.resolve(this.to, current, this.append); var location = ref.location; //要跳转的地址,是一个对象 ;例如:{_normalized: true, path: "/login", query: {…}, hash: ""} var route = ref.route; //路由对象 var href = ref.href; //href属性 var classes = {}; //这段代码和linkactiveclass、linkexactactiveclass的配置有关 var globalactiveclass = router.options.linkactiveclass; var globalexactactiveclass = router.options.linkexactactiveclass; // support global empty active class var activeclassfallback = globalactiveclass == null ? 'router-link-active' : globalactiveclass; var exactactiveclassfallback = globalexactactiveclass == null ? 'router-link-exact-active' : globalexactactiveclass; var activeclass = this.activeclass == null ? activeclassfallback : this.activeclass; var exactactiveclass = this.exactactiveclass == null ? exactactiveclassfallback : this.exactactiveclass; var comparetarget = location.path ? createroute(null, location, null, router) : route; classes[exactactiveclass] = issameroute(current, comparetarget); classes[activeclass] = this.exact ? classes[exactactiveclass] : isincludedroute(current, comparetarget); var handler = function (e) { //绑定的事件 if (guardevent(e)) { //先调用该函数过滤掉一些和导航无关的事件 if (this$1.replace) { //如果设置了replace属性 router.replace(location); //则执行router.replace切换导航 } else { router.push(location); //否则执行router.push更新导航 } } }; var on = { click: guardevent }; //<router-link>组件默认都支持的click事件,下面会重置的 if (array.isarray(this.event)) { this.event.foreach(function (e) { on[e] = handler; }); } else { on[this.event] = handler; } var data = { //一般情况下,这里会重置on.click为handler class: classes }; if (this.tag === 'a') { //如果tag是一个a标签 data.on = on; data.attrs = { href: href }; } else { //如果是其它标签 // find the first <a> child and apply listener and href var a = findanchor(this.$slots.default); //查找第一个<a>标签 if (a) { //如果找到了一个a标签 // in case the <a> is a static node a.isstatic = false; var adata = a.data = extend({}, a.data); adata.on = on; var aattrs = a.data.attrs = extend({}, a.data.attrs); aattrs.href = href; } else { //如果没有找到a标签 // doesn't have <a> child, apply listener to self data.on = on; //则监听自身 } } return h(this.tag, data, this.$slots.default) //最后调用$createelement去创建该vnode } }
如果是a标签,在guardevent函数内会执行preventdefault()函数通知浏览器不要执行与事件相关的动作,所有的动作比如设置hash都是通过vuerouter的内置代码来实现。