欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Vue-Router 源码分析(六) router-view组件的用法及原理

程序员文章站 2022-06-19 10:52:22
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>

渲染如下:

Vue-Router 源码分析(六) router-view组件的用法及原理

当路由到info页时,我们在控制台打印app.$route.matched,输出如下:

 writer by:大沙漠 qq:22969969

Vue-Router 源码分析(六) router-view组件的用法及原理

 当路由到page页时,我们再在控制台打印app.$route.matched,输出如下:

Vue-Router 源码分析(六) router-view组件的用法及原理

 可以看到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完成渲染的。