Vue 實現數組四級聯動

作者:Awbeci

來源:SegmentFault 思否社區

前言

最近項目上有個需求就是做下拉列表的四級聯動,使用的是 vuejs + elementui,使用數組存儲對象的形式做爲列表渲染到頁面上的數據,但是在下拉列表聯動的時候發現幾個問題,現在記錄下解決辦法,分享給大家。

  1. 修改對象數組後前端頁面不重新渲染

  2. 查看或者編輯回顯數據時,聯動數據渲染出錯 (只顯示 key,不顯示 name)

關於複雜數據處理

之前在寫 React 的時候,複雜一點的數據會通過 Immutable.js 來實現,通過 get 和 set 來實現數據的設置和讀取,以及深層拷貝等功能,現在到了 Vue 發現數據複雜一點就不知道如何處理,第三方關於 vue 的 immutable.js 框架也沒有了解過,後面有時間可以關注並學習下 (大家有使用過的可以分享給我)。

四級聯動問題解決方法

這個問題其實 Vue 官網也說明過關於數組變化不會重新渲染頁面的問題。

Vue 不能檢測以下數組的變動:

當你利用索引直接設置一個數組項時,例如:vm.items[indexOfItem] = newValue
當你修改數組的長度時,例如:vm.items.length = newLength
舉個例子:

var vm = new Vue({
   data: {
     items: ['a''b''c']
   }
 })

vm.items[1] = 'x' // 不是響應性的
vm.items.length = 2 // 不是響應性的
爲了解決第一類問題,以下兩種方式都可以實現和 vm.items[indexOfItem] = newValue 相同的效果,同時也將在響應式系統內觸發狀態更新:

// Vue.set
 Vue.set(vm.items, indexOfItem, newValue)
 // Array.prototype.splice
 vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 實例方法,該方法是全局方法 Vue.set 的一個別名:

 vm.$set(vm.items, indexOfItem, newValue)

爲了解決第二類問題,你可以使用 splice:

 vm.items.splice(newLength)

因此解決辦法就是代碼裏使用Vue.set(vm.items, indexOfItem, newValue),下面就演示個例子:

export default {
    data(){
        return {
            arrys :[
                {
                    one: '',
                    oneList: Promise: getOneList(),
                    two: '',
                    twoList: Promise: getTwoList(one),
                    three: '',
                    threeList: Promise: getThreeList(two),
                    four: '',
                    fourList: Promise: getFourList(three),
                }
            ]
        }
    },
    methods: {
        // one下拉列表change事件
        oneChange(key, index){
            this.getTwoList(key).then(res ={
            this.arrys.forEach((item, i) ={
              if (i === index) {
                // 因爲是四級聯動,所以change one之後,two、three和four都要置空
                let newitem = {
                  two: [],
                  twoList: res,
                  three: '',
                  four: '',
                  threeList: [],
                  fourList: []
                }
                // 說明:修改arrys中第i個的數據,只有使用Vue.set頁面纔會重新渲染
                // newitem會覆蓋item中的數據,並生成一個新的引用指針
                Vue.set(this.arrys, i, Object.assign({}, item, newitem))
              }
            })
          });
        },
        // two下拉列表change事件
        twoChange(key, index){
            
        },
        // three下拉列表change事件
        threeChange(key, index){
            
        },
        // four下拉列表change事件
        fourChange(key, index){
            
        },
        // 獲取one 列表
        getOneList(){
            
        },
        // 獲取two 列表
        getTwoList(oneKey){
            
        },
        // 獲取three 列表
        getThreeList(twoKey){
            
        },
        // 獲取four 列表
        getFourList(threeKey){
            
        }
    }
    
}

按照上面的代碼就可以實現四級聯動及 change 的時候頁面能夠動態渲染,這樣就完成了聯動效果以及修改對象數組後前端頁面不重新渲染問題了。

這個問題是這樣的:我們保存到後臺數據 one、two、three 和 four,而 oneList、twoList、threeList 和 fourList 不用保存 (通過另外接口獲取,並每次打開的時候都去調用),之後我們查看和編輯上一次的四級聯動的時候,我們發現下拉列表中 one、two、three 和 four 只顯示 key,不顯示 name,原因就在於 oneList、twoList、threeList 和 fourList 比 one、two、three 和 four 數據賦值時要 “慢”,因爲是異步的關係,所以當 list 回調回來的時候,頁面已經渲染了,所以不成功,因此就出現了問題二:只顯示 Key,不顯示 name 的問題。

那麼如何解決這慢的問題呢?我們可以使用 Promise.all 來解決。

// 假設res是後臺返回的要渲染到頁面上的四級聯動數組數據
let resdata = res;

// 給one、two、three和four賦值
resdata.forEach(item ={
    this.arrys.push({
        one: item.one,
        two: item.two,
        three: item.three,
        four: item.four
    })
})

// 獲取twoList(說明:因爲oneList是首級,所以直接獲取就好,這裏就不展示代碼了)
Promise.all(resdata.map(item => this.getTwoList(item.one)))
    .then(twoListData ={
    
        twoListData.forEach((data, i) ={
            
            this.arrys[i].twoList = data;
    
        })
})
  
// promise獲取threeList列表
Promise.all(resdata.map(item => this.getThreeList(item.two)))
  .then(threeListData ={
  
        threeListData.forEach((data, i) ={
        
            this.arrys[i].threeList = data;
            
        })

  })
  
// promise獲取fourList列表
Promise.all(resdata.map(item => this.getFourList(item.three)))
  .then(fourListData ={
  
        fourListData.forEach((data, i) ={
        
            this.arrys[i].fourList = data;
            
        })

  })

爲什麼要寫三次 Promise.all?因爲 forEach 是異常的,所以不能在 forEach 裏面循環獲取 Promise 來給 arrys 賦值,如果大家有更好的方法可以提出來。

這樣就解決了第二個問題。

總結

1、可能有人會問:爲什麼不把 oneList 和 twoList 設置成公共的列表,和 arrys 數組分開,這樣不是更方便讀取嗎?答案是:不能,因爲是四級聯動數組,所以數組中每個對象應該保存一份自己的 oneList 和 twoList,設想一下:如果 arrys 數組裏面有三條數據,我改變了第一條的 one,那麼 twoList 就會變化,而第二條的 twoList 也就跟着變了,這就是不是單獨的四級聯動了,而是所有 twoList 都跟着動了!

2、el-select 只要單獨賦值 key 和 list,就能顯示對應的 name(說明當 key 賦值上去的時候,el-select 的 list 就去找對應的,找到了就顯示出名稱 name)

3、做的過程中發現有個問題:change 的時候發現 two 和 three 還有 four 只顯示 key,不顯示 name,後來發現是因爲使用了 ht-select 而沒有用 elementUI 自帶的 el-select,換成之後就沒問題了,也算一個小插曲吧。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/7KTARNHfnE3Wmq-TCGqi8w