Vue的遞歸組件:渲染嵌套評論
大多數現代社交網絡都包括一個功能,用戶可以通過對該特定評論的評論來回復評論。如果我們將其可視化,我們的評論的數據會像下面的結構:
- Comment A
- comment a1
- comment a12
- comment a2
- Comment B
- Comment C
Comment A? 有子評論 comment a1? 和 comment a2?。反過來,comment a1? 有子評論comment a12 ,它也可以有自己的子評論。
有了這種結構,我們可以讓一個注釋有無數層的子注釋。你可能已經熟悉了這種結構化數據的方法,也就是所謂的樹狀結構。不理解的可以想想電腦上的目錄,一個文件夾可以有子文件夾等等。
這節課,我們來看看在Vue中如何使用遞歸組件來管理樹狀結構的數據。在介紹 Vue 中遞歸組件之前,我們先回顧一下什么是遞歸。
什么是遞歸
遞歸簡單的說就是自己調用自己,考慮下面這個函數:
function sum_numbers(arr, n) {
return sum_numbers(arr, n - 1) + arr[n - 1];
}
雖然有些缺陷的,但上面的函數可以被認為是遞歸函數,因為它在函數中調用了自己。然而,這個定義并不包括所有的內容。遞歸是一種解決問題的方法。它基于這樣一個前提:給定一個問題,如果我們知道其子問題的解決方案,我們就可以找到其解決方案。
例如,上面的 sum_numbers? 函數可以找到一個給定數組 arr = [1, 2, 3, 4, 5]? 中所有數字的總和。在求和問題中,如果我們知道5?之前的所有數字之和,那么我們可以將問題簡化為arr中的數字之和等于最后一個元素和最后一個元素之前所有數字之和。
在上面定義的sum_numbers?函數中,表達式 return sum_numbers(arr, n - 1) + arr[n - 1]; 所做的正是我們剛才描述的。
為了 描繪 sum_numbers 函數在輸入 [1, 2, 3, 4] 的情況下如何從頭到尾執行,請看下面的代碼:
**sum_numbers([1, 2, 3, 4], 4)
|
calls
|**
**sum_numbers([1, 2, 3], 3) + 4
|
calls
|
sum_numbers([1, 2], 2) + 3
|
calls
|
sum_numbers([1], 1) + 2
|
calls
|
sum_numbers([], 0) + 1 --** 這里有一個問題
這里有一個問:;我們的遞歸函數試圖將一個空列表添加到一個數字中。事實上,更大的問題是,我們的遞歸函數會一直無限地調用自己。
為了確保我們的遞歸函數不會無限地調用自己,我們需要一個基本情況。你可以把基數看作是我們希望我們的函數停止自我調用的點。
在上面例子中,如果sum_numbers函數中只有一個數字,它就應該停止調用自己。如果數組中只剩下一個數字,那么就沒有什么可以與之相加的了,在這種情況下,我們只需返回這個數字。
function sum_numbers(arr, n) {
if(n <= 1){ //Base Case
return arr[0];
} else {
return sum_numbers(arr, n - 1) + arr[n - 1];
}
}
從根本上說,這就是遞歸的意義,但與Vue的遞歸組件有什么聯系?
Vue 遞歸組件
Vue中的組件是可重用的Vue實例。大多數時候,當我們在Vue中創建一個組件時,只是為了能在其他地方重用它。例如,一個電子商務網站,我們可以在多個頁面上顯示產品。也可以有一個Product Component? ,可以在不同的頁面上呈現,而不是在每個需要的頁面上重復 Product Component 的代碼。
如果一個Vue組件在自己的模板中引用自己,那么它就被認為是遞歸的。遞歸組件與普通組件不同。除了在其他地方被重用之外,遞歸組件還在其模板中引用自己。
為什么一個組件會引用自己?當你在其他組件中渲染一個組件時,客體組件是子體,而渲染它的組件是父體。
在 Product Component? 的例子中,該組件可以將 ProductReview 作為其子組件。在這種情況下,我們對這些組件所代表的實體有兩個不同的組件是有意義的,因為產品和評論在各方面都是不同的。
但是,如果我們以 Comment? 和 Sub-comment? 為例,那么就不一樣了。這兩個組成部分代表的是同一件事。一個子評論也是一個評論。因此,我們為 Comment? 和 Sub-comment 設置兩個不同的組件是沒有意義的,因為它們在結構上是一樣的。我們可以只有一個引用自己的Comment 組件。還是太抽象了?看下面的片段:
<template>
<li class="comment">
<span>{{ comment.comment }}</span>
<comment v-for="reply in comment.replies" :comment="reply"></comment>
</li>
</template>
<script>
export default {
name: "comment",
props: {
comment: Object
}
};
</script>
雖然,但上面的組件可以被認為是遞歸的,因為它引用了自己。和遞歸函數一樣,遞歸組件也必須有一個終止條件,而上面的代碼中缺少這個終止條件。這里需要注意的另一件重要的事情是,為了使一個組件能夠引用自己,必須定義 name 選項。
現在明白了什么是Vue中的遞歸組件,接著,來看看如何使用它來構建一個嵌套的評論界面。
構建評論界面
設置Vue開發環境
首先,初始化一個新的Vue項目,在終端運行 vue create nested-comments命令:
vue create nested-comments
根據提示安裝后,會得到如下的目錄結構:
使用 vue serve 把項目跑起來。
用遞歸組件來渲染嵌套的評論
為了將 嵌套評論渲染到DOM,首先,刪除src/views和src/components?中的所有文件。然后,創建 src/components/Comment.vue,script 內容如下:
<script>
export default {
name: "recursive-comment",
props: {
comment: {
type: String,
required: true,
},
replies: {
type: Array,
default: () => [],
},
},
};
</script>
在上面的代碼片斷中,將的組件命名為遞歸組件(recursive-component?)。記住,在Vue中,一個遞歸組件必須有一個聲明的 name? 。此外,我們的組件希望在它被引用的任何地方都能將comment? 和 replies? 的 props 傳遞給它。
接著,template 內容如下:
<template>
<li>
<span class="comment">{{ comment }}</span>
<ul class="replies" v-if="replies.length">
<div v-for="(item, index) in replies" :key="index">
<recursive-comment
v-bind="{
comment: item.comment,
replies: item.replies,
}"
/>
</div>
</ul>
</li>
</template>
recursive-comment? 組件在自己的模板中引用自己。v-if="replies.length" 是終于遞歸的條件,一旦條件不成立,則停止遞歸。
接下來,在 App.vue 引用一下就行啦:
<template>
<ul v-for="(item, index) in comments" :key="index" class="comments">
<Comment
v-bind="{
comment: item.comment,
replies: item.replies,
}"
/>
</ul>
</template>
<script>
import Comment from "@/components/Comment";
export default {
data: () => ({
comments: [
{
comment: "First comment",
replies: [
{
comment: "sub-comment 1 for comment 1",
replies: [
{
comment: "sub-sub-comment 1",
replies: [
{
comment: "sub-sub-sub-comment 1",
},
{ comment: "sub-sub-sub-comment 2" },
],
},
{ comment: "sub-sub-comment 2" },
],
},
{ comment: "sub-comment 2 for comment 1" },
],
},
{
comment: "Second comment",
replies: [
{
comment: "sub-comment 1 for comment 2",
replies: [
{ comment: "sub-sub-comment 1" },
{ comment: "sub-sub-comment 2" },
],
},
{ comment: "sub-comment 2 for comment 2" },
],
},
],
}),
components: {
Comment,
},
};
</script>
<style>
.comments ul {
padding-left: 16px;
margin: 6px 0;
}
</style>
運行,效果如下所示:
總結
雖然我們舉的例子不是一個典型的評論組件,但我們的目標是探索如何利用Vue中遞歸組件的力量來渲染嵌套數據。
我們看到,我們可以通過創建一個在自己的模板中引用自己的組件來做到這一點。這種遞歸方法在渲染那些看似不同但結構相同的數據實體時特別有用。例如,以我們的 comments? 和 replies 為例。
乍一看,我們好像需要兩個組件,一個用于comments? ,另一個用于 replies。但是,用遞歸的方法,我們能夠用一個組件來渲染這兩種內容。最重要的是,我們的組件會渲染所有的評論和回復,直到它達到終止條件。