<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
slot插槽是Vue對元件巢狀這種擴充套件機制的稱謂,在react可以也這樣稱呼,但是並不很常見。不過叫slot確實很形象。
這樣的形式就是slot插槽:
vue
<template> <container-comp> <content></content> <footer></footer> </container-comp> </template>
react
() => ( <ContainerComp> <Content /> <Footer /> </ContainerComp> )
(我們可以把container-comp稱之為容器元件,把content、footer稱之為子元件)
這種機制的好處主要在於,在某個容器提供的模版或者資料中,可以根據需求靈活擴充套件需要渲染的子元件。專業點說就是通過容器和子元件之間的協定(資料交換和渲染方式),將彼此邏輯獨立解藕,提升了各自的複用性。
舉個例子,容器元件提供了一份渲染模版,將各個模組的位置預留出來,使用的時候根據各個子元件的順序或者插槽名稱,在不同場景可以選擇不同的子元件。
再舉個使用插槽的例子,容器元件中提供一些資料,比如定時請求某種介面得到資料,或者監聽,訂閱一些資料,比如在掛載完成後監聽滑鼠事件,得到位置資料。資料拿到之後,具體的對資料的渲染方式交給子元件來做,而這時候,容器可以通過引數傳遞的方式將得到的資料交給子元件。
通過上面簡單的的例子描述,可以看到,使用插槽的的程式碼設計符合單一職責原則,邏輯更加內聚。
而不管是vue還是react上述描述的這些功能都是支援的,只是有的叫法略有所別,但是其目的一致。(因為近期在專案中使用vue多一點,而之前對vue的瞭解只是籠統的學習過響應式原理,並沒有真正在專案裡寫過vue,所以,近期希望結合vue和react,來系統的回顧回顧相關的知識點。)
下面就先看下vue中的插槽都有什麼功能。一邊看vue,一邊對比react。 參考這裡:vue2官網:slot,可以看到,插槽相關的核心功能有:
最簡單插槽用法就像下面這樣:
<template> <main-comp> <div>內容</div> <!-- 可以是任何自定義元件 <my-sub></my-sub> --> </main-comp> </template>
main-comp就是容器元件,而我們將<div>內容</div>
作為容器的子元素,那麼,容器裡面怎麼寫呢?
<template> <div class="main"> <slot>後備資訊,以防萬一</slot> </div> </template>
可以看到,在容器裡面簡單的使用slot標籤,相當於佔位。當元件渲染的時候,<slot></slot>
將會被替換為<div>內容</div>
,如果沒有使用時插槽的話,會渲染出slot標籤內的內容:“後備資訊,以防萬一”。當然,使用的插槽不止是<div>內容<div>
這麼簡單,可以是任何自定義元件。
ok,對應react中實現對應的程式碼怎麼寫呢?
使用插槽元件:
() => ( <MainComp> <div>內容</div> {/* <MySub /> 可以是任何自定義元件 */} </MainComp> )
容器中定義插槽:
const MainComp = (props) => { return ( <div class="main"> {props.children ?? '後備資訊,以防萬一'} </div> ) }
react中,元件的子元件都存在props.children中,所以直接在jsx中渲染對應的位置渲染props.children變數就可以了,而後備內容可以應用任何js語法,來判斷props.children收否存在,從而顯示後備內容與否。
上面是最簡單的場景,但有時候,一個容器需要渲染很多插槽,比如需要渲染一個內容區域content和一個底部區域footer,這時候就需要對插槽命名了,稱之為具名插槽: 下面main-comp元件要使用兩個插槽,分別命名為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屬性標註了其名字“xxx”,對應使用的時候要用v-slot:xxx
,這樣“xxx”的template就會對應替換成name是xxx的slot的位置。有一點需要注意,v-slot這個指令對template生效,也就是說,要使用具名插槽,必須要用template將內容包裹起來。
另外,如果slot顯示指定name,其實它對應也是有name的,它預設的name叫default。
具名插槽,對應react的話,我用了這些年react,還真沒聽過“具名插槽”這個稱謂。不過儘管沒有100%一致的對應vue的具名插槽,但類似的功能有幾種實現方式:
a. 用次序約定:
我們知道react的“插槽”寫法(巢狀子元件),子元件都是作為props.children陣列的子元素,那麼其實最簡單的一種方式就是,children的次序對應著某個子插槽。比如:
使用時:
() => { return ( <MainComp> <SubComp1 /> <FooterComp /> </MainComp> ) }
那麼,props.children[0]
對應的就是SubComp1,而props.children[1]
對應的就是FooterComp,所以在MainComp內部就可以這樣:
const MainComp = (props) => { const content = props.children[0] const footer = props.children[1] return ( <div> { content } { footer } </div> ) }
但是上述寫法的問題在於:具名呢?說好的名稱呢?使用的時候順序亂了咋辦?
b. 傳遞物件:
想要實現具名,可以下面這樣寫法,本質還是props.children,我們把物件作為“插槽”內容,物件的key就是插槽名稱,value就是子元件:
() => { return ( <MainComp> { { content:(<SubComp1 />), footer:(<FooterComp />) } } </MainComp> ) }
對應的MainComp:
const MainComp = (props) => { const { content, footer } = props.children return ( <div> { content } { footer } </div> ) }
這裡這個props.children可以直接解構物件的屬性。(有一點要注意的是:props.children不一定是陣列,當只有一個元素的時候就不是陣列)。
c. 判斷元件的自定義靜態,實現具名
上述這種寫法看上去和vue的功能一致了,但是坦白講,這樣的程式碼在react世界裡面實屬罕見。不是說寫法錯誤,但是似乎不那麼符合使用習慣。
react中類似具名的插槽其實還可以通過給子元件顯示命名方式實現,其實就是給子元件掛了個靜態變數:
const Content = () => (<div>I am Content</div>) const Footer = (props) => (<div>Footer Here {props.info}</div>) // 兩個子元件標記出來 Content.compName = 'content' Footer.compName = 'footer'
這樣在容器元件中就能通過這兩個標記,識別出對應的元件:
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> ) }
這裡面用了幾個React.Children的方法來判斷props.children,核心邏輯是當children是陣列時,遍歷每一個子項,判斷其“name”(這裡我們的約定為compName),再根據name設定對應需要渲染的子元件的變數。
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 }) }
這樣我們在使用“插槽”元件的時候就不用擔心子元件次序問題了:
() => { return ( <MainComp> <Footer /> {/* 先寫footer還是能正確的渲染出來 */} <Content /> </MainComp> ) }
上面a、b、c三種方法實現了和vue一樣的具名的slot,第一種只是簡單的次序對應,第二種能實現,但是不太符合react習慣,第三種能實現,也是react的寫法。
不過,一般簡單的需求,沒必要用第三種,可以直接使用屬性傳遞元件,我也不知道應該怎麼叫,就叫屬性插槽吧。
一個更符合react習慣,近似實現vue具名slot的方法是,直接傳props,但props的型別是元件:
() => { return ( <MainComp content={(<SubComp1 />)} footer={(<FooterComp />)} /> ) }
這樣在MainComp的內部直接通過props去拿對應的元件並渲染在適當的位置就行:
const MainComp = (props) => { const { content, footer } = props return ( <div> { content } { footer } </div> ) }
嚴格意義上說,雖然這樣能實現和vue具名插槽一樣的功能,但使用卻不是用插槽的形式。 但是這樣程式碼在react世界中,卻是最常見的方式。這可能是兩種框架不同特點導致的微小差異了。
插槽傳遞引數,可以幫助我們實現一些更高階的功能。
先看下vue中,如何給插槽傳遞引數:
<template> <div class="main"> <slot :styleProps="shareStyle" :data="shareStyle" :description="desc"> </slot> </div> </template>
上面的程式碼是定義slot時候,我們給slot繫結了三個屬性,這看上去和我們使用元件,給元件傳遞引數的用法沒有什麼區別。
使用slot的時候這樣接收引數:
<!-- slotProps 是通過main傳遞過來的 --> <template v-slot:default="slotProps"> <!-- 預設插槽可以簡寫成 v-slot --> <div :style="slotProps.styleProps">{{ slotProps.description}}</div> </template>
首先我們看到使用的時候在template標籤中使用了指令v-slot,即v-slot:default="slotProps"
,這句話什麼意思呢?
就是當前這裡template對應default這個名稱的slot(defalut可以省略,上面定義slot的地方也沒寫name=“default”,其實是省略了)。
而這個slotProps表示所有傳遞過來的屬性,也就是說:
slotProps = { styleProps, data, description }
所以在template中,可以使用slotProps上的任何屬性。也可以給作為插槽的自定義元件傳遞引數, 比如:
<template v-slot:rightItem="slotProps"> <staff :staff="slotProps.data"></staff> </template>
vue插槽傳參大致就是這樣了,接著看看react吧。
react中其實沒法直接給插槽傳遞引數,只能藉助一點技術手段:函數。
這種方式有個專有名詞叫:render-props。
render-props的具體的方式就是,子元件作為插槽是用函數的形式,而容器元件渲染的時候對應的就呼叫這個函數,在呼叫函數的時候,把需要傳遞的引數傳入函數,這樣在插槽函數的作用域內就拿到了資料:
() => { 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> ) }
當然了,這種函數形式的“插槽”不止可以用作插槽,用在普通的props上自然也是可以的。在react世界裡,凡是要從另一個元件拿資料的場合,都可以考慮傳個函數:
() => { return ( <MainComp staff={(data) => (<Staff staff={data} />)} /> ) }
對應的MainComp,最終也是通過函數呼叫給子元件傳遞引數,只是獲取子元件的方式換一下:
const MainComp = (props) => { const [data, setData] = useState({}) useEffect(() => { const info = await getData() setData(info) }, []) return ( <div> {/* 這裡變一下 */} { props.staff && props.staff(data) } </div> ) }
以上就是對兩種框架“插槽”相關的實現方式的簡單總結。
總言之,不管vue或者react,都有著很靈活的用法,上面的這些要素和技巧,都可以在實際專案中可以根據需要自行組合或者擴充套件。
到此這篇關於vue和react中關於插槽詳解的文章就介紹到這了,更多相關vue react 插槽內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45