router-view是一個 functional 組件,渲染路徑匹配到的視圖組件。<router-view> 渲染的組件還可以內嵌自己的 <router-view>,根據嵌套路徑,渲染嵌套組件 它只有一個名為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完成渲染的。