全域性元件實現遞迴樹,避免迴圈引用

pardon110發表於2019-07-03

概述

目錄分類展示會通常要用到樹形結構。本例結合vue的父子元件,採用遞迴渲染,實現一個基於樹的curd小demo

知識點

  • 父子元件遞迴渲染
  • class 樣式物件寫法,CSS偽元素 ::before 用法
  • vue 指令v-model,內聯模板x-template
  • 事件 dblclick blur, 防止事件傳播修飾符 stop
  • 引用父例項屬性 $parent, 全域性方法 Vue.set Vue.delete,子元件型別校驗props

效果圖

vue 實現樹形資料 curd

節點模型

樹形結構,節點通常由資料域+子節點引用組成。缺少指向子節點引用的節點,稱之為葉子節點。
同理,缺少父節點的節點謂之根節點。二者分別代表遞迴的出口和入口,在邏輯部分透過動態響應實現渲染效果。

  • 下面這段程式碼描述了一個 data 基本treedata模型節點物件。 namestring 型別,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)
      }
    },

原始碼

vue實現目錄的curd

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章