最近我弄清楚了如何遞歸地實現嵌套插槽,包括如何使用作用域插槽來實現。起因是我想看看是否可以構建一個復制v-for指令但僅使用template組件。
它還支持插槽和作用域插槽,也可以支持命名插槽,我們可以這樣使用它:
<template> <div> <!-- Regular list --> <v-for :list="list" /> <!-- List with bolded items --> <v-for :list="list"> <template v-slot="{ item }"> <strong>{{ item }}</strong> </template> </v-for> </div> </template>
第一個將正常打印列表,而第二個將每個項包裝在<strong>標記中。
這不是一個非常有用的組件,但可以從中學到的最多,我們來看看。
無循環實現循環
通常,當我們要渲染元素或組件的列表時,可以使用v-for指令,但這次我們希望完全擺脫它。
那么,我們如何在不使用循環的情況下渲染項目列表呢?就是使用 遞歸。
我們可以使用遞歸來渲染項目列表。過程並不會復雜,我們來看看怎么做。
遞歸表示一個列表
我在大學里最喜歡的課程之一是“編程語言概念”。
對我來說,最有趣的部分是探索函數式編程和邏輯編程,並了解與命令式編程的區別(JavaScript 和最流行的語言是命令式編程)。
這門課讓我真正了解如何使用遞歸,因為在純函數語言中,一切都是遞歸。不管怎樣,從那門課我學到了可以使用遞歸地表示一個列表。
與使用數組不同,每個列表是一個值(頭)和另一個列表(尾)。
[head, tail]
例如要表示列表[1、2、3],則可以遞歸方式表示為:
[1, [2, [3, null]]]
我們必須以某種方式結束列表,因此我們使用null而不是另一個數組(也可以使用空數組)。
看到這里,你或許就可以明白了,我們可以使用此概念並將其應用於我們的組件。 相反,我們將遞歸嵌套組件以表示列表。
我們最終將渲染出這樣的內容。 注意我們“list”的嵌套結構:
<div> 1 <div> 2 <div> 3 </div> </div> </div>
誠然,這與v-for渲染的效果並不完全相同,但這也不是本練習的重點。
構建組件
首先,我們將解決遞歸渲染項目列表的問題。
使用遞歸來渲染列表
這次我們使用一個普通數組,而不是使用前面介紹的遞歸列表:
[1, 2, 3]
這里要討論兩種情況:
- 基本情形-渲染列表中的第一項
- 遞歸情形-渲染項目,然后沉浸下一個列表
我們把[1,2,3]傳給v-for
<template> <v-for :list="[1, 2, 3]" /> </template>
我們希望獲取列表中的第一項,即1,並顯示它
<template> <div> {{ list[0] }} </div> </template>
現在,該組件將渲染1,就像我們期望的那樣。
但是我們不能只渲染第一個值並停止。 我們需要渲染值,然后還渲染列表的其余部分:
<template> <div> {{ list[0] }} <v-for :list="list.slice(1)" /> </div> </template>
我們不傳遞整個list數組,而是刪除第一項並傳遞新數組。第一個項目我們已經打印出來了,所以沒有必要保留它。
順序是這樣的:
- 我們將[1,2,3]傳遞到v-for中進行渲染
- 我們的v-for組件渲染1,然后將[2,3]傳遞到下一個v-for進行渲染
- 取[2,3]並渲染2,然后將[3]傳遞到下一個v-for
- 最后一個v-for組件渲染出3,我們已經打印出列表!
現在,我們的vue應用程序的結構如下所示:
<App> <v-for> <v-for> <v-for /> </v-for> </v-for> </App>
可以看到,我們有幾個v-for組件,它們彼此嵌套在一起。最后一件事,我們需要停止遞歸
<template> <div> {{ list[0] }} <v-for v-if="list.length > 1" :list="list.slice(1)" /> </div> </template>
最終,渲染完所有項后,我們需要停止遞歸操作。
遞歸嵌套的插槽
現在,組件可以正常工作,但是我們也希望它與作用域內插槽一起使用,因為這樣可以自定義渲染每個項的方式:
<template> <v-for :list="list"> <template v-slot="{ item }"> <strong>{{ item }}</strong> </template> </v-for> </template>
嵌套插槽
一旦弄清楚了如何遞歸地嵌套插槽,就會對它痴迷一樣的感嘆:
- 嵌套n級的插槽
- 遞歸插槽
- 包裝組件將一個插槽轉換為多個插槽
首先,我們將簡要介紹嵌套插槽的工作方式,然后介紹如何將它們合並到v-for組件中。
假設我們有三個組件:Parent、Child和Grandchild。我們希望傳遞來自Parent組件的一些內容,並在Grandchild組件中渲染它。
從Parent開始,我們傳遞一些內容:
// Parent.vue
<template> <Child> <span>Never gonna give you up</span> </Child> </template>
我們在Child組件中做一些事情,將在稍后介紹。 然后我們的Grandchild組件獲取插槽並渲染內容:
// Grandchild.vue
<template> <div> <slot /> </div> </template>
那么,這個Child組件是什么樣的?
我們需要它從Parent組件獲取內容並將其提供給Grandchild組件,因此我們將兩個不同的插槽連接在一起。
// Child.vue
<template> <Grandchild> <slot /> </Grandchild> </template>
請記住,<slot />元素渲染出作為插槽傳遞到組件的內容。 因此,我們將從“Parent”中獲取該內容,然后將其渲染到“Grandchild”插槽中。
添加作用域插槽
與嵌套作用域插槽唯一不同的是,我們還必須傳遞作用域數據。將其添加到v-for中,我們現在得到以下信息:
<template> <div> <slot v-bind:item="list[0]"> <!-- Default --> {{ list[0] }} </slot> <v-for v-if="list.length > 1" :list="list.slice(1)" > <!-- Recursively pass down scoped slot --> <template v-slot="{ item }"> <slot v-bind:item="item" /> </template> </v-for> </div> </template>
首先讓我們看一下基本情況。
如果沒有提供插槽,則默認<slot>元素內部的內容,並像以前一樣渲染list[0]。 但是如果我們提供了一個slot,它會將其渲染出來,並通過slot作用域將列表項傳遞給父組件。
這里的遞歸情況類似。 如果我們將插槽傳遞給v-for,它將在下一個v-for的插槽中進行渲染,因此我們得到了嵌套。 它還從作用域槽中獲取item並將其傳遞回鏈。
現在,我們這個組件僅使用template就能實現 v-for效果。
豌豆資源搜索網站https://55wd.com 廣州vi設計公司http://www.maiqicn.com
總結
我們做了很多事情,終於了解了如何創建一個僅使用 template 就能實現v-for的效果。
本文主要內容:
- 遞歸地表示列表
- 遞歸組件
- 嵌套槽和嵌套作用域槽