最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue2無法監(jiān)聽數(shù)組下標(biāo)和對象新增屬性的原因和解決方法

 更新時間:2026年05月18日 08:25:06   作者:yqcoder  
文章詳細(xì)解釋了Vue2中無法監(jiān)聽對象屬性新增/刪除和數(shù)組下標(biāo)變化的原因,并提供了解決方案,如使用Vue.set和重寫數(shù)組方法,還對比了Vue3的Proxy,強調(diào)了Proxy在監(jiān)聽機制和性能上的優(yōu)勢,需要的朋友可以參考下

現(xiàn)象回顧:那些讓人頭禿的 Bug

在 Vue 2 開發(fā)中,你一定遇到過以下場景:

? 場景 1:直接通過索引修改數(shù)組

data() {
    return {
        list: ['a', 'b', 'c']
    }
},
methods: {
    updateItem() {
        this.list[0] = 'x'; // ? 視圖不會更新!
        console.log(this.list); // ['x', 'b', 'c'] (數(shù)據(jù)變了,但界面沒變)
    }
}

? 場景 2:直接添加新屬性

data() {
    return {
        user: { name: 'Alice' }
    }
},
methods: {
    addAge() {
        this.user.age = 25; // ? 視圖不會更新!
        console.log(this.user); // { name: 'Alice', age: 25 } (數(shù)據(jù)變了,但界面沒變)
    }
}

疑問:既然 JavaScript 對象和數(shù)組都是引用類型,為什么 Vue 能監(jiān)聽到 push 或普通屬性的修改,卻對“下標(biāo)賦值”和“新增屬性”視而不見?

答案藏在 Vue 2 的核心實現(xiàn)—— Object.defineProperty 中。

1. 核心原理:Object.defineProperty 的工作方式

Vue 2 在初始化時,會遍歷 data 中的所有屬性,并使用 Object.defineProperty 為它們定義 gettersetter。

// 簡化版 Vue 2 響應(yīng)式初始化
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      console.log(`讀取了 ${key}`);
      // 收集依賴(Dep)
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      console.log(`設(shè)置了 ${key} 為 ${newVal}`);
      // 通知更新(Notify)
    },
  });
}

關(guān)鍵點Object.defineProperty針對特定屬性進行劫持的。它只能監(jiān)聽已經(jīng)存在于對象上的屬性。

2. 痛點一:為什么不能監(jiān)聽對象屬性的新增/刪除?

原因分析

當(dāng)你執(zhí)行 this.user.age = 25 時:

  1. user 對象在初始化時只有 name 屬性。
  2. Vue 只為 name 定義了 getter/setter。
  3. age 是一個全新的屬性,它身上沒有任何 getter/setter
  4. JavaScript 引擎直接將該屬性添加到對象上,完全繞過了 Vue 的攔截機制
  5. Vue 根本不知道 age 被添加了,因此不會觸發(fā)視圖更新。

同理,delete this.user.name 只是刪除了屬性,也不會觸發(fā)任何通知。

比喻
Vue 2 像一個保安,只認(rèn)識門口登記過的住戶(已定義的屬性)。
如果一個陌生人(新屬性)直接翻 墻進來(直接賦值),保安根本看不見,也不會通知業(yè)主(視圖)。

解決方案:Vue.set/this.$set

Vue 提供了 API 來手動觸發(fā)響應(yīng)式:

// 語法:Vue.set(target, propertyName/index, value)
this.$set(this.user, "age", 25);

內(nèi)部原理

  1. 判斷目標(biāo)是否是響應(yīng)式對象。
  2. 如果是新屬性,調(diào)用 Object.defineProperty 為該屬性添加 getter/setter。
  3. 手動觸發(fā)依賴通知。

3. 痛點二:為什么不能監(jiān)聽數(shù)組下標(biāo)的變化?

原因分析

當(dāng)你執(zhí)行 this.list[0] = 'x' 時:

  1. 數(shù)組在初始化時,Vue 會遍歷其元素。如果元素是對象,會遞歸劫持;如果是基本類型,數(shù)組本身并沒有為每個索引(0, 1, 2…)定義 getter/setter。
  2. 性能考量:如果為數(shù)組的每一個索引都定義 getter/setter,當(dāng)數(shù)組長度為 10,000 時,內(nèi)存開銷巨大,且初始化極慢。
  3. 語言限制Object.defineProperty 雖然可以監(jiān)聽索引,但 Vue 2 出于性能考慮,沒有對數(shù)組索引進行劫持。
  4. 因此,直接通過索引賦值 list[0] = 'x' 只是一個普通的 JavaScript 賦值操作,不會觸發(fā) setter。

注意:你可能聽說過“Vue 重寫了數(shù)組方法”。是的,Vue 重寫了 push, pop, shift, unshift, splice, sort, reverse 這 7 個方法。

  • 這些方法會改變數(shù)組長度或內(nèi)容,Vue 在這些方法內(nèi)部手動觸發(fā)了通知。
  • 但是,list[0] = 'x' 不是方法調(diào)用,而是屬性賦值,所以無法被攔截。

解決方案

使用變異方法

this.list.splice(0, 1, "x"); // ? 觸發(fā)更新

使用 Vue.set

this.$set(this.list, 0, "x"); // ? 觸發(fā)更新

替換整個數(shù)組

this.list = [...this.list]; // 或者使用 slice, concat 返回新數(shù)組
// 賦值給 this.list 會觸發(fā) list 屬性的 setter,從而更新視圖

4. 解決方案:Vue 2 是如何“打補丁”的?

為了彌補 Object.defineProperty 的缺陷,Vue 2 做了兩件事:

1. 重寫數(shù)組原型方法

Vue 攔截了數(shù)組的 7 個變異方法,在執(zhí)行原生方法后,手動調(diào)用 ob.dep.notify() 通知更新。

// 偽代碼
const arrayProto = Array.prototype;
const methodsToPatch = [
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse",
];

methodsToPatch.forEach((method) => {
  const original = arrayProto[method];
  def(arrayMethods, method, function mutator(...args) {
    const result = original.apply(this, args);
    const ob = this.__ob__;
    ob.dep.notify(); // 手動通知
    return result;
  });
});

2. 提供$set和$deleteAPI

允許開發(fā)者手動將新屬性轉(zhuǎn)化為響應(yīng)式,或刪除響應(yīng)式屬性并通知更新。

5. 對比 Vue 3:Proxy 如何完美解決?

Vue 3 使用 Proxy 替代了 Object.defineProperty,徹底解決了上述問題。

Proxy 的優(yōu)勢

  1. 攔截整個對象Proxy 代理的是整個對象,而不是單個屬性。
  2. 攔截所有操作:包括屬性的讀取、賦值、刪除、甚至 in 操作符。
  3. 天然支持?jǐn)?shù)組索引:對數(shù)組索引的賦值會被 set 陷阱捕獲。
  4. 天然支持新增屬性:對新屬性的賦值也會被 set 陷阱捕獲。
// Vue 3 簡化原理
const data = new Proxy(
  {},
  {
    get(target, key) {
      track(target, key); // 收集依賴
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const result = Reflect.set(target, key, value);
      trigger(target, key); // 觸發(fā)更新
      return result;
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key);
      trigger(target, key); // 觸發(fā)更新
      return result;
    },
  },
);

// ? 以下操作都能被攔截并觸發(fā)更新
data.list[0] = "x";
data.user.age = 25;
delete data.user.name;

結(jié)論:Vue 3 不再需要 $set,也不再需要擔(dān)心數(shù)組下標(biāo)的問題。代碼更符合 JavaScript 原生直覺。

6. 總結(jié)

特性Vue 2 (Object.defineProperty)Vue 3 (Proxy)
監(jiān)聽機制遞歸定義屬性的 getter/setter代理整個對象,攔截所有操作
對象新增屬性? 無法監(jiān)聽(需 $set? 原生支持
對象刪除屬性? 無法監(jiān)聽(需 $delete? 原生支持
數(shù)組索引賦值? 無法監(jiān)聽(需 splice$set? 原生支持
數(shù)組長度修改? 無法監(jiān)聽? 原生支持
性能初始化慢(遞歸遍歷)初始化快(懶代理)

?? 博主寄語
理解 Vue 2 的局限性,不僅能幫你避免 Bug,更能讓你深刻體會技術(shù)演進的必要性。
Object.defineProperty 是時代的產(chǎn)物,而 Proxy 則是現(xiàn)代化的利器。

記住口訣
Vue 2 劫持靠定義,
新增下標(biāo)難留意。
若要更新需 Set,
變異方法也可以。

Vue 3 代理更強大,
任意操作全攔截。
代碼直觀無死角,
響應(yīng)系統(tǒng)真厲害。

希望這篇文檔能幫你徹底搞懂 Vue 2 響應(yīng)式的底層原理!

以上就是Vue2無法監(jiān)聽數(shù)組下標(biāo)和對象新增屬性的原因和解決方法的詳細(xì)內(nèi)容,更多關(guān)于Vue2無法監(jiān)聽數(shù)組下標(biāo)和對象新增屬性的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 前端處理axios請求下載后端返回的文件流代碼實例

    前端處理axios請求下載后端返回的文件流代碼實例

    使用axios可以很方便地獲取后端返回的文件流數(shù)據(jù),并在前端直接在瀏覽器下載,這篇文章主要給大家介紹了關(guān)于前端處理axios請求下載后端返回的文件流的相關(guān)資料,需要的朋友可以參考下
    2024-07-07
  • Vue3實現(xiàn)雙向綁定的基本原理和代碼示例解析

    Vue3實現(xiàn)雙向綁定的基本原理和代碼示例解析

    Vue3中的響應(yīng)式系統(tǒng)由Proxy實現(xiàn),它取代了Vue2中的Object.defineProperty,提供了更強大的功能和更好的性能,下面給大家介紹Vue3實現(xiàn)雙向綁定的基本原理和代碼示例解析,感興趣的朋友一起看看吧
    2024-12-12
  • vue2引入Three.js預(yù)覽3D文件的實現(xiàn)方法

    vue2引入Three.js預(yù)覽3D文件的實現(xiàn)方法

    three.js是一個基于WebGL的JavaScript庫,它允許開發(fā)者在網(wǎng)頁上創(chuàng)建和顯示3D圖形,這篇文章主要介紹了vue2引入Three.js預(yù)覽3D文件的實現(xiàn)方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-10-10
  • vue使用canvas畫布實現(xiàn)平面圖點位標(biāo)注功能(最新推薦)

    vue使用canvas畫布實現(xiàn)平面圖點位標(biāo)注功能(最新推薦)

    這篇文章主要介紹了vue使用canvas畫布實現(xiàn)平面圖點位標(biāo)注功能,經(jīng)過本文一番研究發(fā)現(xiàn)canvas標(biāo)簽可以完成很多功能,電子簽名,點位標(biāo)注,問題標(biāo)注,畫圖功能,感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • Vue.js事件處理器與表單控件綁定詳解

    Vue.js事件處理器與表單控件綁定詳解

    這篇文章主要為大家詳細(xì)介紹了Vue.js事件處理器與表單控件綁定詳解的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • vue動態(tài)綁定ref(使用變量)以及獲取方式

    vue動態(tài)綁定ref(使用變量)以及獲取方式

    這篇文章主要介紹了vue動態(tài)綁定ref(使用變量)以及獲取方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue項目中axios使用詳解

    vue項目中axios使用詳解

    這篇文章主要介紹了vue項目中axios使用方法以及原理介紹,如果你正巧學(xué)習(xí)這個知識點,那么參考下吧。
    2018-02-02
  • 關(guān)于VUE的編譯作用域及slot作用域插槽問題

    關(guān)于VUE的編譯作用域及slot作用域插槽問題

    這篇文章主要介紹了VUE 的編譯作用域及slot作用域插槽問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-07-07
  • vue填坑之webpack run build 靜態(tài)資源找不到的解決方法

    vue填坑之webpack run build 靜態(tài)資源找不到的解決方法

    今天小編就為大家分享一篇vue填坑之webpack run build 靜態(tài)資源找不到的解決方法。具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • Vue.js中使用Vuex但未正確配置Store問題以及解決過程

    Vue.js中使用Vuex但未正確配置Store問題以及解決過程

    這篇文章主要介紹了Vue.js中使用Vuex但未正確配置Store問題以及解決過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-05-05

最新評論

剑川县| 崇信县| 商都县| 博罗县| 德令哈市| 凯里市| 铜山县| 宁都县| 鱼台县| 徐闻县| 商城县| 庆城县| 万源市| 连江县| 偏关县| 珲春市| 太原市| 子洲县| 邹平县| 中西区| 余庆县| 淮安市| 临潭县| 安溪县| 广昌县| 拉萨市| 铁岭市| 漳州市| 景泰县| 布尔津县| 东方市| 云阳县| 滦南县| 武胜县| 颍上县| 慈利县| 衡南县| 黄梅县| 留坝县| 鄯善县| 扶沟县|