詳解vue中router-view組件的生成原理
前言
在vue的使用過(guò)程中,有一個(gè)組件,幾乎是必用的,那就是router-view。它是所有組件的入口,是單頁(yè)面系統(tǒng)的一把利劍。如果你的系統(tǒng)是火箭,那么router-view無(wú)疑將是這艘火箭的北斗衛(wèi)星。
router-view
< router-view />是 vue-router 默認(rèn)注冊(cè)的全局組件。如果你從0搭建過(guò)系統(tǒng)的話,一定記得在Layout或者AppMain組件中有過(guò)這樣一行代碼:
<transition name="fade-transform" mode="out-in">
<div style="height: 100%">
// 所有組件的入口
<router-view />
</div>
</transition>
我們一直在用,但是有沒(méi)有想過(guò):這個(gè)組件是干嘛用的?為什么注冊(cè)為全局組件,又為什么是所有頁(yè)面組件的入口?它……究竟有什么用?
帶著這樣的疑問(wèn),我打開(kāi)了 vue-router 的gitHub切換到 vue2 分支

然后 在 src->components->view.ts 中找到了 router-view 的相關(guān)代碼:

下面來(lái)分析一下這個(gè)組件的源碼,看一下 vue-router 是怎么設(shè)計(jì)這個(gè)組件的,以及這種設(shè)計(jì)帶給我們的思考有哪些?
為了便于理解,我將這段源碼粘貼下來(lái):
// @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ù)通過(guò)解構(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 意義重大!一開(kāi)始我也在疑惑,vueRouter 在設(shè)計(jì)時(shí)為什么要有這 個(gè) depth,這個(gè)變量有什么用?
后來(lái)仔細(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 難道沒(méi)有其他作用了嗎?當(dāng)然不是,如果僅僅是記錄一個(gè)索引值,但就沒(méi)必要大動(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ì)象,里面記錄了頁(yè)面的基本信息。其中有一個(gè) matched 屬性至關(guān)重要,它是一個(gè) components 集合。也就是說(shuō),在當(dāng)前路由下的所有組件(包括父級(jí) 組件以及兄弟組件),都存在于這個(gè) matched 集合里。

而 depth 是當(dāng)前路由的索引,也就是 matched 集合中的 key。通過(guò) depth 我們能對(duì)應(yīng)到路由中對(duì)應(yīng)好的 component。
所以我們前面為什么說(shuō)depth 意義重大,原因就在這里。
const matched = route.matched[depth]
if (!matched) return h()
const component = matched.components[props.name]
return h(component, data, children)
最后,源碼中增加了一層判斷,如果沒(méi)有找到 matched 則返回一個(gè)空的 vnode,如果找到了則返回對(duì)應(yīng)的 component,最后完成渲染。
matched
matched 集合是從路由記錄樹(shù)中根據(jù)當(dāng)前路由的路徑生成的。當(dāng)發(fā)生路由導(dǎo)航時(shí),Vue Router 會(huì)遍歷路由配置,并根據(jù)當(dāng)前路徑匹配對(duì)應(yīng)的路由記錄。這個(gè)過(guò)程是自動(dòng)完成的,無(wú)需手動(dòng)操作。
例如:假設(shè)你的路由如下:
const routes = [
{
path: '/',
name: 'Home',
component: Home,
children:[
{
path: '/about',
name: 'About',
component: About,
},
]
},
// ...
];
當(dāng)訪問(wèn) /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)航過(guò)程中生成的,因此在路由導(dǎo)航之前或?qū)Ш降轿炊x的路徑時(shí),matched 數(shù)組可能為空。
可以通過(guò)在路由組件中訪問(wèn) this.$route.matched 來(lái)獲取當(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,在不知不覺(jué)中逐漸向優(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)畫(huà)過(guò)度及混入使用介紹
最近在寫(xiě)vue的一個(gè)項(xiàng)目要實(shí)現(xiàn)過(guò)渡的效果,雖然vue動(dòng)畫(huà)不是強(qiáng)項(xiàng),庫(kù)也多,但是基本的坑還是得踩扎實(shí),下面這篇文章主要給大家介紹了關(guān)于Vue中實(shí)現(xiàn)過(guò)渡動(dòng)畫(huà)效果的相關(guān)資料,需要的朋友可以參考下2023-03-03
Vue響應(yīng)式原理及雙向數(shù)據(jù)綁定示例分析
這篇文章主要為大家介紹了Vue響應(yīng)式原理及雙向數(shù)據(jù)綁定的示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
vue 函數(shù)調(diào)用加括號(hào)與不加括號(hào)的區(qū)別
這篇文章主要介紹了vue 函數(shù)調(diào)用加括號(hào)與不加括號(hào)的區(qū)別,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-10-10
axios解決高并發(fā)的方法:axios.all()與axios.spread()的操作
這篇文章主要介紹了axios解決高并發(fā)的方法:axios.all()與axios.spread()的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
Vue3?響應(yīng)式高階用法之customRef()的使用
customRef()是Vue3的高級(jí)工具,允許開(kāi)發(fā)者創(chuàng)建具有復(fù)雜依賴跟蹤和自定義更新邏輯的ref對(duì)象,本文詳細(xì)介紹了customRef()的使用場(chǎng)景、基本用法、功能詳解以及最佳實(shí)踐,包括防抖、異步更新等用例,旨在幫助開(kāi)發(fā)者更好地理解和使用這一強(qiáng)大功能2024-09-09
VUE開(kāi)發(fā)一個(gè)圖片輪播的組件示例代碼
本篇文章主要介紹了VUE開(kāi)發(fā)一個(gè)圖片輪播的組件示例代碼,對(duì)圖片輪播效果感興趣的小伙伴們可以參考一下。2017-03-03
Vue利用路由鉤子token過(guò)期后跳轉(zhuǎn)到登錄頁(yè)的實(shí)例
下面小編就為大家?guī)?lái)一篇Vue利用路由鉤子token過(guò)期后跳轉(zhuǎn)到登錄頁(yè)的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
vue+elementui通用彈窗的實(shí)現(xiàn)(新增+編輯)
這篇文章主要介紹了vue+elementui通用彈窗的實(shí)現(xiàn)(新增+編輯),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

