本文章寫於一年前,修改其中的一些圖片程式碼,轉載至此,略作訂正
寫在前面
首先,本篇文章所開發的元件並非一個已經開源的上線元件,所以如果你急於需要一個外掛來只做你的專案,那麼並不能帶給你及時的幫助。這個元件的開發預計寫兩篇文章,一遍寫元件,一篇寫元件邏輯。這篇文章也是我自己開發的從無到有的過程,所以它可以為你提供一些Tree元件開發的思路,程式碼寫到一定程度,不能完全依賴外掛了,有時間可以看看外掛原始碼或者動手去開發,這樣真的能加深對技術的掌握程度。
開發過程
1.資料倉儲-Vuex
2.元件的迴圈建立-遞迴元件
需求決定了我的技術選型,專案需求是一箇中國各級政府列表的選擇,後臺基於大資料去彙總各級政府網站的文章決策,然後交給前端去展示,左側顯示各級政府的Tree,右側顯示具體文章。因為政府資料的不確定性,所以顯示政府的Tree的每一層級也是動態獲取的,就比如北京市下級的海淀區政府在一小時前可能不存在某某政策文章,那麼北京市節點的子節點中就不會包含海淀區這一子節點,但是10分鐘前網站釋出了一篇文章,那麼就需要在北京市中新增海淀區,為了良好的實時性效果,每一級的子節點都不固定。
因此樹狀結構的data是變化的而不是在初始化的時候就固定好的,資料驅動Dom的思想,data有多少層級,Dom就有多少層級,data不定,所以Dom也無法提前定義好層級,決定採用Vue的遞迴元件,元件遞迴自身實現無限制層級的渲染。需求中每一個節點的Tree都具有增刪改的功能,因為data是單例的,全域性維護著一個data資料來源頭,增刪改也就是操作data第n個子節點的某個子元素,因此採用了Vuex作為一個data存放的倉庫(這不是一個元件庫,只是一個專案的應用)。
專案的關鍵點:
1.動態載入子節點,也就是說頁面載入的時候,根節點的nodes子節點陣列可能是個空陣列,每次單擊節點去獲取子節點陣列。
2.全域性單例的樹節點物件 data 物件,每次得到新的子節點,需要去維護這個物件,因為資料與dom動態繫結,所以每次資料物件內部發生了變化,dom也會重繪。
vue對虛擬dom的對映通過使用diff演算法進行了優化,所以不用擔心,重繪造成頁面閃屏
樹data資料結構如下:
let data = {
id: "01",
lable: "政府機構",
nodes: [
{
id: "02",
lable: "中央機關",
nodes: [{
//..
}]
},
{
id: "03",
lable: "直轄市",
nodes: [{
//..
//北京市...
//天津市..
}]
},
]
};
複製程式碼
顯示成這樣:
每一個節點包含id,label,nodes三個屬性,nodes往下延伸子節點,一共有多少級不確定根據後臺獲取得到。
遞迴元件
一個簡單的遞迴元件的示例如下:
<template>
<div class="tree-menu">
//元件內部不斷用自身,只要子節點存在就遞迴呼叫
<tree-menu v-for='(item, index) of dataNodes' :key='index'></tree-menu>
</div>
</template>
<script>
export default {
name:'TreeMenu',//元件名稱必須寫
data() {
return {
dataNodes: {
//...
}
}
}
}
</script>
複製程式碼
TreeMenu.vue宣告元件TreeMenu,並向外暴露,在元件內部呼叫自身,也就是一個遞迴的思想,繫結的dataNodes有多少層級,那就會遞迴多少層,因為每一層都有v-for都會迴圈子節點。繫結具體資料的時候再具體分析。不斷呼叫自身,當然這只是一個例子,實際情況還要改造一下:
新建TreeMenu.vue,作為顯示節點的邏輯元件:
<template>
<div class="tree-menu">
<div>{{label}}</div>//節點名稱
<tree-menu //如果nodes.length>0就遞迴顯示子節點
v-for="(node, index) of nodes"
:key="index"
:nodes="node.nodes" //子節點的子節點向下傳遞
:label="node.label"
>
</tree-menu>
</div>
</template>
<script>
export default {
name: "TreeMenu",
props:['label','nodes'],//資料通過pros向下傳遞,全域性唯一資料來源
data() {
return {};
}
};
</script>
複製程式碼
建立Tree.vue作為節點樹的入口:
<template>
<div class="tree-alone">
<tree-menu :label="tree.label" :nodes="tree.nodes"></tree-menu>
</div>
</template>
<script>
import TreeMenu from './TreeMenu.vue'
export default {
name: 'tree',
data() {
return {
tree: {
id: "01",
lable: "總層級",
nodes: [
{
id: "02",
label: "層級1",
nodes: [{
label: '層級1-1'
}]
},
{
id: "03",
label: "層級2",
nodes: []
},
]
};
}
}
},
components: {
TreeMenu
}
}
</script>
複製程式碼
在Tree.vue入口元件裡引入子元件TreeMenu.vue,並且向子元件傳遞資料label和nodes,這裡data先預定義一個簡單的物件,後面會動態獲取。
TreeMenu.vue作為樹形選單元件,負責遞迴和資料渲染,它會接受來自入口元件傳遞來的label和nodes資料,進行渲染,並且繼續遞迴傳遞nodes和label。
在從父元件接收nodes和label後,先迴圈渲染n個tree-menu元件,然後每一個tree-menu又會遞迴自身,所以資料就這樣一層層向下傳遞nodes=>nodes.node=>nodes.node.node,此時已經完成了資料與Dom的繫結,可以修改一下Tree.vue中的tree資料來源,看一下元件是否動態改變了。現在渲染如下圖:
資料正確渲染,現在需要加一些樣式和點選事件(展開與收縮),就不做詳細介紹了,程式碼如下: (向下傳遞depth引數來獲取層級索引)
//Tree.vue
<template>
<div class="tree-alone">
<tree-menu :label="tree.label" :nodes="tree.nodes" :depth="0"></tree-menu>
</div>
</template>
複製程式碼
//TreeMenu.vue
<template>
<div class="tree-menu">
<div :style="indent" @click="toggleChildren">{{label}}</div>
<div v-if="showChildren">
<tree-menu
v-for="(item, index) of nodes"
:key="index"
:nodes="node.nodes"
:label="node.label"
:depth="depth + 1"
></tree-menu>
</div>
</div>
</template>
<script>
export default {
name: "TreeMenu",
props: ["label", "nodes"],
data() {
return {
showChildren: false
};
},
methods: {
toggleChildren() {
this.showChildren = !this.showChildren;
}
},
computed: {
indent() {
return { transform: `translate(${this.depth * 20}px)` };
}
}
};
</script>
複製程式碼
修改後會展開收縮與縮排,一些箭頭旋轉等樣式問題就不詳細寫了。
這一篇主要寫遞迴元件的簡單實現,下一篇寫資料獲取方面,其實現在元件已經寫好了,我們現在只需要關注資料層,資料變化Dom會自動響應變化的。 下一篇連結: Vue遞迴元件+Vuex開發樹形元件Tree--資料模組