vue 2.6 中 slot 的新用法
譯者:前端小智
原文:www.smashingmagazine.com/2019/07/usi…
為了保證的可讀性,本文采用意譯而非直譯。
最近發布不久的Vue 2.6,使用插槽的語法變得更加簡潔。 對插槽的這種改變讓我對發現插槽的潛在功能感興趣,以便為我們基于Vue的項目提供可重用性,新功能和更清晰的可讀性。 真正有能力的插槽是什么?
如果你是Vue的新手,或者還沒有看到2.6版的變化,請繼續閱讀。也許學習插槽的最佳資源是Vue自己的文檔,但是我將在這里給出一個綱要。
想優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!
插槽是什么?
插槽是Vue組件的一種機制,它允許你以一種不同于嚴格的父子關系的方式組合組件。插槽為你提供了一個將內容放置到新位置或使組件更通用的出口。從一個簡單的例子開始:
// frame.vue <template><div class="frame"><slot></slot></div> </template> 復制代碼這個組件最外層是一個div。假設div的存在是為了圍繞其內容創建一個樣式框架。這個組件可以通用地用于將框架包圍在wq你想要的任何內容上,來看看它是怎么用的。這里的frame組件指的是我們剛才做的組件。
// app.vue <template><frame><img src="an-image.jpg"></frame> </template> 復制代碼在開始和結束frame標記之間的內容將插入到插槽所在的frame組件中,替換slot標記。這是最基本的方法。還可以簡單地通過填充指定要放入槽中的默認內容
// frame.vue <template><div class="frame"><slot>如果這里沒有指定任何內容,這就是默認內容</slot></div> </template> 復制代碼所以現在如果我們這樣使用它:
// app.vue <template><frame /> </template> 復制代碼“如果這里沒有指定任何內容,這就是默認內容”是默認內容,但是如果像以前那樣使用它,默認文本將被img標記覆蓋。
多個/命名的插槽
可以向組件添加多個插槽,但是如果這樣做了,那么除了其中一個之外,其他所有插槽都需要有名稱。如果有一個沒有名稱的槽,它就是默認槽。下面是如何創建多個插槽:
// titled-frame.vue <template><div class="frame"><header><h2><slot name="header">Title</slot></h2></header><slot>如果這里沒有指定任何內容,這就是默認內容</slot></div> </template> 復制代碼我們保留了相同的默認槽,但這次我們添加了一個名為header的槽,可以在其中輸入標題,用法如下:
// app.vue <template><titled-frame><template v-slot:header><!-- The code below goes into the header slot -->My Image’s Title</template><!-- The code below goes into the default slot --><img src="an-image.jpg"></titled-frame> </template> 復制代碼就像之前一樣,如果我們想將內容添加到默認槽中,只需將其直接放在titled-frame組件中。但是,要將內容添加到命名槽中,我們需要用v-slot指令將代碼包裹在在template標記中。在v-slot之后添加冒號(:),然后寫出要傳遞內容的slot的名稱。
注意,v-slot是Vue 2.6的新版本,所以如果你使用的是舊版本,則需要閱讀關于不推薦的slot語法的文檔。
作用域插槽
還需要知道的另一件事是插槽可以將數據/函數傳遞給他們的孩子。 為了證明這一點,我們需要一個完全不同的帶有插槽的示例組件:創建一個組件,該組件將當前用戶的數據提供給其插槽:
// current-user.vue <template><span><slot v-bind:user="user">{{ user.lastName }}</slot></span> </template><script> export default {data () {return {user: ...}} } </script> 復制代碼該組件有一個名為user的屬性,其中包含關于用戶的詳細信息。默認情況下,組件顯示用戶的姓,但請注意,它使用v-bind將用戶數據綁定到slot。這樣,我們就可以使用這個組件向它的后代提供用戶數據
// app.vue <template><current-user><template v-slot:default="slotProps">{{ slotProps.user.firstName }}</template> </current-user> </template> 復制代碼為了訪問傳遞給slot的數據,我們使用v-slot指令的值指定作用域變量的名稱。
這里有幾點需要注意:
-
我們指定了default的名稱,但是不需要為默認槽指定名稱。相反,我們可以使用v-slot="slotProps"。
-
不需要使用slotProps作為名稱,可以隨便叫它什么。
-
如果只使用默認槽,可以跳過內部template標記,直接將v-slot指令放到當前current-user上。
-
可以使用對象解構來創建對作用域插槽數據的直接引用,而不是使用單個變量名。換句話說,可以使用v-slot="{user}"代替v-slot="slotProps",然后可以直接使用user而不是slotProps.user。
所以,上面的例子可以這樣重寫
// app.vue <template><current-user v-slot="{user}">{{ user.firstName }}</current-user> </template> 復制代碼還有幾點要記住:
-
可以使用v-bind指令綁定多個值。
-
也可以將函數傳遞到作用域槽。許多庫使用它來提供可重用的函數組件。
-
v-slot 的別名是#。因此,可以用#header="data" 來代替 v-slot:header="data"。還可以使用 #header來代替 v-slot:header(前提:不是作用域插槽時)。對于默認插槽,在使用別名時需要指定默認名稱。換句話說,需要這樣寫 #default="data" 而不是#="data"。
可以從文檔中了解更多的細節,但這足以幫助你理解在本文剩下部分中討論的內容。
你能用插槽做什么?
插槽不是為了一個目的而構建的,或者至少如果它們是,它們已經超越了最初的意圖,成為做許多不同事物的強大工具。
可重用的模式
組件總是被設計為可重用的,但是某些模式對于使用單個“普通”組件來實施是不切實際的,因為為了自定義它,需要的props 數量可能過多或者需要通過props傳遞大部分內容或其它組件。
插槽可用包裹外部的HTML標簽或者組件,并允許其他HTML或組件放在具名插槽對應名稱的插槽上。
對于的第一個例子,從簡單的東西開始:一個按鈕。假設咱們的團隊正在使用 Bootstrap。使用Bootstrap,按鈕通常與基本的“btn”類和指定顏色的類綁定在一起,比如“btn-primary”。你還可以添加size類,比如'btn-lg'。
為了簡單起見,現在讓我們假設你的應用使用btn、btn-primary和btn-lg。你不希望總是必須在按鈕上寫下這三個類,或者你不相信新手會記得寫下這三個類。
在這種情況下,可以創建一個自動包含所有這三個類的組件,但是如何允許自定義內容? prop 不實用,因為允許按鈕包含各種HTML,因此我們應該使用一個插槽。
<!-- my-button.vue --> <template><button class="btn btn-primary btn-lg"><slot>Click Me!</slot></button> </template> 復制代碼現在我們可以在任何地方使用它,無論你想要什么內容
<!-- 使用 my-button.vue --> <template><my-button><img src="/img/awesome-icon.jpg"> 我是小智!</my-button> </template> 復制代碼當然,你可以選擇比按鈕更大的東西。 堅持使用Bootstrap,讓我們看一個模態:
<!-- my-modal.vue --> <template> <div class="modal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><slot name="header"></slot><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="modal-body"><slot name="body"></slot></div><div class="modal-footer"><slot name="footer"></slot></div></div></div> </div> </template> 復制代碼現在,使用它:
<!-- 使用 my-modal.vue --> <template><my-modal><template #header><h5>大家最棒!</h5></template><template #body><p>大家加油</p></template><template #footer><em>大家好樣的!</em></template></my-modal> </template> 復制代碼上述類型的插槽用例顯然非常有用,但它可以做得更多。
復用函數
Vue組件并不完全是關于HTML和CSS的。它們是用JavaScript構建的,所以也是關于函數的。插槽對于一次性創建函數并在多個地方使用功能非常有用。讓我們回到模態示例并添加一個關閉模態的函數
<!-- my-modal.vue --> <template> <div class="modal" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><slot name="header"></slot><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div><div class="modal-body"><slot name="body"></slot></div><div class="modal-footer"> <slot name="footer" :closeModal="closeModal"></slot></div></div></div> </div> </template><script> export default {//...methods: {closeModal () {// 關閉對話框時,需要做的事情}} } </script> 復制代碼當使用此組件時,可以向footer添加一個可以關閉模??態的按鈕。 通常,在Bootstrap模式的情況下,可以將data-dismiss =“modal”添加到按鈕來進行關閉。
但我們希望隱藏Bootstrap 特定的東西。 所以我們傳遞給他們一個他們可以調用的函數,這樣使用者就不會知道我們有使用 Bootstrap 的東西。
<!-- 使用 my-modal.vue --> <template><my-modal><template #header><h5>Awesome Interruption!</h5></template><template #body><p>大家加油!</p></template><template #footer="{closeModal}"><button @click="closeModal">點我可以關閉煩人的對話框</button></template></my-modal> </template> 復制代碼無渲染組件
最后,可以利用你所知道的關于使用插槽來傳遞可重用函數的知識,并剝離所有HTML,只使用插槽。這就是無渲染組件的本質:一個只提供函數而不包含任何HTML的組件。
使組件真正無渲染可能有點棘手,因為需要編寫render函數而不是使用模板來消除對根元素的依賴,但它可能并不總是必要的。 來看看一個先使用模板的簡單示例:
<template><transition name="fade" v-bind="$attrs" v-on="$listeners"><slot></slot></transition> </template> <style> .fade-enter-active, .fade-leave-active {transition: opacity 0.3s; } .fade-enter, .fade-leave-to {opacity: 0; } </style> 復制代碼這是一個無渲染組件的奇怪例子,因為它甚至沒有任何JavaScript。這主要是因為我們正在創建一個內置無渲染函數的預配置可重用版本:transition。
是的,Vue有內置的無渲染組件。這個特殊的例子取自Cristi Jora的一篇關于可重用transition的文章,展示了一種創建無渲染組件的簡單方法,該組件可以標準化整個應用程序中使用的 transition。
對于我們的另一個示例,我們將創建一個組件來處理切換 Promise 的不同狀態中顯示的內容: pending、resolved 和 failed。這是一種常見的模式,雖然它不需要很多代碼,但是如果沒有為了可重用性而提取邏輯,它會使很多組件變得混亂。
<!-- promised.vue --> <template><span><slot name="rejected" v-if="error" :error="error"></slot><slot name="resolved" v-else-if="resolved" :data="data"></slot><slot name="pending" v-else></slot></span> </template><script> export default {props: {promise: Promise},data: () => ({resolved: false,data: null,error: null}), watch: {promise: {handler (promise) {this.resolved = falsethis.error = nullif (!promise) {this.data = nullreturn}promise.then(data => {this.data = datathis.resolved = true}).catch(err => {this.error = errthis.resolved = true})},immediate: true}} } </script> 復制代碼這是怎么回事,小老弟?首先,請注意,該組件接收一個Promise 類型參數。在watch部分中,監聽promise的變化,當promise發生變化時,清除狀態,然后調用 then 并 catch promise,當 promise 成功完成或失敗時更新狀態。
然后,在模板中,我們根據狀態顯示一個不同的槽。請注意,我們沒有保持它真正的無渲染,因為我們需要一個根元素來使用模板。我們還將data和error傳遞到相關的插槽范圍。
<template><div><promised :promise="somePromise"><template #resolved="{ data }">Resolved: {{ data }}</template><template #rejected="{ error }">Rejected: {{ error }}</template><template #pending>請求中...</template></promised></div> </template> ... 復制代碼我們將somePromise傳遞給無渲染組件。 然后等待它完成,對于 pending 的插槽,顯示“請求中...”。 如果成功,顯示“Resolved:對應的值”。 如果失敗,顯示“已Rejected:失敗的原因”。 現在我們不再需要跟蹤此組件中的promise的狀態,因為該部分被拉出到它自己的可重用組件中。
那么,我們可以做些什么來繞過promised.vue中的插槽? 要刪除它,我們需要刪除template部分并向我們的組件添加render函數:
render () {if (this.error) {return this.$scopedSlots['rejected']({error: this.error})}if (this.resolved) {return this.$scopedSlots['resolved']({data: this.data})}return this.$scopedSlots['pending']() } 復制代碼這 里沒有什么太復雜的。我們只是使用一些if塊來查找狀態,然后返回正確的作用域slot(通過this.$ scopedslot ['SLOTNAME'](…)),并將相關數據傳遞到slot作用域。當你不使用模板時,可以跳過使用.vue文件擴展名,方法是將JavaScript從script標記中提取出來,然后將其放入.js文件中。在編譯這些Vue文件時,這應該會給你帶來非常小的性能提升。
總結
Vue的插槽將基于組件的開發提升到了一個全新的水平,雖然本文已經展示了許多可以使用插槽的好方法,但還有更多的插槽。歡迎留言討論。
代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
交流
干貨系列文章匯總如下,覺得不錯點個Star,歡迎 加群 互相學習。
github.com/qq449245884…
我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的干貨,在進階的路上,共勉!
關注公眾號,后臺回復福利,即可看到福利,你懂的。
轉載于:https://juejin.im/post/5d23d9ddf265da1bbf6941c9
總結
以上是生活随笔為你收集整理的vue 2.6 中 slot 的新用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java桌球小游戏1
- 下一篇: VueJS教程4