網(wǎng)站首頁(yè) Vue 正文
前言
不知道大家有沒(méi)遇到過(guò)這樣的場(chǎng)景:渲染列表數(shù)據(jù)的時(shí)候,列表的子項(xiàng)還是列表。如果層級(jí)少尚且可以用幾個(gè)for循環(huán)搞定,但是層級(jí)多或者層級(jí)不確定就有點(diǎn)無(wú)從下手了。
其實(shí)這就是樹形結(jié)構(gòu)數(shù)據(jù),像常見的組織架構(gòu)圖,文件夾目錄,導(dǎo)航菜單等都屬于這種結(jié)構(gòu)。很多組件庫(kù)都帶有樹形組件,但往往樣式不是我們想要的,改起來(lái)也非常的費(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)。接下來(lái)我們就看看如何實(shí)現(xiàn)以上效果吧!
渲染完整數(shù)據(jù)
渲染數(shù)據(jù)這一步非常簡(jiǎn)單,首先是把樹形結(jié)構(gòu)封裝成一個(gè)列表組件,其次判斷每一項(xiàng)有沒(méi)有子節(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ù)
接下來(lái)我們要做的是,點(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è)過(guò)程中,把數(shù)據(jù)傳遞過(guò)去就可以了。這塊有點(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é)
原文鏈接:https://juejin.cn/post/7056922161788747789
相關(guān)推薦
- 2022-11-23 Android10?Binder原理概述深入解析_Android
- 2022-10-17 React報(bào)錯(cuò)信息之Expected?an?assignment?or?function?call?
- 2022-10-05 C++淺析數(shù)據(jù)在內(nèi)存中如何存儲(chǔ)_C 語(yǔ)言
- 2022-04-15 ASP.NET?Core基礎(chǔ)之中間件_基礎(chǔ)應(yīng)用
- 2022-06-11 SQL常用日期查詢語(yǔ)句及顯示格式設(shè)置_MsSql
- 2022-08-30 python在文件中倒序查找個(gè)關(guān)鍵詞
- 2022-07-10 Executor 線程池技術(shù)詳解
- 2022-06-29 python循環(huán)神經(jīng)網(wǎng)絡(luò)RNN函數(shù)tf.nn.dynamic_rnn使用_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支