最新国产好看的视频,伊人天堂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,強(qiáng)調(diào)了Proxy在監(jiān)聽機(jī)制和性能上的優(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 的核心實(shí)現(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)鍵點(diǎn)Object.defineProperty針對特定屬性進(jìn)行劫持的。它只能監(jiān)聽已經(jīng)存在于對象上的屬性。

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

原因分析

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

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

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

比喻
Vue 2 像一個保安,只認(rèn)識門口登記過的住戶(已定義的屬性)。
如果一個陌生人(新屬性)直接翻 墻進(jì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. 痛點(diǎn)二:為什么不能監(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ù)組索引進(jìn)行劫持。
  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 是如何“打補(bǔ)丁”的?

為了彌補(bǔ) 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)聽機(jī)制遞歸定義屬性的 getter/setter代理整個對象,攔截所有操作
對象新增屬性? 無法監(jiān)聽(需 $set? 原生支持
對象刪除屬性? 無法監(jiān)聽(需 $delete? 原生支持
數(shù)組索引賦值? 無法監(jiān)聽(需 splice$set? 原生支持
數(shù)組長度修改? 無法監(jiān)聽? 原生支持
性能初始化慢(遞歸遍歷)初始化快(懶代理)

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

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

Vue 3 代理更強(qiáng)大,
任意操作全攔截。
代碼直觀無死角,
響應(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)文章

最新評論

安泽县| 华蓥市| 石阡县| 北川| 上杭县| 康马县| 大石桥市| 武邑县| 新闻| 宜良县| 都兰县| 南华县| 子洲县| 五指山市| 都江堰市| 北海市| 砚山县| 五指山市| 太原市| 乳山市| 新绛县| 丹寨县| 扶余县| 禹州市| 天气| 兰溪市| 淳化县| 唐河县| 滦平县| 宁化县| 濮阳县| 黄石市| 益阳市| 商河县| 马龙县| 咸丰县| 祁阳县| 会同县| 灌南县| 平原县| 阿瓦提县|