最近在項(xiàng)目中遇到了一個(gè)問(wèn)題,不知道為什么,所以最后通過(guò)動(dòng)手做demo實(shí)踐、查文檔的方式解決了,這里做一個(gè)總結(jié)。
東阿網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,東阿網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為東阿1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的東阿做網(wǎng)站的公司定做!
例1
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script> <style> li:hover { cursor: pointer; } </style> </head> <body> <div class="wrap"> <ul> <li v-for="item,index in items" v-on:click="handle(index)"> <span>{{item.name}}</span> <span>{{numbers[index]}}</span> </li> </ul> </div> <script> var vm = new Vue({ el: ".wrap", data: { numbers: [], items: [ {name: 'jjj'}, {name: 'kkk'}, {name: 'lll'}, ] }, methods: { handle: function (index) { // WHY: 更新數(shù)據(jù),view層未渲染,但通過(guò)console這個(gè)數(shù)組可以發(fā)現(xiàn)數(shù)據(jù)確實(shí)更新了 if (typeof(this.numbers[index]) === "undefined" ) { this.numbers[index] = 1; } else { this.numbers[index]++; } } } }); </script> </body> </html>
這里的實(shí)現(xiàn)目的很明確 --- 我希望在點(diǎn)擊li時(shí)先檢測(cè)是否存在,當(dāng)然是不存在的,所以就將值設(shè)置為1, 如果再次點(diǎn)擊,就讓數(shù)字累加。
但是出現(xiàn)的問(wèn)題是: 點(diǎn)擊之后數(shù)字并沒(méi)有在view層更新,而通過(guò)console打印發(fā)現(xiàn)數(shù)據(jù)確實(shí)更新了,只是view層沒(méi)有及時(shí)的檢測(cè)到, 而我一直以來(lái)的想法就是: 既然vue實(shí)現(xiàn)的時(shí)數(shù)據(jù)雙向綁定,那么在model層發(fā)生了變化之后為什么就沒(méi)有在view層更新呢?
首先,我就考慮了這是不是數(shù)組的問(wèn)題,于是,我測(cè)試了下面的例子:
例2
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script> <style> li:hover { cursor: pointer; } </style> </head> <body> <div class="wrap"> <ul> <li v-for="item,index in items" v-on:click="handle(index)"> <span>{{item.name}}</span> <span>{{numbers[index]}}</span> </li> </ul> </div> <script> var vm = new Vue({ el: ".wrap", data: { numbers: [], items: [ {name: 'jjj'}, {name: 'kkk'}, {name: 'lll'}, ] }, methods: { handle: function (index) { // 不是數(shù)組,這里更新數(shù)據(jù)就可以直接在view層渲染 this.items[index].name += " success"; } } }); </script> </body> </html>
這時(shí),我再測(cè)試時(shí)就發(fā)現(xiàn),這里的model層發(fā)生了變化時(shí),view層就能及時(shí)、有效的得到更新。
而數(shù)組為什么不可以呢?
于是在文檔上的一個(gè)不起眼的地方找到了下面的說(shuō)明:
其中最重要的一句話就是 --- 如果對(duì)象是響應(yīng)式的,確保屬性被創(chuàng)建后也是響應(yīng)式的,同時(shí)觸發(fā)視圖更新,這個(gè)方法主要用于避開(kāi)Vue不能檢測(cè)到屬性被添加的限制。
那么什么情況下Vue是不能檢測(cè)到屬性被添加呢? 根據(jù)參考鏈接,我們?cè)谖臋n上看到了很好的說(shuō)明 --- 深入響應(yīng)式原理
首先,我們要了解Vue是如何實(shí)現(xiàn)數(shù)據(jù)的雙向綁定的!
把一個(gè)普通 JavaScript 對(duì)象傳給 Vue 實(shí)例的 data 選項(xiàng),Vue 將遍歷此對(duì)象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter。Object.defineProperty 是僅 ES5 支持,且無(wú)法 shim 的特性,這也就是為什么 Vue 不支持 IE8 以及更低版本瀏覽器的原因。
知識(shí)補(bǔ)充:
訪問(wèn)器屬性不包含數(shù)據(jù)值,他們包含一對(duì)getter函數(shù)和setter函數(shù)(這兩個(gè)函數(shù)不是必須的)。在讀取訪問(wèn)器屬性時(shí),會(huì)調(diào)用getter函數(shù),這個(gè)函數(shù)負(fù)責(zé)返回有效的值;在寫(xiě)入訪問(wèn)器屬性是,會(huì)調(diào)用setter函數(shù)并傳入新值,這個(gè)函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。
訪問(wèn)器屬性不能直接定義,必須是用Object.defineProperty()來(lái)定義。
下面是一個(gè)例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> </head> <body> <script> var book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){ return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); console.log(book.year); // 2004 在讀取訪問(wèn)器屬性時(shí)會(huì)調(diào)用get函數(shù) book.year=2005; // 在給訪問(wèn)器屬性賦值時(shí)會(huì)調(diào)用set函數(shù) console.log(book.edition); // 2 </script> </body> </html>
這個(gè)例子應(yīng)該可以很好的理解訪問(wèn)器屬性了。
所以,當(dāng)對(duì)象下的訪問(wèn)器屬性值發(fā)生了改變之后(vue會(huì)將屬性都轉(zhuǎn)化為訪問(wèn)器屬性,之前提到了), 那么就會(huì)調(diào)用set函數(shù),這時(shí)vue就可以通過(guò)這個(gè)set函數(shù)來(lái)追蹤變化,調(diào)用相關(guān)函數(shù)來(lái)實(shí)現(xiàn)view視圖的更新。
每個(gè)組件實(shí)例都有相應(yīng)的 watcher 實(shí)例對(duì)象,它會(huì)在組件渲染的過(guò)程中把屬性記錄為依賴,之后當(dāng)依賴項(xiàng)的 setter 被調(diào)用時(shí),會(huì)通知 watcher 重新計(jì)算,從而致使它關(guān)聯(lián)的組件得以更新。
即在渲染的過(guò)程中就會(huì)調(diào)用對(duì)象屬性的getter函數(shù),然后getter函數(shù)通知wather對(duì)象將之聲明為依賴,依賴之后,如果對(duì)象屬性發(fā)生了變化,那么就會(huì)調(diào)用settter函數(shù)來(lái)通知watcher,watcher就會(huì)在重新渲染組件,以此來(lái)完成更新。
OK!既然知道了原理,我們就可以進(jìn)一步了解為什么出現(xiàn)了之前數(shù)組的問(wèn)題了!
變化檢測(cè)問(wèn)題
收到現(xiàn)代JavaScript瀏覽器的限制,其實(shí)主要是 Object.observe() 方法支持的不好,Vue不能檢測(cè)到對(duì)象的添加或者刪除。然而Vue在初始化實(shí)例時(shí)就對(duì)屬性執(zhí)行了setter/getter轉(zhuǎn)化過(guò)程,所以屬性必須開(kāi)始就在對(duì)象上,這樣才能讓Vue轉(zhuǎn)化它。
所以對(duì)于前面的例子就不能理解了 --- 數(shù)組中index都可以看做是屬性,當(dāng)我們添加屬性并賦值時(shí),Vue并不能檢測(cè)到對(duì)象中屬性的添加或者刪除,但是其的確是添加或刪除了,故我們可以通過(guò)console看到變化,所以就沒(méi)有辦法做到響應(yīng)式; 而在第二個(gè)例子中,我們是在已有的屬性的基礎(chǔ)上進(jìn)行修改的,這些屬性是在最開(kāi)始就被Vue初始化實(shí)例時(shí)執(zhí)行了setter/getter的轉(zhuǎn)化過(guò)程,所以說(shuō)他們的修改是有效的,model的數(shù)據(jù)可以實(shí)時(shí)的在view層中得到相應(yīng)。
補(bǔ)充知識(shí): 什么是 Object.observe() ?
在介紹之前,不得不殘忍的說(shuō),盡管這個(gè)方法可以在某些瀏覽器上運(yùn)行,但事實(shí)是這個(gè)方法已經(jīng)廢棄!
概述: 此方法用于異步地監(jiān)視一個(gè)對(duì)象的修改。當(dāng)對(duì)象的屬性被修改時(shí),方法的回調(diào)函數(shù)會(huì)提供一個(gè)有序的修改流,然而這個(gè)接口已經(jīng)從各大瀏覽器移除,可以使用通用的proxy 對(duì)象?! ?/p>
方法:
Object.observe(obj, callback[, acceptList])
其中obj就是被監(jiān)控的對(duì)象, callback是一個(gè)回調(diào)函數(shù),其中的參數(shù)包括changes和acceptList,
changes一個(gè)數(shù)組,其中包含的每一個(gè)對(duì)象代表一個(gè)修改行為。每個(gè)修改行為的對(duì)象包含:
acceptList在給定對(duì)象上給定回調(diào)中要監(jiān)視的變化類型列表。如果省略, ["add", "update", "delete", "reconfigure", "setPrototype", "preventExtensions"] 將會(huì)被使用。
var obj = { foo: 0, bar: 1 }; Object.observe(obj, function(changes) { console.log(changes); }); obj.baz = 2; // [{name: 'baz', object: <obj>, type: 'add'}] obj.foo = 'hello'; // [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}] delete obj.baz; // [{name: 'baz', object: <obj>, type: 'delete', oldValue: 2}]
如上所示: 但是chrome也是不支持的,瀏覽器的兼容性如下:
參考文檔: Object.ovserve()
推薦閱讀文章: Object.observe() 引爆數(shù)據(jù)綁定革命
解決方法
使用 Vue.set(object, key, value) 方法將響應(yīng)屬性添加到嵌套的對(duì)象上。 還可以使用 vm.$set 實(shí)例方法,這也是全局 Vue.set 方法的別名。
例3
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vue</title> <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script> <style> li:hover { cursor: pointer; } </style> </head> <body> <div class="wrap"> <ul> <li v-for="item,index in items" v-on:click="handle(index)"> <span>{{item.name}}</span> <span>{{numbers[index]}}</span> </li> </ul> </div> <script> var vm = new Vue({ el: ".wrap", data: { numbers: [], items: [ {name: 'jjj'}, {name: 'kkk'}, {name: 'lll'}, ] }, methods: { handle: function (index) { // WHY: 更新數(shù)據(jù),view層未渲染,但通過(guò)console這個(gè)數(shù)組可以發(fā)現(xiàn)數(shù)據(jù)確實(shí)更新了 if (typeof(this.numbers[index]) === "undefined" ) { this.$set(this.numbers, index, 1); } else { this.$set(this.numbers, index, ++this.numbers[index]); } } } }); </script> </body> </html>
這樣,我們就可以實(shí)現(xiàn)最終的目的了!
第二部分
上面一部分是指在data下的數(shù)組,而如果是在store中的數(shù)組,一般可以這樣:
[ADD_ONE] (state, index) { if ( typeof state.numbers[index] == "undefined") { Vue.set(state.numbers, index, 1) } else { Vue.set(state.numbers, index, ++state.numbers[index]) } }
即使用 Vue.set() 的方式來(lái)改變、增加。
注意:這里是確定index的增加和減少,所以用 Vue.set() 的方式
減的方式如下:
[REMOVE_ONE] (state, index) { Vue.set(state.numbers, index, --state.numbers[index]); }
第四部分
如果是在store的actions中我們需要對(duì)stroe中的數(shù)組進(jìn)行填充,方法如下:
state內(nèi)容:
kindnames: []
Mutations內(nèi)容:
[ADD_KIND_NAME] (state, name) { state.kindnames.push(name); }
注意: 這里直接使用push的方式
當(dāng)然,除了push,我們還可以shift等各種方式。
actions的內(nèi)容:
commit(ADD_KIND_NAME, state.items[index++].name);
這里,state.items[index++].name獲取到的是一個(gè)一個(gè)的字符串。
最后,看到數(shù)據(jù)如下:
注:同樣可以參考文檔 --- 細(xì)節(jié)與最佳實(shí)踐
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
網(wǎng)站名稱:vue中遇到的坑之變化檢測(cè)問(wèn)題(數(shù)組相關(guān))
文章來(lái)源:http://www.rwnh.cn/article34/pgecse.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、域名注冊(cè)、電子商務(wù)、移動(dòng)網(wǎng)站建設(shè)、Google、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)