詳解vue中router-view組件的生成原理
前言
在vue的使用過程中,有一個組件,幾乎是必用的,那就是router-view。它是所有組件的入口,是單頁面系統(tǒng)的一把利劍。如果你的系統(tǒng)是火箭,那么router-view無疑將是這艘火箭的北斗衛(wèi)星。
router-view
< router-view />是 vue-router 默認注冊的全局組件。如果你從0搭建過系統(tǒng)的話,一定記得在Layout或者AppMain組件中有過這樣一行代碼:
<transition name="fade-transform" mode="out-in"> <div style="height: 100%"> // 所有組件的入口 <router-view /> </div> </transition>
我們一直在用,但是有沒有想過:這個組件是干嘛用的?為什么注冊為全局組件,又為什么是所有頁面組件的入口?它……究竟有什么用?
帶著這樣的疑問,我打開了 vue-router 的gitHub切換到 vue2 分支
然后 在 src->components->view.ts 中找到了 router-view 的相關代碼:
下面來分析一下這個組件的源碼,看一下 vue-router 是怎么設計這個組件的,以及這種設計帶給我們的思考有哪些?
為了便于理解,我將這段源碼粘貼下來:
// @ts-nocheck import { Component } from 'vue' const View: Component = { name: 'RouterView', functional: true, props: { name: { type: String, default: 'default', }, }, render(_, { children, parent, data, props }) { data.routerView = true const h = parent.$createElement const route = parent.$route // determine current view depth, also check to see if the tree // has been toggled inactive but kept-alive. let depth = 0 // let inactive = false // @ts-ignore while (parent && parent._routerRoot !== parent) { const vnodeData = parent.$vnode && parent.$vnode.data if (vnodeData) { // @ts-ignore if (vnodeData.routerView) { depth++ } // if (vnodeData.keepAlive && parent._inactive) { // inactive = true // } } parent = parent.$parent } data.routerViewDepth = depth const matched = route.matched[depth] if (!matched) return h() const component = matched.components[props.name] return h(component, data, children) }, } export default View
- 引入依賴
引入 vue 中的 component 組件,然后注冊一個 name 為 RouterView 的組件,這里的functional:true 表示這個組件是一個函數(shù)式組件。
import { Component } from 'vue' const View: Component = { name: 'RouterView', functional: true, props: { name: { type: String, default: 'default', }, }
組件內(nèi)部定義了一個 props,接受參數(shù) name。一般情況下,router-view 很少傳 name,所以這個 參數(shù)可以忽略
- render函數(shù)的參數(shù)通過解構(gòu)賦值拿到組件內(nèi)部屬性
我們在 render 中拿到了 children、parent、data、props 等屬性。在 parent 中我們?nèi)〉搅?nbsp;$createElement 內(nèi)部方法,以及 route 路由信息,然后定義了一個 depth 變量,并初始賦值為 0。
render(_, { children, parent, data, props }) { data.routerView = true const h = parent.$createElement const route = parent.$route // determine current view depth, also check to see if the tree // has been toggled inactive but kept-alive. let depth = 0
這個 depth 意義重大!一開始我也在疑惑,vueRouter 在設計時為什么要有這 個 depth,這個變量有什么用?
后來仔細翻看了代碼,發(fā)現(xiàn) depth 記錄了每一條路由的索引,然后又將該索引賦值給$vnode->data->routerViewDepth
// has been toggled inactive but kept-alive. let depth = 0 // let inactive = false // @ts-ignore while (parent && parent._routerRoot !== parent) { const vnodeData = parent.$vnode && parent.$vnode.data if (vnodeData) { // @ts-ignore if (vnodeData.routerView) { depth++ } // if (vnodeData.keepAlive && parent._inactive) { // inactive = true // } } parent = parent.$parent } data.routerViewDepth = depth const matched = route.matched[depth]
注意看:routerViewDepth 就是這個 depth 值。
可是,routerViewDepth 只是記錄了這個值而已,depth 難道沒有其他作用了嗎?當然不是,如果僅僅是記錄一個索引值,但就沒必要大動干戈地調(diào)用 while 循環(huán)深度遍歷了。所以,我們繼續(xù)往下看:
const matched = route.matched[depth] if (!matched) return h() const component = matched.components[props.name] return h(component, data, children)
在當前路由下,有個 route 對象,里面記錄了頁面的基本信息。其中有一個 matched 屬性至關重要,它是一個 components 集合。也就是說,在當前路由下的所有組件(包括父級 組件以及兄弟組件),都存在于這個 matched 集合里。
而 depth 是當前路由的索引,也就是 matched 集合中的 key。通過 depth 我們能對應到路由中對應好的 component。
所以我們前面為什么說depth 意義重大,原因就在這里。
const matched = route.matched[depth] if (!matched) return h() const component = matched.components[props.name] return h(component, data, children)
最后,源碼中增加了一層判斷,如果沒有找到 matched 則返回一個空的 vnode,如果找到了則返回對應的 component,最后完成渲染。
matched
matched 集合是從路由記錄樹中根據(jù)當前路由的路徑生成的。當發(fā)生路由導航時,Vue Router 會遍歷路由配置,并根據(jù)當前路徑匹配對應的路由記錄。這個過程是自動完成的,無需手動操作。
例如:假設你的路由如下:
const routes = [ { path: '/', name: 'Home', component: Home, children:[ { path: '/about', name: 'About', component: About, }, ] }, // ... ];
當訪問 /about 路徑時,Vue Router 會生成一個 matched數(shù)組,其中包含與當前路徑 /about匹配的路由記錄。在這種情況下,matched數(shù)組可能如下所示:
[ { name:'Home', alias: xx, beforeEnter: (...), components: Object, enteredCbs: (...), instances: (...), matchAs: (...), meta: (...), xxx }, { alias: xx, name:'About', beforeEnter: (...), components: Object, enteredCbs: (...), instances: (...), matchAs: (...), meta: (...), xxx }, ]
matched數(shù)組的順序是根據(jù)路由配置的嵌套關系確定的,父級路由記錄在數(shù)組中的順序靠前。
注意,matched數(shù)組是在路由導航過程中生成的,因此在路由導航之前或?qū)Ш降轿炊x的路徑時,matched 數(shù)組可能為空。
可以通過在路由組件中訪問 this.$route.matched 來獲取當前路由的匹配路由記錄數(shù)組。
總結(jié)
雖然現(xiàn)在已經(jīng)進入了 vue3 的世界,但是 vue2 源碼中的諸多設計仍然值得我們借鑒,技術始終服務于業(yè)務。相信現(xiàn)在不少公司仍然有大量的 vue2 項目在維護,推翻 shi 山,深度重構(gòu)不是一朝一夕的事。對于源碼,我們可以抱著學習的態(tài)度去看待,或者看成是一種興趣。源碼看多了,你的代碼不會再 shi,在不知不覺中逐漸向優(yōu)秀的人靠齊。
以上就是詳解vue中的router-view組件是如何生成的的詳細內(nèi)容,更多關于vue router-view組件生成的資料請關注腳本之家其它相關文章!
相關文章
vue 函數(shù)調(diào)用加括號與不加括號的區(qū)別
這篇文章主要介紹了vue 函數(shù)調(diào)用加括號與不加括號的區(qū)別,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-10-10axios解決高并發(fā)的方法:axios.all()與axios.spread()的操作
這篇文章主要介紹了axios解決高并發(fā)的方法:axios.all()與axios.spread()的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11Vue利用路由鉤子token過期后跳轉(zhuǎn)到登錄頁的實例
下面小編就為大家?guī)硪黄猇ue利用路由鉤子token過期后跳轉(zhuǎn)到登錄頁的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10vue+elementui通用彈窗的實現(xiàn)(新增+編輯)
這篇文章主要介紹了vue+elementui通用彈窗的實現(xiàn)(新增+編輯),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01