EP32 - ex4. Tree View

更新 發佈閱讀 18 分鐘
可以想像Tree View會有很多階層~
相當適合來搭建組件父子關係~
但還是有點無法想像怎麼實作!一起快來看看怎麼回事~

註解說明這個組件是一個嵌套樹狀結構,並且它能夠遞歸渲染自己。用戶可以雙擊一個項目將其轉換為文件夾。

App.vue

<!--
A nested tree component that recursively renders itself.
You can double click on an item to turn it into a folder.
-->

<script setup>
import { ref } from 'vue'
import TreeItem from './TreeItem.vue'

const treeData = ref({
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'world' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [{ name: 'hello' }, { name: 'world' }]
},
{ name: 'hello' },
{ name: 'world' },
{
name: 'child folder',
children: [{ name: 'hello' }, { name: 'world' }]
}
]
}
]
})
</script>

<template>
<ul>
<TreeItem class="item" :model="treeData"></TreeItem>
</ul>
</template>

<style>
.item {
cursor: pointer;
line-height: 1.5;
}
.bold {
font-weight: bold;
}
</style>

<script setup> 部分

import { ref } from 'vue' 
import TreeItem from './TreeItem.vue'
  • import { ref } from 'vue':從 Vue 中導入 ref 函數,這是一個響應式引用,用來創建可變的數據。
  • import TreeItem from './TreeItem.vue':導入名為 TreeItem 的子組件,這個組件負責渲染樹中的每個項目。
const treeData = ref({
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'world' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [{ name: 'hello' }, { name: 'world' }]
},
{ name: 'hello' },
{ name: 'world' },
{
name: 'child folder',
children: [{ name: 'hello' }, { name: 'world' }]
}
]
}
]
})

const treeData = ref({...}):定義了一個響應式的 treeData 變數,這個變數包含了一個樹狀結構的數據。

  • name: 'My Tree':樹的根節點名稱為 "My Tree"。
  • children: [...]:根節點的子項目是一個陣列,包含多個項目,每個項目都可以是一個葉子或一個具有子項的文件夾。
    • 項目包括名稱為 "hello" 和 "world" 的葉子項目,以及一個名為 "child folder" 的文件夾,該文件夾中又包含其他項目。

<template> 部分

  <ul>
<TreeItem class="item" :model="treeData"></TreeItem>
</ul>
  • <ul>:使用無序列表標籤來呈現樹狀結構。
  • <TreeItem class="item" :model="treeData"></TreeItem>:渲染 TreeItem 子組件,並將 treeData 作為屬性 model 傳遞給它。這樣 TreeItem 就能夠使用這些數據來顯示樹狀結構。

<style> 部分

<style>
.item {
cursor: pointer;
line-height: 1.5;
}
.bold {
font-weight: bold;
}
</style>
  • .item:為 TreeItem 的 CSS 類,設置游標為指針型,讓使用者知道這些項目是可點擊的。同時設置行高為 1.5,增強可讀性。
  • .bold:設置字體加粗的 CSS 類,用於突出顯示特定的項目。

TreeItem.vue

<script setup>
import { ref, computed } from 'vue'

const props = defineProps({
model: Object
})

const isOpen = ref(false)
const isFolder = computed(() => {
return props.model.children && props.model.children.length
})

function toggle() {
isOpen.value = !isOpen.value
}

function changeType() {
if (!isFolder.value) {
props.model.children = []
addChild()
isOpen.value = true
}
}

function addChild() {
props.model.children.push({ name: 'new stuff' })
}
</script>

<template>
<li>
<div
:class="{ bold: isFolder }"
@click="toggle"
@dblclick="changeType">
{{ model.name }}
<span v-if="isFolder">[{{ isOpen ? '-' : '+' }}]</span>
</div>
<ul v-show="isOpen" v-if="isFolder">
<!--
A component can recursively render itself using its
"name" option (inferred from filename if using SFC)
-->
<TreeItem
class="item"
v-for="model in model.children"
:model="model">
</TreeItem>
<li class="add" @click="addChild">+</li>
</ul>
</li>
</template>

這段 Vue.js 代碼定義了一個名為 TreeItem 的組件,該組件用於渲染樹狀結構中的每一個項目,並實現文件夾的打開與關閉功能。以下是逐行解釋:

<script setup> 部分

import { ref, computed } from 'vue'
  • import { ref, computed } from 'vue':從 Vue 中導入 refcomputed 函數。
    • ref 用於創建響應式變量。
    • computed 用於創建響應式計算屬性。
const props = defineProps({
model: Object
})
  • const props = defineProps({...}):定義組件的屬性,這裡的 model 是一個物件(Object),用於傳遞樹狀結構的數據。
const isOpen = ref(false)
  • const isOpen = ref(false):定義一個響應式變量 isOpen,初始值為 false,表示文件夾是否展開。
const isFolder = computed(() => {
return props.model.children && props.model.children.length
})
  • const isFolder = computed(() => {...}):定義一個計算屬性 isFolder,用於判斷當前項目是否為文件夾。如果 model 物件有 children 並且其長度大於零,則返回 true,否則返回 false
function toggle() {
isOpen.value = !isOpen.value
}
  • function toggle() {...}:定義一個函數 toggle,用來切換 isOpen 的值。當用戶點擊項目時,這個函數會被調用,從而展開或收起文件夾。
function changeType() {
if (!isFolder.value) {
props.model.children = []
addChild()
isOpen.value = true
}
}
  • function changeType() {...}:定義一個函數 changeType,用於改變項目的類型。如果當前項目不是文件夾(!isFolder.value),則:
    • modelchildren 設置為空陣列,準備為其添加子項。
    • 調用 addChild() 函數添加一個新的子項。
    • isOpen 設置為 true,展開該項目。
function addChild() {
props.model.children.push({ name: 'new stuff' })
}
  • function addChild() {...}:定義一個函數 addChild,該函數向 modelchildren 陣列中推入一個新的物件,這個物件的 name 屬性設置為 'new stuff',用於表示新添加的項目。

<template> 部分

<template>
<li> <!-- 列表項,代表樹狀結構中的一個項目 -->
<div
:class="{ bold: isFolder }" <!-- 根據 isFolder 計算 class,若為文件夾則加上 bold -->
@click="toggle" <!-- 單擊事件,呼叫 toggle 函數 -->
@dblclick="changeType"> <!-- 雙擊事件,呼叫 changeType 函數 -->
{{ model.name }} <!-- 顯示當前項目的名稱 -->
<span v-if="isFolder"> <!-- 若是文件夾,顯示以下內容 -->
[{{ isOpen ? '-' : '+' }}] <!-- 根據 isOpen 狀態顯示 '-''+' -->
</span> <!-- 結束 span 標籤 -->
</div>
<ul v-show="isOpen" v-if="isFolder"> <!-- 若 isOpen 為 true 且是文件夾,顯示子項目 -->
<TreeItem
class="item" <!-- 給子項目加上 class -->
v-for="model in model.children" <!-- 遍歷子項目 -->
:model="model"> <!-- 傳遞每個子項目作為 model -->
</TreeItem> <!-- 結束 TreeItem 標籤 -->
<li class="add" @click="addChild">+</li> <!-- 顯示加號,點擊時呼叫 addChild 函數 -->
</ul> <!-- 結束 ul 標籤 -->
</li> <!-- 結束 li 標籤 -->
</template>

v-show:

  • 功能: 根據條件顯示或隱藏元素。
  • 使用情境: 在此範例中,v-show="isOpen" 用於控制 <ul> 標籤的顯示狀態。當 isOpentrue 時,<ul> 會顯示;當 isOpenfalse 時,會隱藏,但仍然保留在 DOM 中。

v-if:

  • 功能: 根據條件渲染或不渲染元素。
  • 使用情境: 在此範例中,v-if="isFolder" 用於判斷當前項目是否為文件夾。只有在 isFoldertrue 時,<ul> 標籤才會被渲染到 DOM 中。如果 isFolderfalse,則不會渲染該 <ul> 標籤。

v-if(在 span 標籤中):

    • 功能: 根據條件渲染或不渲染元素。
    • 使用情境: v-if="isFolder" 判斷當前項目是否為文件夾,只有在 isFoldertrue 時,該 <span> 標籤才會被渲染,顯示 +- 符號。

v-for:

  • 功能: 用於遍歷數組或對象,並為每個項目生成元素。
  • 使用情境: 在此範例中,v-for="model in model.children" 遍歷 model.children 陣列中的每個子項目,並為每個子項目生成一個 <TreeItem> 元素。

:class:

  • 功能: 動態綁定元素的 CSS class。
  • 使用情境: 在此範例中,:class="{ bold: isFolder }" 根據 isFolder 的值動態添加或移除 bold class。如果 isFoldertrue,則元素會加上 bold class。

@click@dblclick:

  • 功能: 綁定單擊和雙擊事件。
  • 使用情境:
    • @click="toggle":當用戶單擊 div 時,會呼叫 toggle 函數,切換 isOpen 的值。
    • @dblclick="changeType":當用戶雙擊 div 時,會呼叫 changeType 函數,根據當前項目的類型來進行相應的處理。
<TreeItem> 標籤在這個範例中確實是指遞回(recursive)渲染。這意味著 TreeItem 組件會在其自身的模板中調用自己,以便顯示樹形結構的每一層。這是一種常見的模式,用於處理樹形結構或類似的層級數據結構。

如何實現遞回:

在這個範例中,<TreeItem> 的使用方式如下:

<TreeItem
class="item"
v-for="model in model.children"
:model="model">
</TreeItem>
  • v-for="model in model.children":這一行表示對當前 TreeItemmodel.children 陣列進行遍歷,為每個子項目創建一個新的 TreeItem 實例。
  • :model="model":這一行將當前子項目(model)作為 model 屬性傳遞給新創建的 TreeItem 實例。

這種設計使得每個 TreeItem 都能夠渲染自己的子項目,從而形成一個多層次的樹形結構。比如,如果 model.children 中有子項目,這些子項目會被渲染為新的 TreeItem,每個子項目都可以再次包含自己的子項目,以此類推,形成樹形結構。

這樣的遞回渲染非常適合顯示如文件夾、目錄或任何層級結構的數據,並且在 Vue.js 中實現相對簡單,通過組件的重用來達到目的。








留言
avatar-img
卡關的人生
4會員
73內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
CSS 盒模型是理解和設計網頁佈局的核心概念。它包括元素的內容、填充、邊框和外邊距。
Thumbnail
CSS 盒模型是理解和設計網頁佈局的核心概念。它包括元素的內容、填充、邊框和外邊距。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
《轉轉生》(Re:INCARNATION)為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,結合拉各斯街頭節奏、Afrobeat/Afrobeats、以及約魯巴宇宙觀的非線性時間,建構出關於輪迴的「誕生—死亡—重生」儀式結構。本文將從約魯巴哲學概念出發,解析其去殖民的身體政治。
Thumbnail
《轉轉生》(Re:INCARNATION)為奈及利亞編舞家庫德斯.奧尼奎庫與 Q 舞團創作的當代舞蹈作品,結合拉各斯街頭節奏、Afrobeat/Afrobeats、以及約魯巴宇宙觀的非線性時間,建構出關於輪迴的「誕生—死亡—重生」儀式結構。本文將從約魯巴哲學概念出發,解析其去殖民的身體政治。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News