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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM