首頁  >  文章  >  web前端  >  聊聊Vue中$set是如何實現的?

聊聊Vue中$set是如何實現的?

青灯夜游
青灯夜游轉載
2022-12-15 21:26:323020瀏覽

聊聊Vue中$set是如何實現的?

在日常開發中,$set的也是一個非常實用的API,因為Vue2實作響應式的核心是利用了ES5的Object.defineProperty,當我們透過直接修改數組下標更改數組或為物件添加新的屬性,這時候Object.defineproperty是監聽不到資料的變化的,這時候大家就會用上 $set,讓修改的操作也實現響應,我們知其然更要知其所以然,接下來看一下Vue中的$set是如何實現的。 【相關推薦:vuejs影片教學web前端開發

#應用程式場景

 let dataArr = ["item1"];
 let dataObject = {
      name: "ccs"
    };

    dataArr[2] = "item2";
    dataObject.age = 22;
    响应失败,页面没有显示更新新增的数据

    this.$set(this.dataArr,2,'item2')
    this.$set(this.dataObject,'age',22)
    响应成功,页面显示更新新增的数据

set實作

接下來我們來看看$set在Vue中的定義

function set(target: Array<any> | Object, key: any, val: any): any {
  if (
    process.env.NODE_ENV !== "production" &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(
      `Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
    );
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key);
    target.splice(key, 1, val);
    return val;
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val;
    return val;
  }
  const ob = (target: any).__ob__;
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== "production" &&
      warn(
        "Avoid adding reactive properties to a Vue instance or its root $data " +
          "at runtime - declare it upfront in the data option."
      );
    return val;
  }
  if (!ob) {
    target[key] = val;
    return val;
  }
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val;
}

在原始碼中先判斷set的目標是否是undefined基本型別如果是 undefined基本類型就報錯,
因為使用者不應該往undefined和基本類型中set東西
然後又判斷了目標是否是數組與key是不是合法的index,合法的index是指值為大於等於0的整數,
如果兩個條件都成立就對目標數組調用splice方法插入或者修改數組
這裡的splice不是普通的splice是王維詩裡的splice,是被vue代理人重寫過的splice

#陣列實作回應

$set實作陣列修改回應的方式是代理人重寫的陣列的一部分方法,接下來我們看一下具體實作

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  &#39;push&#39;,
  &#39;pop&#39;,
  &#39;shift&#39;,
  &#39;unshift&#39;,
  &#39;splice&#39;,
  &#39;sort&#39;,
  &#39;reverse&#39;
]
function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true
    });
}
methodsToPatch.forEach(function (method) {
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case &#39;push&#39;:
      case &#39;unshift&#39;:
        inserted = args
        break
      case &#39;splice&#39;:
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    ob.dep.notify()
    return result
  })
})

vue中代理重寫的不只是splice,有push、pop、shift、unshift、splice、sort、reverse這七個方法, 首先執行了const result = original.apply(this, args)#執行原本數組的方法並取得它的值,接下來判斷如果是往數組中新增值就將新加入的值也實作響應式,
最後一步拿到這個陣列的_ob_物件_ob_ 裡的dep進行派發更新。
想深入了解vue的回應式可以查閱往期文章
面試官問你Vue2的回應式原理,你怎麼答? - 掘金(juejin.cn)

物件實作回應

$set中下半部的邏輯就是用來處理物件回應的,我們接著往下看

  if (key in target && !(key in Object.prototype)) {
    target[key] = val;
    return val;
  }
  const ob = (target: any).__ob__;
  if (!ob) {
    target[key] = val;
    return val;
  }
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val;

首先判斷了屬性如果在目標物件中直接return結束邏輯,
因為vue只有在新增目標物件中原本沒有的屬性時才會失去回應
例如let obj={}  obj.name='ccs'
vue在初始化的時候會將data裡的所有屬性都變成響應式,如果的值是物件或陣列則會new一個Observer實例儲存在__ob__,想深入了解vue的回應式可以查閱往期文章
面試官問你Vue2的響應式原理,你怎麼答? - 掘金(juejin.cn)
拿到這個物件的_ob_進行判斷,如果不存在就表示是未經過vue初始化的普通物件而不是回應式物件否則就手動透過defineReactive為屬性新增get方法與set方法實作回應
然後手動呼叫dep裡的notify()發布更新。

總結

vue中$set方法對數組和物件的處理本質上的一樣的,對新增的值添加回應然後手動觸發派發更新。

(學習影片分享:vuejs入門教學程式設計基礎影片

以上是聊聊Vue中$set是如何實現的?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除