Vue組件化開發(fā)的必備技能之組件遞歸
前言
不知道大家有沒遇到過這樣的場(chǎng)景:渲染列表數(shù)據(jù)的時(shí)候,列表的子項(xiàng)還是列表。如果層級(jí)少尚且可以用幾個(gè)for循環(huán)搞定,但是層級(jí)多或者層級(jí)不確定就有點(diǎn)無從下手了。
其實(shí)這就是樹形結(jié)構(gòu)數(shù)據(jù),像常見的組織架構(gòu)圖,文件夾目錄,導(dǎo)航菜單等都屬于這種結(jié)構(gòu)。很多組件庫(kù)都帶有樹形組件,但往往樣式不是我們想要的,改起來也非常的費(fèi)勁。那么,如何自己渲染這些數(shù)據(jù)呢?答案就是——組件遞歸!
效果展示

以上就是使用組件遞歸,并加入簡(jiǎn)單交互的展示效果。點(diǎn)擊節(jié)點(diǎn)會(huì)在控制臺(tái)輸出節(jié)點(diǎn)對(duì)應(yīng)的數(shù)據(jù),如果有子節(jié)點(diǎn),則會(huì)展開或收起子節(jié)點(diǎn)。接下來我們就看看如何實(shí)現(xiàn)以上效果吧!
渲染完整數(shù)據(jù)
渲染數(shù)據(jù)這一步非常簡(jiǎn)單,首先是把樹形結(jié)構(gòu)封裝成一個(gè)列表組件,其次判斷每一項(xiàng)有沒有子節(jié)點(diǎn),如果有子節(jié)點(diǎn),再使用自身組件去渲染就可以了。
src/components/myTree.vue
<template>
<div class="tree-item">
<div v-for="item in treeData" :key="item.id">
<div class="item-title">{{ item.name }}</div>
<div v-if="item.children && item.children.length" class="item-childen">
<my-tree :treeData="item.children"></my-tree>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'myTree',
props: {
treeData: {
type: Array,
default: () => []
}
}
}
</script>
<style lang="scss" scoped>
.tree-item {
.item-title {
padding: 4px 8px;
}
.item-childen {
padding-left: 20px;
}
}
</style>src/App.vue
<template>
<my-tree :tree-data="treeData"></my-tree>
</template>
<script>
const treeData = [
{ id: 1, name: '一級(jí)1' },
{
id: 2,
name: '一級(jí)2',
children: [
{ id: 3, name: '二級(jí)2-1' },
{ id: 4, name: '二級(jí)2-2' }
]
},
{
id: 5,
name: '一級(jí)3',
children: [
{
id: 6,
name: '二級(jí)3-1',
children: [
{ id: 7, name: '三級(jí)3-1-1' },
{ id: 8, name: '三級(jí)3-1-2' }
]
},
{ id: 9, name: '二級(jí)3-2' },
{ id: 10, name: '二級(jí)3-3' }
]
}
]
import myTree from '@/components/myTree.vue'
export default {
components: {
myTree
},
data() {
return {
treeData: treeData
}
}
}
</script>效果如下

獲取節(jié)點(diǎn)數(shù)據(jù)
接下來我們要做的是,點(diǎn)擊節(jié)點(diǎn)時(shí)在控制臺(tái)輸出對(duì)應(yīng)的數(shù)據(jù)。首先我們使用 $emit,將一級(jí)節(jié)點(diǎn)的 item 傳遞出去,也就是子傳父的方法,相信大家都會(huì)。
其次是將內(nèi)層節(jié)點(diǎn)的數(shù)據(jù)傳遞出去,同樣使用子傳父的方法,只是我們需要給組件里面的 my-tree 綁定@node-click="$emit('node-click', $event)",這樣每次子級(jí)每次都可以調(diào)用父級(jí)的 node-click 方法,父級(jí)又調(diào)用它的父級(jí) node-click 方法,最終調(diào)的都是最外層的 node-click 方法,我們只需要在這個(gè)過程中,把數(shù)據(jù)傳遞過去就可以了。這塊有點(diǎn)繞,相信大家多看幾遍應(yīng)該可以看懂。修改如下:
src/components/myTree.vue
<div class="item-title" @click="itemNodeClick(item)">{{ item.name }}</div>
<div v-if="item.children && item.children.length" class="item-childen">
<my-tree
:treeData="item.children"
@node-click="$emit('node-click', $event)"
></my-tree>
</div>
...
itemNodeClick(item) {
this.$emit("node-click", item)
}src/App.vue
<my-tree :tree-data="treeData" @node-click="nodeClick"></my-tree>
...
nodeClick(val) {
console.log(val)
}效果如下

動(dòng)態(tài)展開收起
這一步的思路是給組件設(shè)置一個(gè)數(shù)組,數(shù)組中存放的是當(dāng)前列表中需要展開的節(jié)點(diǎn)的id,當(dāng)點(diǎn)擊節(jié)點(diǎn)的時(shí)候添加或刪除節(jié)點(diǎn)id,然后判斷每個(gè)節(jié)點(diǎn)的id在不在這個(gè)數(shù)組,在則顯示子節(jié)點(diǎn),不在則隱藏子節(jié)點(diǎn)。
src/components/myTree.vue
<div class="item-title" @click="nodeClick(item)">
<span>{{ item.name }}</span>
<span v-if="item.children && item.children.length">
[{{ isOpen(item.id) ? '-' : '+' }}]
</span>
</div>
<div
v-if="item.children && item.children.length"
v-show="isOpen(item.id)"
class="item-childen"
>
<my-tree
:treeData="item.children"
@node-click="$emit('node-click', $event)"
></my-tree>
</div>
...
data() {
return {
expandedKeys: [] // 當(dāng)前列表需要展開的節(jié)點(diǎn)id組成的數(shù)組
}
},
methods: {
nodeClick(item) {
this.$emit('node-click', item)
if (item.children && item.children.length) {
let index = this.expandedKeys.indexOf(item.id)
if (index > -1) {
// 如果當(dāng)前節(jié)點(diǎn)id存在數(shù)組中,則刪除
this.expandedKeys.splice(index, 1)
} else {
// 如果當(dāng)前節(jié)點(diǎn)id不存在數(shù)組中,則添加
this.expandedKeys.push(item.id)
}
}
},
isOpen(id) {
// 判斷節(jié)點(diǎn)id在不在數(shù)組中,在則顯示,不在則隱藏
return this.expandedKeys.includes(id)
}
}效果如下

最后我們?cè)偬砑右恍邮剑痛蠊Ω娉衫玻?/p>
完整代碼
src/components/myTree.vue
<template>
<div class="tree-item">
<div v-for="item in treeData" :key="item.id">
<div class="item-title" @click="nodeClick(item)">
<span>{{ item.name }}</span>
<span v-if="item.children && item.children.length">
[{{ isOpen(item.id) ? '-' : '+' }}]
</span>
</div>
<div
v-if="item.children && item.children.length"
v-show="isOpen(item.id)"
class="item-childen"
>
<my-tree
:treeData="item.children"
@node-click="$emit('node-click', $event)"
></my-tree>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'myTree',
props: {
treeData: {
type: Array,
default: () => []
}
},
data() {
return {
expandedKeys: [] // 當(dāng)前展開的節(jié)點(diǎn)id組成的數(shù)組
}
},
methods: {
nodeClick(item) {
this.$emit('node-click', item)
if (item.children && item.children.length) {
let index = this.expandedKeys.indexOf(item.id)
if (index > -1) {
// 如果當(dāng)前節(jié)點(diǎn)id存在數(shù)組中,則刪除
this.expandedKeys.splice(index, 1)
} else {
// 如果當(dāng)前節(jié)點(diǎn)id不存在數(shù)組中,則添加
this.expandedKeys.push(item.id)
}
}
},
isOpen(id) {
// 判斷節(jié)點(diǎn)id在不在數(shù)組中,在則顯示,不在則隱藏
return this.expandedKeys.includes(id)
}
}
}
</script>
<style lang="scss" scoped>
.tree-item {
cursor: pointer;
.item-title {
padding: 4px 8px;
&:hover {
background: #eee;
}
}
.item-childen {
padding-left: 20px;
}
}
</style>src/App.vue
<template>
<my-tree :tree-data="treeData" @node-click="nodeClick"></my-tree>
</template>
<script>
const treeData = [
{ id: 1, name: '一級(jí)1' },
{
id: 2,
name: '一級(jí)2',
children: [
{ id: 3, name: '二級(jí)2-1' },
{ id: 4, name: '二級(jí)2-2' }
]
},
{
id: 5,
name: '一級(jí)3',
children: [
{
id: 6,
name: '二級(jí)3-1',
children: [
{ id: 7, name: '三級(jí)3-1-1' },
{ id: 8, name: '三級(jí)3-1-2' }
]
},
{ id: 9, name: '二級(jí)3-2' },
{ id: 10, name: '二級(jí)3-3' }
]
}
]
import myTree from '@/components/myTree.vue'
export default {
components: {
myTree
},
data() {
return {
treeData: treeData
}
},
methods: {
nodeClick(val) {
console.log(val)
}
}
}
</script>效果如下

以上就是今天的分享!有興趣的小伙伴可以動(dòng)手試一哈,把組件進(jìn)一步封裝,或修改成自己想要的樣式。 Vue官方的樹形視圖:cn.vuejs.org/v2/examples…
總結(jié)
到此這篇關(guān)于Vue組件化開發(fā)的必備技能之組件遞歸的文章就介紹到這了,更多相關(guān)Vue組件遞歸內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Vue3和Element Plus實(shí)現(xiàn)自動(dòng)導(dǎo)入功能
在 Vue 3 項(xiàng)目中,結(jié)合 Element Plus 實(shí)現(xiàn)自動(dòng)導(dǎo)入可以顯著減少代碼量,提升開發(fā)效率,Element Plus 提供了官方的自動(dòng)導(dǎo)入插件 unplugin-vue-components 和 unplugin-auto-import,以下是如何配置和使用的詳細(xì)步驟,需要的朋友可以參考下2025-03-03
Vue中通過屬性綁定為元素綁定style行內(nèi)樣式的實(shí)例代碼
這篇文章主要介紹了Vue中通過屬性綁定為元素綁定style行內(nèi)樣式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
vue定時(shí)器設(shè)置和關(guān)閉頁(yè)面時(shí)關(guān)閉定時(shí)器方式
這篇文章主要介紹了vue定時(shí)器設(shè)置和關(guān)閉頁(yè)面時(shí)關(guān)閉定時(shí)器方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
vue+element項(xiàng)目實(shí)時(shí)監(jiān)聽div寬度的變化
這篇文章主要介紹了vue+element項(xiàng)目里實(shí)時(shí)監(jiān)聽某個(gè)div寬度的變化,然后執(zhí)行相應(yīng)的事件,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-08-08
Vue2項(xiàng)目中對(duì)百度地圖的封裝使用詳解
近期項(xiàng)目需求相關(guān)地圖限定使用百度地圖,功能比較簡(jiǎn)單,下面這篇文章主要給大家介紹了關(guān)于Vue2項(xiàng)目中對(duì)百度地圖的封裝使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
前端vue項(xiàng)目如何使用Decimal.js做加減乘除求余運(yùn)算
decimal.js是使用的二進(jìn)制來計(jì)算的,可以更好地實(shí)現(xiàn)格化式數(shù)學(xué)運(yùn)算,對(duì)數(shù)字進(jìn)行高精度處理,使用decimal類型處理數(shù)據(jù)可以保證數(shù)據(jù)計(jì)算更為精確,這篇文章主要給大家介紹了關(guān)于前端vue項(xiàng)目如何使用Decimal.js做加減乘除求余運(yùn)算的相關(guān)資料,需要的朋友可以參考下2024-05-05
Vue彈窗的兩種實(shí)現(xiàn)方式實(shí)例詳解
這篇文章主要介紹了Vue彈窗的兩種實(shí)現(xiàn)方式,一種使用.sync修飾符另一種使用v-model,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08

