zoukankan      html  css  js  c++  java
  • Vue-Router 源码分析(六) router-view组件的用法及原理

    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完成渲染的。

  • 相关阅读:
    jmeter接口测试3-正则表达式提取器的使用
    Sublime中Markdown的安装与使用
    python使用you-get模块下载视频
    python BeautifulSoup模块的简要介绍
    python Requests模块的简要介绍
    mongodb基本操作的学习
    python中的常用方法
    网盘的选择,百度网盘、google drive 还是 Dropbox
    python_爬虫一之爬取糗事百科上的段子
    pycharm的使用破解和Anaconda的使用
  • 原文地址:https://www.cnblogs.com/greatdesert/p/12464471.html
Copyright © 2011-2022 走看看