router-view是一个 functional 组件,渲染路径匹配到的视图组件。
渲染的组件还可以内嵌自己的
,根据嵌套路径,渲染嵌套组件
它只有一个名为name的props,这个name还有个默认值,就是default,一般情况下,我们不用传递name,只有在命名视图的情况下,我们需要传递name,命名视图就是在同级展示多个视图,而不是嵌套的展示出来,
router-view组件渲染时是从VueRouter实例._route.matched属性获取需要渲染的组件,也就是我们在vue内部的this.$route.matched上获取的,举个栗子:
<div id="app"> <router-link to="/info/">info页router-link> <router-link to="/info/face">page页router-link> <hr/> <router-view>router-view> div> <script> const info = { template:'info Page'} //外层组件 const page = { template:' face Page'} //内层组件 const routes = [ { path:'/info/', component:info, children:[ {path:'face',component:page} //使用了嵌套路由 ] } ] const app = new Vue({ el:'#app', router:new VueRouter({routes}) }) script>
渲染如下:
当路由到info页时,我们在控制台打印app.$route.matched,输出如下:
writer by:大沙漠 QQ:22969969
当路由到page页时,我们再在控制台打印app.$route.matched,输出如下:
可以看到matched中保存所有父子组件信息,索引从0开始,依次是顶层组件、然后是一层层下来的子组件。router-view组件内部render实现时就会读取这个matched属性的,如下:
var View = { name: 'RouterView', functional: true, //函数式组件 props: { name: { type: String, default: 'default' } }, render: function render (_, ref) { var props = ref.props; //获取props ;例如:{name: "default"} var children = ref.children; //获取所有子节点 var parent = ref.parent; //父组件的引用 var data = ref.data; // used by devtools to display a router-view badge data.routerView = true; // directly use parent context's createElement() function // so that components rendered by router-view can resolve named slots var h = parent.$createElement; //获取父组件的$createElement函数引用 这样组件在执行render时可以用命名插槽 var name = props.name; var route = parent.$route; //当前的路由地址 var cache = parent._routerViewCache || (parent._routerViewCache = {}); //获取父组件的_routerViewCache属性,如果没有则初始化为空对象 // determine current view depth, also check to see if the tree // has been toggled inactive but kept-alive. var depth = 0; //组件嵌套的层次 var inactive = false; //是否在keep-alive组件内 while (parent && parent._routerRoot !== parent) { if (parent.$vnode && parent.$vnode.data.routerView) { depth++; } if (parent._inactive) { //如果parent._inactive存在 inactive = true; //则设置inactive为true } parent = parent.$parent; } data.routerViewDepth = depth; //组件嵌套的层次 // render previous view if the tree is inactive and kept-alive if (inactive) { return h(cache[name], data, children) } var matched = route.matched[depth]; //从matched属性当中获取当前层次的路由对象,这里保存了需要渲染的组件,这就是上面我们通过app.$route.matched获取的对象 // render empty node if no matched route if (!matched) { cache[name] = null; return h() } var component = cache[name] = matched.components[name]; //获取需要渲染的组件 // attach instance registration hook // this will be called in the instance's injected lifecycle hooks data.registerRouteInstance = function (vm, val) { // val could be undefined for unregistration var current = matched.instances[name]; if ( (val && current !== vm) || (!val && current === vm) ) { matched.instances[name] = val; } } // also register instance in prepatch hook // in case the same component instance is reused across different routes ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { matched.instances[name] = vnode.componentInstance; }; // resolve props var propsToPass = data.props = resolveProps(route, matched.props && matched.props[name]); if (propsToPass) { // clone to prevent mutation propsToPass = data.props = extend({}, propsToPass); // pass non-declared props as attrs 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) //最后渲染该组件 } }
通过阅读源码,我们得知router-view通过判断当前组件的嵌套层次,然后通过这个层次从route.matches数组中获取当前需要渲染的组件,最后调用全局的$createElement来创建对应的VNode完成渲染的。