詳解vue中router-view組件的生成原理
前言
在vue的使用過程中,有一個組件,幾乎是必用的,那就是router-view。它是所有組件的入口,是單頁面系統(tǒng)的一把利劍。如果你的系統(tǒng)是火箭,那么router-view無疑將是這艘火箭的北斗衛(wèi)星。
router-view
< router-view />是 vue-router 默認(rèn)注冊的全局組件。如果你從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 的相關(guān)代碼:

下面來分析一下這個組件的源碼,看一下 vue-router 是怎么設(shè)計這個組件的,以及這種設(shè)計帶給我們的思考有哪些?
為了便于理解,我將這段源碼粘貼下來:
// @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 在設(shè)計時為什么要有這 個 depth,這個變量有什么用?
后來仔細(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 就是這個 depth 值。

可是,routerViewDepth 只是記錄了這個值而已,depth 難道沒有其他作用了嗎?當(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)前路由下,有個 route 對象,里面記錄了頁面的基本信息。其中有一個 matched 屬性至關(guān)重要,它是一個 components 集合。也就是說,在當(dāng)前路由下的所有組件(包括父級 組件以及兄弟組件),都存在于這個 matched 集合里。

而 depth 是當(dāng)前路由的索引,也就是 matched 集合中的 key。通過 depth 我們能對應(yīng)到路由中對應(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 則返回一個空的 vnode,如果找到了則返回對應(yīng)的 component,最后完成渲染。
matched
matched 集合是從路由記錄樹中根據(jù)當(dāng)前路由的路徑生成的。當(dāng)發(fā)生路由導(dǎo)航時,Vue Router 會遍歷路由配置,并根據(jù)當(dāng)前路徑匹配對應(yīng)的路由記錄。這個過程是自動完成的,無需手動操作。
例如:假設(shè)你的路由如下:
const routes = [
{
path: '/',
name: 'Home',
component: Home,
children:[
{
path: '/about',
name: 'About',
component: About,
},
]
},
// ...
];
當(dāng)訪問 /about 路徑時,Vue Router 會生成一個 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)系確定的,父級路由記錄在數(shù)組中的順序靠前。
注意,matched數(shù)組是在路由導(dǎo)航過程中生成的,因此在路由導(dǎo)航之前或?qū)Ш降轿炊x的路徑時,matched 數(shù)組可能為空。
可以通過在路由組件中訪問 this.$route.matched 來獲取當(dāng)前路由的匹配路由記錄數(shù)組。
總結(jié)
雖然現(xiàn)在已經(jīng)進(jìn)入了 vue3 的世界,但是 vue2 源碼中的諸多設(shè)計仍然值得我們借鑒,技術(shù)始終服務(wù)于業(yè)務(wù)。相信現(xiàn)在不少公司仍然有大量的 vue2 項目在維護(hù),推翻 shi 山,深度重構(gòu)不是一朝一夕的事。對于源碼,我們可以抱著學(xué)習(xí)的態(tài)度去看待,或者看成是一種興趣。源碼看多了,你的代碼不會再 shi,在不知不覺中逐漸向優(yōu)秀的人靠齊。
以上就是詳解vue中的router-view組件是如何生成的的詳細(xì)內(nèi)容,更多關(guān)于vue router-view組件生成的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue響應(yīng)式原理及雙向數(shù)據(jù)綁定示例分析
這篇文章主要為大家介紹了Vue響應(yīng)式原理及雙向數(shù)據(jù)綁定的示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
vue 函數(shù)調(diào)用加括號與不加括號的區(qū)別
這篇文章主要介紹了vue 函數(shù)調(diào)用加括號與不加括號的區(qū)別,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-10-10
axios解決高并發(fā)的方法:axios.all()與axios.spread()的操作
這篇文章主要介紹了axios解決高并發(fā)的方法:axios.all()與axios.spread()的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
Vue3?響應(yīng)式高階用法之customRef()的使用
customRef()是Vue3的高級工具,允許開發(fā)者創(chuàng)建具有復(fù)雜依賴跟蹤和自定義更新邏輯的ref對象,本文詳細(xì)介紹了customRef()的使用場景、基本用法、功能詳解以及最佳實踐,包括防抖、異步更新等用例,旨在幫助開發(fā)者更好地理解和使用這一強大功能2024-09-09
Vue利用路由鉤子token過期后跳轉(zhuǎn)到登錄頁的實例
下面小編就為大家?guī)硪黄猇ue利用路由鉤子token過期后跳轉(zhuǎn)到登錄頁的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
vue+elementui通用彈窗的實現(xiàn)(新增+編輯)
這篇文章主要介紹了vue+elementui通用彈窗的實現(xiàn)(新增+編輯),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

