VueRouter源码(三)--RouterView, RouterLink

本文深入探讨VueRouter的源码,主要关注RouterView和RouterLink两个组件。RouterView用于渲染对应的组件,而RouterLink则处理子组件样式,监听事件并根据事件属性执行相应操作,通常默认生成a标签并处理点击事件,实现路由跳转。若指定tag属性,事件和属性将被绑定到相应元素。
摘要由CSDN通过智能技术生成

注册为vue组件

RouterView 和RouterLink组件在install方法中注册成了Vue的组件

function install (Vue) {
  ...
  Vue.component('RouterView', View);
  Vue.component('RouterLink', Link);
  ...
}

RouterView

vue-router.js line45

主要作用: 在RouterView里渲染需要显示的组件

var View = { 
	name: 'RouterView',
	functional: true,
	props:{
		name: {
			type: String,
			default: 'default'
		}
	},
	render:function(_, ref){
		var props = ref.props;
	    var children = ref.children;
	    var parent = ref.parent;
	    var data = ref.data;

		var h = parent.$createElement; //用于创建DOM
		var cache = parent._routerViewCache || (parent._routerViewCache = {});
		var route = parent.$route;
		
		//遍历父节点,找到顶级节点
		while (parent && parent._routerRoot !== parent) {
	      var vnodeData = parent.$vnode && parent.$vnode.data;
	      if (vnodeData) {
	        if (vnodeData.routerView) {
	          depth++;
	        }
	        if (vnodeData.keepAlive && parent._inactive) {
	          inactive = true;
	        }
	      }
	      parent = parent.$parent;
	    }
	    //如果dom树是keep-alive状态,直接渲染
	    if (inactive) {
	      return h(cache[name], data, children)
	    }
	    //从路由的matched列表中获取当前的record(传入路由实例的routes数组,包含name, components, redirect, path等属性)
	    var matched = route.matched[depth];
	    // 如果没有找到匹配路由,渲染一个空节点并返回
	    if (!matched) {
	      cache[name] = null;
	      return h()
	    }
	    //否则查找匹配到的路由中的组件
	    //获取要渲染的组件
	    var component = cache[name] = matched.components[name];
	    ...
	    //处理props
	    var propsToPass = data.props = resolveProps(route, matched.props && matched.props[name]);
	    if (propsToPass) {
	      // clone to prevent mutation
	      propsToPass = data.props = extend({}, propsToPass);
	      // 略过没有声明的属性
	      var attrs = data.attrs = data.attrs || {};
	      for (var key in propsToPass) {
	        if (!component.props || !(key in component.props)) {
	          attrs[key] = propsToPass[key];
	          delete propsToPass[key];
	        }
	      }
	    }
		//渲染组件
	    return h(component, data, children)
	}
}

RouterLink

line 1005

主要作用:

  1. 处理子组件样式
  2. 根据传入的event属性,处理事件监听,包装成on:{事件名: 回调}
  3. 默认渲染出一个a标签包裹中包裹的内容,并给这个a标签添加指定事件的回调(默认click,然后触发router实例的router或push方法)
  4. 如果指定了tag属性,且不是‘a’标签,那么首先会从包裹的子元素中寻找到第一个a标签,给他添加事件和属性。如果没有找到,那么就把事件和属性绑定到tag指定的元素上。
var Link = {
  name: 'RouterLink',
  props: {
    to: {
      type: toTypes, // [String, Object]
      required: true
    },
    tag: {
      type: String,
      default: 'a' //默认a标签
    },
    exact: Boolean,
    append: Boolean,
    replace: Boolean,
    activeClass: String,
    exactActiveClass: String,
    event: {
      type: eventTypes, //[String, Array]
      default: 'click' //默认click触发跳转事件
    }
  },
  render: function render (h) {
	// this-> vue
    var router = this.$router; //路由实例
    var current = this.$route; //当前路由对象
    /** resolve()返回值
	    *return { 
	    *location: location,
	    *route: route,
	    *href: href,
	    *normalizedTo: location,
	    *resolved: route
	  *}
    */
    var ref = router.resolve(
      this.to,
      current,
      this.append
    );
    var location = ref.location;
    var route = ref.route;
    var href = ref.href;

    //省略..处理样式class

    var compareTarget = route.redirectedFrom
      ? createRoute(null, normalizeLocation(route.redirectedFrom), null, router)
      : route;

    //依然省略..样式处理
	//事件回调
    var handler = function (e) {
      if (guardEvent(e)) {
      //追加或替换历史记录
        if (this$1.replace) {
          router.replace(location, noop);
        } else {
          router.push(location, noop);
        }
      }
    };
    //补充guardEvent
    function guardEvent (e) {
	  // 控制键不处理
	  if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) { return }
	  // 如果已经禁用默认 不处理
	  if (e.defaultPrevented) { return }
	  // 右击不处理
	  if (e.button !== undefined && e.button !== 0) { return }
	  // 如果target属性是_blank 不处理
	  if (e.currentTarget && e.currentTarget.getAttribute) {
	    var target = e.currentTarget.getAttribute('target');
	    if (/\b_blank\b/i.test(target)) { return }
	  }
	  // this may be a Weex event which doesn't have this method
	  if (e.preventDefault) {
	    e.preventDefault();
	  }
	  return true
	}
    //补充结束

    var on = { click: guardEvent }; //guardEvent 返回true或undefined
    if (Array.isArray(this.event)) { //event默认是click,可以自行制定,格式是[String, Array]
      this.event.forEach(function (e) {
        on[e] = handler; //如果是数组,依次触发事件
      });
    } else {
      on[this.event] = handler;
    }

    var data = { class: classes };
	//插槽作用域
    var scopedSlot =
      !this.$scopedSlots.$hasNormal &&
      this.$scopedSlots.default &&
      this.$scopedSlots.default({
        href: href,
        route: route,
        navigate: handler,
        isActive: classes[activeClass],
        isExactActive: classes[exactActiveClass]
      });

    if (scopedSlot) {
      if (scopedSlot.length === 1) {
        return scopedSlot[0]
      } else if (scopedSlot.length > 1 || !scopedSlot.length) {
        ...警告处理
        return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot) //如果是空数组,渲染一个空节点,否则一个空的span节点
      }
    }
	//处理包裹标签 默认是A 标签
    if (this.tag === 'a') {
      data.on = on;
      data.attrs = { href: href };
    } else {
      // 查找子标签中第一个A标签,并绑定回调和href属性到这个A标签上
      var a = findAnchor(this.$slots.default);
      if (a) {
        // 如果有A 标签
        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>,事件回调绑定到自身
        data.on = on;
      }
    }
	//渲染标签
    return h(this.tag, data, this.$slots.default)
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值