Vue 技巧之控制父類的 slot
首先來(lái)思考一個(gè)問(wèn)題:是否有一種方法可以從子組件填充父組件的插槽?
最近一位同事問(wèn)我這個(gè)問(wèn)題,答案很簡(jiǎn)單:可以的。但我的解決方案可能和你想的完全不一樣,這是涉及一個(gè)棘手的Vue架構(gòu)問(wèn)題,但也是一個(gè)非常有趣的問(wèn)題。
為什么會(huì)有這個(gè)問(wèn)題
在我們的應(yīng)用程序中,我們有一個(gè)頂部欄,其中包含不同的按鈕、搜索欄和其他一些控件。根據(jù)每個(gè)人所在的頁(yè)面,它可能略有不同,因此我們需要一種基于每個(gè)頁(yè)面配置它的方法。
為此,我們希望每個(gè)頁(yè)面都能夠配置操作欄??雌饋?lái)很簡(jiǎn)單,但這里有個(gè)問(wèn)題
這個(gè)頂部欄(我們稱之為ActionBar
)實(shí)際上是我們的主布局的一部分,結(jié)構(gòu)如下:
<template> <div> <FullPageError /> <ActionBar /> <App /> </div> </template>
根據(jù)你所在的頁(yè)面/路線動(dòng)態(tài)注入App
的位置。
我們可以使用ActionBar
上的一些插槽來(lái)配置它。 但是,我們?nèi)绾螐?code>App組件中控制這些插槽?
定義問(wèn)題
首先,最好是盡可能清楚地知道我們要解決的問(wèn)題。
我們來(lái)看一個(gè)具有一個(gè)子組件和一個(gè)插槽的組件:
// Parent.vue <template> <div> <Child /> <slot /> </div> </template>
我們可以這樣填充Parent
的插槽:
// App.vue <template> <Parent> <p>This content goes into the slot</p> </Parent> </template>
這里沒(méi)什么特別的。。。
填充子組件的插槽很容易,這也是使用插槽的最常見(jiàn)方式。
但是,有沒(méi)有一種方法可以控制從Child
組件內(nèi)部進(jìn)入Parent
組件slot
的內(nèi)容呢?
換種說(shuō)法:我們可以讓子組件填充父組件的插槽嗎?來(lái)看看我想到的第一個(gè)解決方案。
向下使用 props,向上使用 event
數(shù)據(jù)流經(jīng)組件樹(shù)的唯一途徑是使用props
。 而向上通信的方法是使用事件。這意味著,如果要讓子組件與父組件進(jìn)行通信,我們需要使用事件來(lái)實(shí)現(xiàn)。
因此,我們將使用事件來(lái)將內(nèi)容傳遞到ActionBars
槽中
import SlotContent from './SlotContent'; export default { name: 'Application', created() { // As soon as this component is created we'll emit our events this.$emit('slot-content', SlotContent); } };
我們將要放入插槽中的所有內(nèi)容打包到SlotContent
組件中。 一旦創(chuàng)建了應(yīng)用程序組件,我們就會(huì)發(fā)出slot-content
事件,并傳遞我們要使用的組件。
我們的組件結(jié)構(gòu)如下:
<template> <div> <FullPageError /> <ActionBar> <Component :is="slotContent" /> </ActionBar> <App @slot-content="component => slotContent = component" /> </div> </template>
監(jiān)聽(tīng)該事件,并將slotContent
設(shè)置為我們的App
組件發(fā)送給我們的任何內(nèi)容。 然后,使用內(nèi)置的Component
,就可以動(dòng)態(tài)地渲染該組件。
但是,通過(guò)事件傳遞組件感覺(jué)很奇怪,并非是主流的做法。幸運(yùn)的是,還有一種方法可以完全避免使用事件。
使用 $options
由于Vue組件只是 JS 對(duì)象,因此我們可以向它們添加所需的任何屬性。無(wú)需使用事件傳遞插槽內(nèi)容,我們只需將其作為字段添加到組件中即可:
// App.vue import SlotContent from './SlotContent'; export default { name: 'Application', slotContent: SlotContent, props: { /***/ }, computed: { /***/ }, };
在主頁(yè)中通過(guò) App.slotContent
獲取對(duì)應(yīng)的組件
<template> <div> <FullPageError /> <ActionBar> <Component :is="slotContent" /> </ActionBar> <App /> </div> </template> import App from './App'; import FullPageError from './FullPageError'; import ActionBar from './ActionBar'; export default { name: 'Scaffold', components: { App, FullPageError, ActionBar, } data() { return { slotContent: App.slotContent, } }, };
這更像是靜態(tài)配置,更美觀、更簡(jiǎn)潔,但這仍然是不對(duì)的。
理想情況下,我們不會(huì)在代碼中混合使用范式,所有操作應(yīng)該都是以聲明方式完成。
但是在這里,我們沒(méi)有將我們的組件組合在一起,而是將它們作為 JS 對(duì)象傳遞。如果我們能以正常的Vue方式把我們想要的寫在插槽里就好了。
考慮 Portal(傳送門)
Vue 中的 Portal 技術(shù) 在 Vue 項(xiàng)目中,我們使用模板來(lái)聲明 dom嵌套關(guān)系,然而有時(shí)候一些組件需要脫離固定的層級(jí)關(guān)系,不再受制與層疊上下文,比如說(shuō) Modal 和 Dialog
這種組件就希望能夠脫離當(dāng)前模板所在的層疊上下文。在 Vue 中有兩種方式來(lái)實(shí)現(xiàn)這種效果,一種是使用指令,操作真實(shí) dom,使用熟知的 dom 操作方法將指令所在的元素 append
到另外一個(gè) dom 節(jié)點(diǎn)上去。另一種方式就是定義一套組件,將組件內(nèi)的 vnode 轉(zhuǎn)移到另外一個(gè)組件中去,然后各自渲染。
它們的工作方式和你想象的完全一樣。你可以把任何東西從一個(gè)地方傳送到另一個(gè)地方。在我們的例子中,我們將元素從DOM中的一個(gè)位置“傳送”到另一個(gè)位置。
無(wú)論組件樹(shù)如何顯示,我們都可以控制組件在DOM中的顯示位置。
例如,假設(shè)我們想要填充一個(gè)modal
。但是我們的modal
必須在根頁(yè)面處渲染,這樣我們才能正確地覆蓋它。首先,我們要在modal
中指定我們想要的:
<template> <div> <!-- Other components --> <Portal to="modal"> Rendered in the modal. </Portal> </div> </template>
然后,在我們的modal
組件中,我們將擁有另一個(gè)將內(nèi)容渲染出來(lái)的 portal:
<template> <div> <h1>Modal</h1> <Portal from="modal" /> </div> </template>
這是一項(xiàng)改進(jìn),因?yàn)楝F(xiàn)在我們實(shí)際上是在編寫HTML,而不僅僅是傳遞對(duì)象。 它更具聲明性,更容易查看應(yīng)用程序中發(fā)生的事情。
由于 portal 在背后執(zhí)行一些操作以在不同位置渲染元素,因此它完全打破了DOM渲染在Vue中工作方式的模型。 看起來(lái)您正在正常渲染元素,但根本無(wú)法正常工作,這可能會(huì)引起很多混亂和沮喪。
還有一個(gè)很大的問(wèn)題,稍后我們會(huì)講到。
提升狀態(tài)
“提升狀態(tài)”是指將狀態(tài)從子組件移動(dòng)到父組件或祖父組件,將它向上移動(dòng)到組件樹(shù)中。
這可能對(duì)應(yīng)用程序的體系結(jié)構(gòu)產(chǎn)生較大的影響。對(duì)于我們的目的,這會(huì)是更簡(jiǎn)單的解決方案。
這里的“狀態(tài)”是我們?cè)噲D傳遞到ActionBar
組件插槽中的內(nèi)容。但是該狀態(tài)包含在Page
組件中,我們不能真正將 page
特定的邏輯移到layout
組件中。 我們的狀態(tài)必須保留在我們正在動(dòng)態(tài)渲染的Page
組件內(nèi)。
因此,我們必須提升整個(gè)Page
組件才能提升狀態(tài)。當(dāng)前,我們的Page
組件是Layout
組件的子組件:
<template> <div> <FullPageError /> <ActionBar /> <Page /> </div> </template>
解除它需要我們將其翻轉(zhuǎn),并使Layout
組件成為Page
組件的子組件。 我們的Page
組件看起來(lái)像這樣:
<template> <Layout> <!-- Page-specific content --> </Layout> </template>
現(xiàn)在,我們的Layout
組件將看起來(lái)像這樣,我們可以在其中使用插槽插入頁(yè)面內(nèi)容:
<template> <div> <FullPageError /> <ActionBar /> <slot /> </div> </template>
但這還不能讓我們自定義任何內(nèi)容。 我們必須在Layout
組件中添加一些命名的插槽,以便我們可以傳遞應(yīng)放置在ActionBar
中的內(nèi)容。
最簡(jiǎn)單的方法是使用一個(gè)插槽來(lái)完全替代ActionBar
組件:
<template> <div> <FullPageError /> <slot name="actionbar"> <ActionBar /> </slot> <slot /> </div> </template>
這樣,如果你不指定“actionbar”
插槽,默認(rèn)使用ActionBar
組件。 但我們可以使用自己的自定義ActionBar
配置覆蓋此插槽:
<template> <Layout> <template #actionbar> <ActionBar> <!-- Custom content that goes into the action bar --> </ActionBar> </template> <!-- Page-specific content --> </Layout> </template>
對(duì)我來(lái)說(shuō),這是一種理想的處理方式,但是它確實(shí)需要我們重構(gòu)頁(yè)面的布局方式。 對(duì)于界面復(fù)雜點(diǎn)的,這可能是一項(xiàng)艱巨的任務(wù)。
簡(jiǎn)化一下
當(dāng)我們第一次定義問(wèn)題時(shí):
我們可以讓子組件填充父組件的插槽嗎?
但實(shí)際上,這個(gè)問(wèn)題與props
沒(méi)有任何關(guān)系。 更簡(jiǎn)單地說(shuō),它是關(guān)于使子組件控制在其自己的子樹(shù)之外渲染的內(nèi)容。
我們可以這樣表述問(wèn)題
組件控制在其子組件之外渲染的內(nèi)容的最佳方法是什么?
通過(guò)這個(gè)鏡頭檢查我們提出的每個(gè)解決方案,都會(huì)為我們提供一個(gè)有趣的新視角。
向父組件發(fā)出事件
數(shù)據(jù)流經(jīng)組件樹(shù)的唯一途徑是使用 props
。 而向上通信的方法是使用事件。這意味著,如果要讓子組件與父組件進(jìn)行通信,我們需要使用事件來(lái)實(shí)現(xiàn)。
靜態(tài)配置
只是將必要的信息提供給其他組件,而不是主動(dòng)地要求另一個(gè)組件做事情。
傳送門
組件無(wú)法控制其子樹(shù)之外的內(nèi)容。這里的每個(gè)方法都是讓另一個(gè)組件執(zhí)行我們的命令并控制我們真正感興趣的元素不同的方式。
在這方面,使用 portal
更好的原因是它們?cè)试S我們將所有這些通信邏輯封裝到單獨(dú)的組件中。
提升狀態(tài)
提升狀態(tài)是一種比我們前面看到的3種更簡(jiǎn)單、更強(qiáng)大的技術(shù),這里我們的主要限制是我們想要控制的內(nèi)容在子組件之外。
最簡(jiǎn)單的解決方法是:
提升狀態(tài)以及操縱該狀態(tài)的邏輯,使我們可以擁有更大范圍的組件,并將目標(biāo)元素包含在該組件中。如果可以這樣做,這是解決此特定問(wèn)題以及所有相關(guān)問(wèn)題的最簡(jiǎn)單方法。
請(qǐng)記住,這并不一定意味著要提升整個(gè)組件。 你也可以重構(gòu)你的應(yīng)用程序,以將邏輯移到組件樹(shù)中更高的組件中。
依賴注入
如果熟悉軟件工程設(shè)計(jì)模式的人可能已經(jīng)注意到,我們?cè)谶@里所做的是依賴注入,這是我們?cè)谲浖こ讨幸呀?jīng)使用了幾十年的技術(shù)。
它的用途之一是編寫易于配置的代碼。在我們的例子中,,我們?cè)谑褂玫拿總€(gè)Page
中以不同的方式配置Layout
組件。
當(dāng)調(diào)換Page
和Layout
組件時(shí),我們正在執(zhí)行所謂的控件反轉(zhuǎn)。
在基于組件的框架中,父組件控制子組件的操作,因此我們選擇讓Page
來(lái)控制Layout
組件,而不是由Layout
組件控制Page。
為了做到這一點(diǎn),我們使用插槽為Layout
組件提供完成任務(wù)所需的內(nèi)容。
正如我們所看到的,使用依賴注入可以使我們的代碼更加模塊化和易于配置。
總結(jié)
我們討論了解決這個(gè)問(wèn)題的4種不同方法,展示了每種方法的優(yōu)缺點(diǎn)。然后我們更進(jìn)一步,將問(wèn)題轉(zhuǎn)化為一個(gè)更一般的問(wèn)題,即控制組件子樹(shù)之外的內(nèi)容。
、提升狀態(tài)和依賴項(xiàng)注入是兩個(gè)非常有用的模式。它們是我們武器庫(kù)中最好的工具,因?yàn)樗鼈兛梢詰?yīng)用于無(wú)數(shù)的軟件開(kāi)發(fā)問(wèn)題。
但最重要的是,希望你還能學(xué)會(huì):
通過(guò)使用一些常見(jiàn)的軟件模式,將一個(gè)丑陋解決方案的問(wèn)題轉(zhuǎn)變成一個(gè)非常優(yōu)雅的問(wèn)題。許多其他的問(wèn)題都可以用這種方法解決,即把一個(gè)丑陋的、復(fù)雜的問(wèn)題轉(zhuǎn)化成一個(gè)更簡(jiǎn)單、更容易解決的問(wèn)題。
代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。
原文:https://dev.to/michaelthiesse...
以上就是Vue 技巧之控制父類的 slot的詳細(xì)內(nèi)容,更多關(guān)于Vue控制父類的 slot的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)動(dòng)態(tài)菜單
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)動(dòng)態(tài)菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Vue動(dòng)態(tài)路由路徑重復(fù)及刷新丟失頁(yè)面問(wèn)題的解決
這篇文章主要介紹了Vue動(dòng)態(tài)路由路徑重復(fù)及刷新丟失頁(yè)面問(wèn)題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng)
這篇文章主要介紹了Vue2?響應(yīng)式系統(tǒng)之深度響應(yīng),文章基于Vue2?響應(yīng)式系統(tǒng)的相關(guān)資料展開(kāi)對(duì)Vue2?深度響應(yīng)的介紹,需要的小伙伴可以參考一下2022-04-04vue3.0中使用websocket,封裝到公共方法的實(shí)現(xiàn)
這篇文章主要介紹了vue3.0中使用websocket,封裝到公共方法的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Vue3.0版本強(qiáng)勢(shì)升級(jí)點(diǎn)特性詳解
這篇文章主要介紹了Vue3.0版本強(qiáng)勢(shì)升級(jí)點(diǎn)特性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-06-06vue如何將base64流數(shù)據(jù)轉(zhuǎn)成pdf文件并在新頁(yè)面打開(kāi)
這篇文章主要介紹了vue如何將base64流數(shù)據(jù)轉(zhuǎn)成pdf文件并在新頁(yè)面打開(kāi)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02vue監(jiān)聽(tīng)sessionStorage中值的變化方式
這篇文章主要介紹了vue監(jiān)聽(tīng)sessionStorage中值的變化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07vue實(shí)現(xiàn)可以快進(jìn)后退的跑馬燈組件
這篇文章主要為大家詳細(xì)介紹了vue編寫一個(gè)可以快進(jìn)后退的跑馬燈組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04