注册为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
主要作用:
- 处理子组件样式
- 根据传入的event属性,处理事件监听,包装成on:{事件名: 回调}
- 默认渲染出一个a标签包裹中包裹的内容,并给这个a标签添加指定事件的回调(默认click,然后触发router实例的router或push方法)
- 如果指定了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)
}
}