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