概述
目錄分類展示會通常要用到樹形結構。本例結合vue的父子元件,採用遞迴渲染,實現一個基於樹的curd小demo
知識點
- 父子元件遞迴渲染
class
樣式物件寫法,CSS偽元素::before
用法vue
指令v-model
,內聯模板x-template
- 事件
dblclick blur
, 防止事件傳播修飾符stop
- 引用父例項屬性
$parent
, 全域性方法Vue.set Vue.delete
,子元件型別校驗props
效果圖
節點模型
樹形結構,節點通常由資料域+子節點引用組成。缺少指向子節點引用的節點,稱之為葉子節點。
同理,缺少父節點的節點謂之根節點。二者分別代表遞迴的出口和入口,在邏輯部分通過動態響應實現渲染效果。
- 下面這段程式碼描述了一個
data
基本treedata
模型節點物件。name
為string
型別,children
是陣列型別。
var data = {
name: 'home',
children: [
{ name: 'hello.js' },
]
}
模板
- 以
x-template
形式描述當單個節點,然後遞迴複用該元件。 - 用檔案與資料夾關係類比,檔案則顯示檔名,有資料夾則迭代。
- 所呈現的無非檔案或資料夾,遇資料夾則遞迴使用元件本身。
用v-model實現雙向繫結,雙擊檔名則達到檔案變身資料夾,此外有相應的清除,刪除,修改vue事件繫結
<script type="text/x-template" id="item-template">
<li>
<div :class="{foldero:isFolder && open ,folderc: !(isFolder && open),file:!isFolder}"
@click="toggle" @dblclick="changeType">
<span v-show="show">{{ name }}</span>
<input type="text" v-show="!show" v-model="name"
@blur="doneEdit(name)"
@keyup.enter="doneEdit(name)"
@keyup.esc="cancelEdit()"
@click.stop @dblclick.stop >
<span @click.stop='editToggle'>[e]</span>
<span @click.stop='delChild' v-show="isFolder">[c]</span>
<span @click.stop='delFile' v-show="!isFolder">[x]</span>
<span v-if="isFolder">[{{ open ? '-':'+'}}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item class="item"
v-for="(treeData,index) in this.model.children" :
model="treeData" :key="index"/>
<li><span class="add" @click.stop="addChild">[append] </span></li>
</ul>
</li>
</script>
元件
- 註冊全域性元件
item
- 需要注意在直接在
DOM
(即非字串的模板如x-template
) 中使用時只有item
有效 - 約定校驗資料型別是
Object
,屬性名model
視為元件接收父元件的模型資料介面
- 需要注意在直接在
Vue.component('item', {
template: '#item-template',
// 型別校驗,接收父元件傳遞過來的資料(只讀)
props: {
model: Object,
},
computed: {
isFolder() {
return this.model.children && this.model.children.length
}
},
...
方法
- 動態增/刪新節點
Vue.delete
清除資料夾下子節點Vue.set
向響應式物件中新增一個新屬性,需確保這個新屬性同樣是響應式的,以觸發檢視更新
methods: {
changeType() {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild() {
this.model.children.push({name: 'new file'})
},
delChild() {
if (this.isFolder) {
Vue.delete(this.model, 'children')
}
}
...
- 刪除檔案
- 通過
$prarent
引用父例項節點刪除當前節點 - 用引用型別陣列方法,
indexOf
定位當前模型在父節點中的索引,splice
改變資料來源 - 需要排除沒有父節點(即當前節點是根節點情況),以及判斷父節點是一個完整節點(是否存在有效子節點)
- 通過
delFile() {
if (this.$parent.model === undefined) {
return
}
var parent = this.$parent.model.children
if (parent && Array.isArray(parent) && parent.length) {
parent.splice(parent.indexOf(this.model), 1)
}
},