最近項目上有個需求就是作下拉列表的四級聯動,使用的是vuejs + elementui,使用數組存儲對象的形式作爲列表渲染到頁面上的數據,可是在下拉列表聯動的時候發現幾個問題,如今記錄下解決辦法,分享給你們。html
以前在寫React的時候,複雜一點的數據會經過Immutable.js來實現,經過get和set來實現數據的設置和讀取,以及深層拷貝等功能,如今到了Vue發現數據複雜一點就不知道如何處理,第三方關於vue的immutable.js框架也沒有了解過,後面有時間能夠關注並學習下(你們有使用過的能夠分享給我)。前端
這個問題其實Vue官網也說明過關於數組變化不會從新渲染頁面的問題。vue
Vue 不能檢測如下數組的變更:react
當你利用索引直接設置一個數組項時,例如:vm.items[indexOfItem] = newValue
當你修改數組的長度時,例如:vm.items.length = newLength
舉個例子:segmentfault
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的時候頁面可以動態渲染,這樣就完成了聯動效果以及修改對象數組後前端頁面不從新渲染問題了。promise
這個問題是這樣的:咱們保存到後臺數據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賦值,若是你們有更好的方法能夠提出來。ide
這樣就解決了第二個問題。
一、可能有人會問:爲何不把oneList和twoList設置成公共的列表,和arrys數組分開,這樣不是更方便讀取嗎?答案是:不能,由於是四級聯動,因此只能每一個對象裏面保存一份oneList和twoList,設想一下:若是arrys數組裏面有三條數據,我改變了第一條的one,那麼twoList就會變化,而第二條的twoList也就跟着變了,這就是否是單獨的四級聯動了,而是全部twoList都跟着動了!
二、el-select只要單獨賦值key和list,就能顯示對應的name(說明當key賦值上去的時候,el-select的list就去找對應的,找到了就顯示出名稱name)
三、作的過程當中發現有個問題:change的時候發現two和three還有four只顯示key,不顯示name,後來發現是由於使用了ht-select而沒有用elementUI自帶的el-select,換成以後就沒問題了,也算一個小插曲吧。