怎樣使用Vue的muse-ui庫寫一個動態(tài)n級嵌套list樹。

咋一看標(biāo)題這似乎沒啥難度啊,不就一個組件嵌套的事嗎?事實上如果真這么簡單嗎?請看下文!

題目

data 數(shù)據(jù)如下,要求我們使用該數(shù)據(jù),用mu-list渲染成為一個樹。

//  文件data.js
export default  [
  {
    title: '文件',
    icon: 'file',
    children: [
      {
        title: '打開文件',
        icon: 'open',
        command: 'openFile',
        children: [
           {
              title: '打開最近使用的文件'
           }
        ]
      },
      {
        title: '打開項目',
        icon: 'open',
        command: 'doIt'
      }
    ]
  },
  {
    title: '編輯',
    children: [
      {
        title: '剪切'
      },
      {
        title: '復(fù)制'
      },
      {
        title: '粘貼'
      }
    ]
  }
]

方案一:遞歸嵌套組件

先別急,我們先按第一時間想到的方法——通過組件嵌套來試試,實現(xiàn)代碼如下:

子組件Item.vue

<template>
  <mu-list-item :title="data.title">
    <mu-icon slot="left" :value="data.icon"/>
    <item v-for="childItem in data.children" :data="childItem" slot="nested" />
  </mu-list-item>
</template>

<script>
export default {
  name: 'item',
  props: [
     'data'
  ]
}
</script>

父組件Container.vue

<template>
  <mu-list>
    <item v-for="item in data" :data="item"/>
  </mu-list>
</template>

<script>
import Item from './Item.vue'
import data from './data.js'
export default {
  data() {
     return {
        data
     }
  },
  components: {
    item
  },
  props: [
     data
  ]
}
</script>

好了,運(yùn)行一下,能渲染出來,咋一看似乎沒什么毛病,只是看起來樣子有點(diǎn)怪怪的。
點(diǎn)擊一個項試試下,結(jié)果報錯了?。?!

ERROR: this.$parent.handlerClick is not a function

怎么回事???!
在devtools一看,組件結(jié)構(gòu):mu-list>item>mu-item>item>mu-item
在mu-list和父級item之間,以及父級item和子級item之間都夾了一層 item 組件,子級中的this.$parent自然就指向了 item 組件,而item組件自然是沒有handlerClick的實現(xiàn)。

這下真相大白了,是因為父級和子級組件不兼容導(dǎo)致出錯,怪怪的樣子也是這個原因引起的。這可難辦了,難道必須要放棄掉muse漂亮的組件效果,自己用dom寫一個嵌套組件?!先別灰心,我們來看看方案二

在這里我要提一下,可重用Vue的組件之間不建議偶合度過高,如果必須嵌套的,建議內(nèi)置實現(xiàn)嵌套(參考element-ui 的 el-tree組件),以減少組件使用者的編碼量。

方案二:render函數(shù)渲染

我們還有一條路,那就是render函數(shù)(render函數(shù)參考),Vue有了非常方面的模板之后,仍然為我們保留了類似React的渲染方式,并且其還支持jsx哦!重點(diǎn)是我們可以通過render函數(shù)將兩個組件合并為一個組件,這樣就不會產(chǎn)生多余的中間組件了,我們可以通過render函數(shù)來完成渲染,代碼如下:
合并后的唯一組件Tree.vue

// Tree.vue
<script>
import data from './data.js'
export default {
  data() {
     return {
        data
     }
  },
  render(createElement) {
    const items = this.data.map(item => this.createItem(createElement, item))
    return createElement('mu-list', items)
  },
  methods: {
    // 創(chuàng)建子組件
    createItem(createElement, item, slot = 'default') {
      //創(chuàng)建子組件
      const children = item.children ? 
        item.children.map(childItem => this.createItem(createElement, childItem, 'nested')) : []
      // 插入圖標(biāo)組件
      children.push(createElement('mu-icon', {
        slot: 'left', 
        props: {
            value: item.icon
        }
      ))
      return createElement('mu-item', {
        slot: 'nested',
        props: {
          title: childItem.title
        },
        on: {
              // 如果有事件綁定可以寫在這里
        }
      }, children)
    }
  }
}
</script>

運(yùn)行一下,正常,點(diǎn)擊項,完全正常!一切OK!

方案三:jsx語法的render函數(shù)

也許你會覺得直接用JS來生成元素的方式,不太直觀,用起來也不方便,沒關(guān)系,我們可以使用React的jsx語法來編寫render函數(shù)(jsx參考),按照jsx使用文檔上的說明,只需要簡單幾步就可以將上面的例子改造成簡潔易與讀寫的代碼。



// Tree.vue
<script>
import data from './data.js'
export default {
  data() {
     return {
        data
     }
  },
  render(createElement) {
    return <mu-list>
      this.data.map(item => this.createItem(item, 'default'))
    </mu-list>
  },
  methods: {
    // 創(chuàng)建項
    createItem(item, slot = 'default') {
      return <mu-item
        slot={slot},
        title={childItem.title}>
        <mu-icon slot="left" value={item.icon} />
        item.children && item.children.map(childItem => this.createItem(childItem, 'nested')) : []
      </mu-item>
    }
  }
}
</script>

這樣一看是不是簡單易讀多了呢?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容