Vue2 基本型別陣列的更新問題

當我們在Vue2中使用陣列時,若陣列中的內容是基本型別,也就是非物件的情況下,如果只更新陣列中的某一筆資料,會發現Vue並不知道資料已更新,導致畫面不會重繪。

在Vue2中的data區塊內所宣告的屬性,都被轉換成getter setter,因此當你重新賦值時,Vue就可以知道你更新了資料,然後就執行重繪畫面的工作。在data內所宣告的物件,如果一開始就有先宣告其屬性,屬性們也會被轉換成getter setter,但後來才加進去的屬性就無法被轉換,除非整個物件重新賦值,才有辦法轉換新的屬性。

宣告過的物件屬性可以觸發更新:

data () {
  return {
    myObj: { a: 0 }
  }
}

mounted () {
  this.myObj.a = 10
}

沒宣告過的不會觸發更新:

this.myObj.b = 10

這樣才能讓b也被轉換,並且觸發更新:

this.myObj = { a: 10, b: 10 }

但陣列中的基本型別因為不是物件,所以即使宣告時已存在,但還是無法轉換為響應式(設getter, setter),所以以下面這種方式改變時不會更新:

this.myArray[0] = 1

下面這幾種情況才會觸發更新:

this.myArray = […this.myArray]
this.myArray = Object.assign([], this.myArray)
this.myArray = this.myArray.splice(0, this.myArray.length)

就是immutable的概念吧

但是,當我們使用push, pop, shift… 這些array的method來操作這個基本型別的陣列時,會發現畫面竟然就乖乖更新了。這是因為vue把data中宣告過的陣列的method都覆寫了。這些是被覆寫過的methods:push, pop, shift, unshift, splice, sort, reverse。

當你執行這些methods的時候,其實並非直接執行native code,而是會執行vue覆寫的function。這隻function做的事情就是先跑完原始native code要做的事情,再發出通知讓vue重繪畫面。有興趣的可以看一下vue.runtime.esm.js,搜尋methodsToPatch。

也就是說,如果有個被覆寫的native function,可以在執行之後不會對目前陣列產生什麼影響的話,我們就可以利用他來當作陣列更新後對vue通知重繪畫面的媒介。這裡面有 push 跟 splice 這兩個methods,如果不帶任何參數的情況下,可以不影響陣列內容。因此我們可以這樣修改並觸發更新:

this.myArray[0] = 10
this.myArray.push()
// or
this.myArray.splice()

Vue3的話因為改用Proxy來監聽改變,所以不需要這樣告知更新。但老實說,我到現在還是不太喜歡Vue3的Reactivity API。尤其是基本型別的ref()的設計。Vue2還是直覺得多,寫起來也比較漂亮。我應該暫時還會是Vue2的愛用者XD

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料