Vue-Router 源码分析(六) router-view组件的用法及原理
程序员文章站
2022-03-26 13:24:23
router-view是一个 functional 组件,渲染路径匹配到的视图组件。 渲染的组件还可以内嵌自己的 ,根据嵌套路径,渲染嵌套组件 它只有一个名为name的props,这个name还有个默认值,就是default,一般情况下,我们不用传 ......
router-view是一个 functional 组件,渲染路径匹配到的视图组件。<router-view>
渲染的组件还可以内嵌自己的 <router-view>
,根据嵌套路径,渲染嵌套组件
它只有一个名为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:'<div>info page<router-view><br/></router-view></div>'} //外层组件 const page = { template:'<div>face page</div>'} //内层组件 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完成渲染的。