<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么可以用Object.assign觸發(fā)$watch

          共 1917字,需瀏覽 4分鐘

           ·

          2022-04-01 20:27


          471fb520825b4037554820a97b5dbcb8.webp

          ????油菜花

          Object.assign,這個api在簡單拷貝可枚舉對象的屬性值時經(jīng)常用到。這里介紹一個在vue2中Object.assign的用法,這個用法在官網(wǎng)文檔 有詳細介紹:

          watch:?{
          ?someObject(nvalue,?ovalue)?{
          ??...
          ?}
          }

          //?為對象添加新屬性
          this.someObject?=?Object.assign({},?this.someObject,?{?a:?1,?b:?2?})

          而且要注意的是如果像下面這樣添加上去的新屬性無法觸發(fā)更新:

          this.someObject?=?Object.assign(this.someObject,?{?a:?1,?b:?2?})

          問題是為什么前面那種寫法會有效?

          先看vue2文檔

          在vue2的文檔中有詳細說明,在組件的依賴收集過程中,所有property 在被訪問和修改時會通知變更,對于對象來說,Vue 無法檢測 property 的添加或移除。

          fa240669e894a45d604b0233f3f416a3.webp

          一般情況下,在vue中,如果要對data對象中實例添加根級別property,我們可以這樣操作:

          Vue.set(someObject,?'name',?value)

          或者這樣操作

          this.$set(this.someObject,'name',2)

          但是如果我們要對一個對象添加多個屬性,同時還要保持對象的響應性,這種情況下就要用到開篇提到的方法。

          在mdn 上,對 Object.assign 有這一句解釋:該方法使用源對象的[[Get]]和目標對象的[[Set]],所以它會調(diào)用相關 getter 和 setter。對這句,我們用下面的例子a來理解。

          var?obj?=?{};
          var?c?=?null
          Object.defineProperty(obj,?'c',?{
          ??set:function(x){
          ????console.log('c被賦值:',x);
          ????c=x
          ??},
          ??get:function(){
          ????console.log('c被取出:',c)
          ????return?c
          ??}
          })

          obj.c=3??//c被賦值:3
          obj.c??//c被取出:??3

          obj?=?Object.assign(obj,?{c:?'wer23e'})?//?觸發(fā)了set!
          obj?=?Object.assign(obj,?{a:?'wer23e'})?//?由于事先未用defineProperty定義a,所以無法監(jiān)聽
          //?由于目標對象未定義屬性,無法監(jiān)聽
          obj?=?Object.assign({},obj,?{a:?'wer23e',c:?'dfrr23e'})?

          通過這段代碼可以理解上面說的“Object.assign會使用目標對象的[[Set]]”,同時,這段代碼也演示了vue2中響應原理,因為vue2中所有需要響應的屬性都是用 Object.defineProperty 進行響應綁定,這樣所有的訪問和修改動作都會被追蹤到。但是對于沒有事先被 Object.defineProperty定義的屬性,比如添加一個屬性就無法監(jiān)聽到了。在上面的示例中,即使我用文檔提到的用法 obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'}) 仍然無法觸發(fā)c屬性的 set。走到這一步,是不是得看watch源碼了?其實不必!

          用Object.assign觸發(fā)watch原理

          針對這個問題,watch的源碼不必看,但是 Object.assign 的源碼必須要看,

          if?(typeof?Object.assign?!==?'function')?{
          ??//?Must?be?writable:?true,?enumerable:?false,?configurable:?true
          ??Object.defineProperty(Object,?"assign",?{
          ????value:?function?assign(target,?varArgs)?{?//?.length?of?function?is?2
          ??????'use?strict';
          ??????if?(target?===?null?||?target?===?undefined)?{
          ????????throw?new?TypeError('Cannot?convert?undefined?or?null?to?object');
          ??????}

          ??????var?to?=?Object(target);

          ??????for?(var?index?=?1;?index?arguments.length;?index++)?{
          ????????var?nextSource?=?arguments[index];

          ????????if?(nextSource?!==?null?&&?nextSource?!==?undefined)?{
          ??????????for?(var?nextKey?in?nextSource)?{
          ????????????//?Avoid?bugs?when?hasOwnProperty?is?shadowed
          ????????????if?(Object.prototype.hasOwnProperty.call(nextSource,?nextKey))?{
          ??????????????to[nextKey]?=?nextSource[nextKey];
          ????????????}
          ??????????}
          ????????}
          ??????}
          ??????return?to;
          ????},
          ????writable:?true,
          ????configurable:?true
          ??});
          }

          其實就是把 assign 方法中的參數(shù)的可枚舉屬性全部復制到此方法的第一參數(shù)上去。回頭再去理解下例子a, obj = Object.assign({},obj, {a: 'wer23e',c: 'dfrr23e'}) 無法觸發(fā)c屬性的 set 函數(shù)是因為,obj引用關系已經(jīng)被改變了,不再是原來那個對象,也就沒有了對應的屬性監(jiān)控,但是為什么官方文檔會建議這么用呢?

          接下來,我寫了個小demo,來幫你理解,你可以試下效果