vue和react中關(guān)于插槽詳解
簡(jiǎn)述Slot
slot插槽是Vue對(duì)組件嵌套這種擴(kuò)展機(jī)制的稱謂,在react可以也這樣稱呼,但是并不很常見。不過叫slot確實(shí)很形象。
這樣的形式就是slot插槽:
vue
<template>
<container-comp>
<content></content>
<footer></footer>
</container-comp>
</template>react
() => (
<ContainerComp>
<Content />
<Footer />
</ContainerComp>
)(我們可以把container-comp稱之為容器組件,把content、footer稱之為子組件)
這種機(jī)制的好處主要在于,在某個(gè)容器提供的模版或者數(shù)據(jù)中,可以根據(jù)需求靈活擴(kuò)展需要渲染的子組件。專業(yè)點(diǎn)說就是通過容器和子組件之間的協(xié)議(數(shù)據(jù)交換和渲染方式),將彼此邏輯獨(dú)立解藕,提升了各自的復(fù)用性。
舉個(gè)例子,容器組件提供了一份渲染模版,將各個(gè)模塊的位置預(yù)留出來,使用的時(shí)候根據(jù)各個(gè)子組件的順序或者插槽名稱,在不同場(chǎng)景可以選擇不同的子組件。
再舉個(gè)使用插槽的例子,容器組件中提供一些數(shù)據(jù),比如定時(shí)請(qǐng)求某種接口得到數(shù)據(jù),或者監(jiān)聽,訂閱一些數(shù)據(jù),比如在掛載完成后監(jiān)聽鼠標(biāo)事件,得到位置數(shù)據(jù)。數(shù)據(jù)拿到之后,具體的對(duì)數(shù)據(jù)的渲染方式交給子組件來做,而這時(shí)候,容器可以通過參數(shù)傳遞的方式將得到的數(shù)據(jù)交給子組件。
通過上面簡(jiǎn)單的的例子描述,可以看到,使用插槽的的代碼設(shè)計(jì)符合單一職責(zé)原則,邏輯更加內(nèi)聚。
而不管是vue還是react上述描述的這些功能都是支持的,只是有的叫法略有所別,但是其目的一致。(因?yàn)榻谠陧?xiàng)目中使用vue多一點(diǎn),而之前對(duì)vue的了解只是籠統(tǒng)的學(xué)習(xí)過響應(yīng)式原理,并沒有真正在項(xiàng)目里寫過vue,所以,近期希望結(jié)合vue和react,來系統(tǒng)的回顧回顧相關(guān)的知識(shí)點(diǎn)。)
下面就先看下vue中的插槽都有什么功能。一邊看vue,一邊對(duì)比react。 參考這里:vue2官網(wǎng):slot,可以看到,插槽相關(guān)的核心功能有:
- 基本渲染插槽內(nèi)容;
- 具名插槽:一個(gè)容器多個(gè)插槽,需要分別命名;
- 插槽傳遞參數(shù),屬于相對(duì)高級(jí)點(diǎn)的用法;
基本插槽
vue基本插槽
最簡(jiǎn)單插槽用法就像下面這樣:
<template>
<main-comp>
<div>內(nèi)容</div>
<!--
可以是任何自定義組件
<my-sub></my-sub>
-->
</main-comp>
</template>main-comp就是容器組件,而我們將<div>內(nèi)容</div>作為容器的子元素,那么,容器里面怎么寫呢?
<template>
<div class="main">
<slot>后備信息,以防萬一</slot>
</div>
</template>可以看到,在容器里面簡(jiǎn)單的使用slot標(biāo)簽,相當(dāng)于占位。當(dāng)組件渲染的時(shí)候,<slot></slot> 將會(huì)被替換為<div>內(nèi)容</div>,如果沒有使用時(shí)插槽的話,會(huì)渲染出slot標(biāo)簽內(nèi)的內(nèi)容:“后備信息,以防萬一”。當(dāng)然,使用的插槽不止是<div>內(nèi)容<div>這么簡(jiǎn)單,可以是任何自定義組件。
ok,對(duì)應(yīng)react中實(shí)現(xiàn)對(duì)應(yīng)的代碼怎么寫呢?
react基本插槽
使用插槽組件:
() => (
<MainComp>
<div>內(nèi)容</div>
{/* <MySub /> 可以是任何自定義組件 */}
</MainComp>
)容器中定義插槽:
const MainComp = (props) => {
return (
<div class="main">
{props.children ?? '后備信息,以防萬一'}
</div>
)
}react中,組件的子組件都存在props.children中,所以直接在jsx中渲染對(duì)應(yīng)的位置渲染props.children變量就可以了,而后備內(nèi)容可以應(yīng)用任何js語法,來判斷props.children收否存在,從而顯示后備內(nèi)容與否。
具名插槽
vue具名插槽
上面是最簡(jiǎn)單的場(chǎng)景,但有時(shí)候,一個(gè)容器需要渲染很多插槽,比如需要渲染一個(gè)內(nèi)容區(qū)域content和一個(gè)底部區(qū)域footer,這時(shí)候就需要對(duì)插槽命名了,稱之為具名插槽: 下面main-comp組件要使用兩個(gè)插槽,分別命名為content和footer:
<template>
<main-comp>
<template v-slot:content>
<sub-comp1></sub-comp1>
</template>
<template v-slot:footer>
<footer-comp></footer-comp>
</template>
</main-comp>
</template>定義插槽:
<template>
<div>
<slot name="content"></slot>
<slot name="footer"></slot>
</div>
</template>slot用name屬性標(biāo)注了其名字“xxx”,對(duì)應(yīng)使用的時(shí)候要用v-slot:xxx,這樣“xxx”的template就會(huì)對(duì)應(yīng)替換成name是xxx的slot的位置。有一點(diǎn)需要注意,v-slot這個(gè)指令對(duì)template生效,也就是說,要使用具名插槽,必須要用template將內(nèi)容包裹起來。
另外,如果slot顯示指定name,其實(shí)它對(duì)應(yīng)也是有name的,它默認(rèn)的name叫default。
react具名插槽的討論
具名插槽,對(duì)應(yīng)react的話,我用了這些年react,還真沒聽過“具名插槽”這個(gè)稱謂。不過盡管沒有100%一致的對(duì)應(yīng)vue的具名插槽,但類似的功能有幾種實(shí)現(xiàn)方式:
模仿具名插槽
a. 用次序約定:
我們知道react的“插槽”寫法(嵌套子組件),子組件都是作為props.children數(shù)組的子元素,那么其實(shí)最簡(jiǎn)單的一種方式就是,children的次序?qū)?yīng)著某個(gè)子插槽。比如:
使用時(shí):
() => {
return (
<MainComp>
<SubComp1 />
<FooterComp />
</MainComp>
)
}那么,props.children[0]對(duì)應(yīng)的就是SubComp1,而props.children[1]對(duì)應(yīng)的就是FooterComp,所以在MainComp內(nèi)部就可以這樣:
const MainComp = (props) => {
const content = props.children[0]
const footer = props.children[1]
return (
<div>
{ content }
{ footer }
</div>
)
}但是上述寫法的問題在于:具名呢?說好的名稱呢?使用的時(shí)候順序亂了咋辦?
b. 傳遞對(duì)象:
想要實(shí)現(xiàn)具名,可以下面這樣寫法,本質(zhì)還是props.children,我們把對(duì)象作為“插槽”內(nèi)容,對(duì)象的key就是插槽名稱,value就是子組件:
() => {
return (
<MainComp>
{
{
content:(<SubComp1 />),
footer:(<FooterComp />)
}
}
</MainComp>
)
}對(duì)應(yīng)的MainComp:
const MainComp = (props) => {
const { content, footer } = props.children
return (
<div>
{ content }
{ footer }
</div>
)
}這里這個(gè)props.children可以直接解構(gòu)對(duì)象的屬性。(有一點(diǎn)要注意的是:props.children不一定是數(shù)組,當(dāng)只有一個(gè)元素的時(shí)候就不是數(shù)組)。
c. 判斷組件的自定義靜態(tài),實(shí)現(xiàn)具名
上述這種寫法看上去和vue的功能一致了,但是坦白講,這樣的代碼在react世界里面實(shí)屬罕見。不是說寫法錯(cuò)誤,但是似乎不那么符合使用習(xí)慣。
react中類似具名的插槽其實(shí)還可以通過給子組件顯示命名方式實(shí)現(xiàn),其實(shí)就是給子組件掛了個(gè)靜態(tài)變量:
const Content = () => (<div>I am Content</div>)
const Footer = (props) => (<div>Footer Here {props.info}</div>)
// 兩個(gè)子組件標(biāo)記出來
Content.compName = 'content'
Footer.compName = 'footer'這樣在容器組件中就能通過這兩個(gè)標(biāo)記,識(shí)別出對(duì)應(yīng)的組件:
export function MainComp(props) {
const isMany = React.Children.count(props.children) > 0
let footer = (<div>footer</div>)
let content = (<div>content</div>)
if (isMany) {
React.Children.forEach(props.children, item => {
const { compName } = item.type
// 判斷子組件類型
if (compName === 'footer') footer = item
if (compName === 'content') content = item
})
}
return (
<div>
title text<br/>
{ content }
{ footer }
</div>
)
}這里面用了幾個(gè)React.Children的方法來判斷props.children,核心邏輯是當(dāng)children是數(shù)組時(shí),遍歷每一個(gè)子項(xiàng),判斷其“name”(這里我們的約定為compName),再根據(jù)name設(shè)置對(duì)應(yīng)需要渲染的子組件的變量。
isMany部分也可以這樣寫:
try {
React.Children.only(children)
} catch (e) {
React.Children.forEach(children, item => {
const { compName } = item.type
if (compName === 'footer') footer = item
if (compName === 'content') content = item
})
}這樣我們?cè)谑褂?ldquo;插槽”組件的時(shí)候就不用擔(dān)心子組件次序問題了:
() => {
return (
<MainComp>
<Footer /> {/* 先寫footer還是能正確的渲染出來 */}
<Content />
</MainComp>
)
}上面a、b、c三種方法實(shí)現(xiàn)了和vue一樣的具名的slot,第一種只是簡(jiǎn)單的次序?qū)?yīng),第二種能實(shí)現(xiàn),但是不太符合react習(xí)慣,第三種能實(shí)現(xiàn),也是react的寫法。
不過,一般簡(jiǎn)單的需求,沒必要用第三種,可以直接使用屬性傳遞組件,我也不知道應(yīng)該怎么叫,就叫屬性插槽吧。
屬性插槽
一個(gè)更符合react習(xí)慣,近似實(shí)現(xiàn)vue具名slot的方法是,直接傳props,但props的類型是組件:
() => {
return (
<MainComp
content={(<SubComp1 />)}
footer={(<FooterComp />)}
/>
)
}這樣在MainComp的內(nèi)部直接通過props去拿對(duì)應(yīng)的組件并渲染在適當(dāng)?shù)奈恢镁托校?/strong>
const MainComp = (props) => {
const { content, footer } = props
return (
<div>
{ content }
{ footer }
</div>
)
}嚴(yán)格意義上說,雖然這樣能實(shí)現(xiàn)和vue具名插槽一樣的功能,但使用卻不是用插槽的形式。 但是這樣代碼在react世界中,卻是最常見的方式。這可能是兩種框架不同特點(diǎn)導(dǎo)致的微小差異了。
插槽傳參
vue插槽傳參
插槽傳遞參數(shù),可以幫助我們實(shí)現(xiàn)一些更高級(jí)的功能。
先看下vue中,如何給插槽傳遞參數(shù):
<template>
<div class="main">
<slot
:styleProps="shareStyle"
:data="shareStyle"
:description="desc">
</slot>
</div>
</template>上面的代碼是定義slot時(shí)候,我們給slot綁定了三個(gè)屬性,這看上去和我們使用組件,給組件傳遞參數(shù)的用法沒有什么區(qū)別。
使用slot的時(shí)候這樣接收參數(shù):
<!-- slotProps 是通過main傳遞過來的 -->
<template v-slot:default="slotProps">
<!-- 默認(rèn)插槽可以簡(jiǎn)寫成 v-slot -->
<div :style="slotProps.styleProps">{{ slotProps.description}}</div>
</template>首先我們看到使用的時(shí)候在template標(biāo)簽中使用了指令v-slot,即v-slot:default="slotProps",這句話什么意思呢?
就是當(dāng)前這里template對(duì)應(yīng)default這個(gè)名稱的slot(defalut可以省略,上面定義slot的地方也沒寫name=“default”,其實(shí)是省略了)。
而這個(gè)slotProps表示所有傳遞過來的屬性,也就是說:
slotProps = {
styleProps, data, description
}所以在template中,可以使用slotProps上的任何屬性。也可以給作為插槽的自定義組件傳遞參數(shù), 比如:
<template v-slot:rightItem="slotProps"> <staff :staff="slotProps.data"></staff> </template>
vue插槽傳參大致就是這樣了,接著看看react吧。
react:render-props
react中其實(shí)沒法直接給插槽傳遞參數(shù),只能借助一點(diǎn)技術(shù)手段:函數(shù)。
這種方式有個(gè)專有名詞叫:render-props。
render-props的具體的方式就是,子組件作為插槽是用函數(shù)的形式,而容器組件渲染的時(shí)候?qū)?yīng)的就調(diào)用這個(gè)函數(shù),在調(diào)用函數(shù)的時(shí)候,把需要傳遞的參數(shù)傳入函數(shù),這樣在插槽函數(shù)的作用域內(nèi)就拿到了數(shù)據(jù):
() => {
return (
<MainComp>
{
(data) => (<Staff staff={data} />)
}
</MainComp>
)
}看下容器MainComp組件:
const MainComp = (props) => {
const [data, setData] = useState({})
useEffect(() => {
const info = await getData()
setData(info)
}, [])
return (
<div>
{
props.children && props.children(data)
}
</div>
)
}當(dāng)然了,這種函數(shù)形式的“插槽”不止可以用作插槽,用在普通的props上自然也是可以的。在react世界里,凡是要從另一個(gè)組件拿數(shù)據(jù)的場(chǎng)合,都可以考慮傳個(gè)函數(shù):
() => {
return (
<MainComp
staff={(data) => (<Staff staff={data} />)}
/>
)
}對(duì)應(yīng)的MainComp,最終也是通過函數(shù)調(diào)用給子組件傳遞參數(shù),只是獲取子組件的方式換一下:
const MainComp = (props) => {
const [data, setData] = useState({})
useEffect(() => {
const info = await getData()
setData(info)
}, [])
return (
<div>
{/* 這里變一下 */}
{
props.staff && props.staff(data)
}
</div>
)
}以上就是對(duì)兩種框架“插槽”相關(guān)的實(shí)現(xiàn)方式的簡(jiǎn)單總結(jié)。
總言之,不管vue或者react,都有著很靈活的用法,上面的這些要素和技巧,都可以在實(shí)際項(xiàng)目中可以根據(jù)需要自行組合或者擴(kuò)展。
到此這篇關(guān)于vue和react中關(guān)于插槽詳解的文章就介紹到這了,更多相關(guān)vue react 插槽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue項(xiàng)目打包成exe可執(zhí)行文件的實(shí)現(xiàn)過程(無瑕疵,完美版)
突然接到公司需求,說客戶想讓我們把項(xiàng)目直接打包,所以下面這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目打包成exe可執(zhí)行文件的實(shí)現(xiàn)過程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
Vue使用axios發(fā)送請(qǐng)求并實(shí)現(xiàn)簡(jiǎn)單封裝的示例詳解
這篇文章主要介紹了Vue使用axios發(fā)送請(qǐng)求并實(shí)現(xiàn)簡(jiǎn)單封裝,主要包括安裝axios及簡(jiǎn)單使用配置方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
el-form-item中表單項(xiàng)label和表單項(xiàng)內(nèi)容換行實(shí)現(xiàn)方法
這篇文章主要給大家介紹了el-form-item中表單項(xiàng)label和表單項(xiàng)內(nèi)容換行實(shí)現(xiàn)的相關(guān)資料,每個(gè)表單el-form由多個(gè)表單域el-form-item組成,需要的朋友可以參考下2023-09-09
vue_drf實(shí)現(xiàn)短信驗(yàn)證碼
我們?cè)谧鼍W(wǎng)站開發(fā)時(shí),登錄頁(yè)面很多情況下是可以用手機(jī)號(hào)接收短信驗(yàn)證碼,本文主要介紹了vue_drf實(shí)現(xiàn)短信驗(yàn)證碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Vue v-model相關(guān)知識(shí)總結(jié)
這篇文章主要介紹了Vue ​v-model相關(guān)知識(shí)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下2021-01-01

