1. 程式人生 > 其它 >Vue深入響應式原理之物件 - 為什麼我的修改沒有生效?

Vue深入響應式原理之物件 - 為什麼我的修改沒有生效?

技術標籤:前端vuevue.js

如有錯誤望請指出

在開發過程中可能會遇到修改了值,檢視未發生更新的情況。

實際上在vue的官方文件中也有描述,見 深入響應式原理

這裡會以實際案例講述

問題重現

下面是一個迴圈顯示物件的vuejs程式碼(建議有條件的可以直接執行檢視效果)

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body> <div id="root"> <div v-for="(v,k) in obj">{{k}} => {{v}}</div> <button @click="setA">賦值A</button> <button @click="setB">賦值b</button> <button @click="
print"
>
列印</button> </div> </body> <script> new Vue({ el: '#root', data() { return { obj: { a: 1 } } }, methods: { setA() { this
.obj.a = 'new A'; }, setB() { this.obj.b = 2; }, print() { console.log(this.obj) } } });
</script> </html>

通過瀏覽器開啟後初始狀態如下圖:
在這裡插入圖片描述
點選賦值A 後,頁面發生了預期的變化,結果如下:
在這裡插入圖片描述
點選 賦值b 後,此時發現並沒有發生預期的變化,html並沒有渲染出b => 2這樣的內容。
但實際物件確實新增了一個值為2b屬性,點選列印驗證
在這裡插入圖片描述

如何解決?

方法一:提前定義

咱們可以很明顯的發現差異,a是原來就定義在obj中的,而b不是。
因此第一個方法就是定義一個空值b。如下:

...
...
 data() {
     return {
         obj: {
             a: 1,
             b: null
         }
     }
 }
...
...

方法二:使用 $set

方法一雖然有效,但有時確實存在無法預先知道要新增的屬性名或者說懶得(?)。

出現問題的本質是vue不知道新增了一個b屬性,vue本身提供了一個$set方法用於解決這個問題,將setB中賦值方法修改。
程式碼如下:

    setB() {
        // this.obj.b = 2;
        this.$set(this.obj, 'b', 2);
    },

此時再點選賦值b就可以看到html發生了符合預期的變化。

為什麼?

要實現在值發生變化時頁面也同時發生改變,關鍵是在於發現值的變化

Vue利用了Object.defineProperty (這裡有更詳細的描述)。簡單的說,使用這個方法可以給物件的某一個屬性加上getter/setter方法,之後在讀取這個值時會執行getter方法,在設定時會執行setter方法。以此就可以監聽到物件屬性的存取操作,繼而也就能實現html的更新。

因此,當在data中定義完值並返回後,vue會對其中物件(陣列的情況暫不考慮)的所有屬性,進行遞迴式的Object.defineProperty
這也是為什麼,需要提前定義。因為這個方法並不能監聽到物件屬性的新增刪除

vue作者因此提供了$set$delete 這樣的顯式操作。以支援響應式。

另外對於陣列

對於陣列,可以知道能夠對陣列自身產生變化的方法只有七個。
分別是:push,pop,shift,unshift,splice,sort,reverse
因此vue中對這些方法重新進行了定義,能夠在呼叫這些方法時發現數組的變化。
所以!當以陣列下標的方式修改陣列時,vue也是發現不了的,這個時候還是得使用$set來解決。

其他:Proxy

在vue3中Object.defineProperty方式被放棄,轉而使用Proxy實現對資料的觀察。Proxy從能力上是優於Object.defineProperty的,更詳細內容的,先自己查查吧。。