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

Vue2?Observer實(shí)例dep和閉包中dep區(qū)別詳解

 更新時間:2022年10月31日 17:09:35   作者:tomato7777  
這篇文章主要為大家介紹了Vue2?Observer實(shí)例dep和閉包中dep區(qū)別詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

start

此前學(xué)習(xí) Vue2 源碼。對 Vue 源碼中兩次出現(xiàn)的new Dep(),不清楚它們的區(qū)別,寫一個文章記錄一下。

Vue2 源碼中有兩處通過new Dep生成dep實(shí)例:

1. Observer實(shí)例上的dep

export class Observer {
  value;
  dep;
  vmCount;
  constructor(value) {
    this.value = value;
     /* Observer的實(shí)例上有一個 dep 屬性 */
    this.dep = new Dep();
    def(value, "__ob__", this);
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  }

2. 定義getter,setter中dep

export function defineReactive(obj, key, val, customSetter, shallown) {
  /* 這個地方也定義了一個dep */
  const dep = new Dep()
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable,
    configurable,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter(newVal) {
      /* ... */
    },
  })
}

為了方便后續(xù)介紹,我對兩種dep的名稱做一下縮減。

1. Observer實(shí)例上的dep => Observer實(shí)例上的dep

2. 定義getter,setter中dep => 閉包中的dep

既然是 new Dep(),肯定是想要實(shí)現(xiàn)某些功能。想要弄懂這兩種 dep 的區(qū)別,先看看在源碼中它們?nèi)绾喂ぷ鞯摹?/p>

1. 依賴收集

在打包輸出的dist文件夾中,找到完整的 Vue.js 源碼,全局搜索一下dep.depend() (收集依賴的方法)。

僅三處做了依賴收集

// 第 1 種情況
// 當(dāng)觸發(fā)對象屬性 getter 的時候,`閉包中的dep`會收集依賴。
dep.depend()
// 第 2 種情況
// 當(dāng)觸發(fā)對象屬性 getter 的時候,若屬性值有更深的子層級。`Observer實(shí)例上的dep`會收集依賴。
childOb.dep.depend()
// 第 3 種情況
// 當(dāng)觸發(fā)對象屬性 getter的時候,數(shù)據(jù)有更深的子層級且子層級是數(shù)組類型。遞歸遍歷數(shù)組的每一項(xiàng),若數(shù)組項(xiàng)上存在`Observer的實(shí)例`,則對應(yīng)的`Observer實(shí)例上的dep`會收集依賴。
function dependArray(value) {
  for (var e = void 0, i = 0, l = value.length; i < l; i++) {
    e = value[i]
    e && e.__ob__ && e.__ob__.dep.depend()
    if (Array.isArray(e)) {
      dependArray(e)
    }
  }
}

情況說明:

  • 當(dāng)觸發(fā)對象屬性 getter 的時候,閉包中的dep會收集依賴。
  • 當(dāng)觸發(fā)對象屬性 getter 的時候,若屬性值有更深的子層級。Observer實(shí)例上的dep會收集依賴。

當(dāng)觸發(fā)對象屬性 getter 的時候,數(shù)據(jù)有更深的子層級且子層級是數(shù)組類型。遞歸遍歷數(shù)組的每一項(xiàng),若數(shù)組項(xiàng)上存在Observer的實(shí)例,則對應(yīng)的Observer實(shí)例上的dep會收集依賴。

寫到這里我有點(diǎn)疑惑,為什么數(shù)組的情況,還要額外處理?

  • 因?yàn)閿?shù)組中如果存儲的不是對象或數(shù)組,對應(yīng)數(shù)組項(xiàng)不會有Observer的實(shí)例;
  • 因?yàn)閿?shù)組中如果存儲的是對象或數(shù)組,對應(yīng)數(shù)組項(xiàng)身上就會有Observer的實(shí)例;
  • (核心原因:數(shù)組的每一項(xiàng)并不是都會被設(shè)置 getter,所以這里需要遞歸處理一下數(shù)組)

舉個例子

寫一個 Html 頁面測試

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <title>lazy_tomato</title>
  </head>
  <body>
    <div id="app">
      <div>{{ c }}</div>
    </div>
    <script src="./vue.js"></script>
    <script>
      new Vue({
        el: '#app',
        data() {
          return {
            b: '你好呀',
            c: [{}],
            d: {},
          }
        },
      })
    </script>
  </body>
</html>

修改一下Vue源碼,加入打?。?/p>

if (Dep.target) {
  dep.depend()
  /* 這里 */
  console.log('111')
  if (childOb) {
    childOb.dep.depend()
    /* 這里 */
    console.log('222')
    if (Array.isArray(value)) {
      dependArray(value)
    }
  }
}
function dependArray(value) {
  for (var e = void 0, i = 0, l = value.length; i < l; i++) {
    e = value[i]
    e && e.__ob__ && e.__ob__.dep.depend()
    if (e && e.__ob__ && e.__ob__.dep) {
      /* 這里 */
      console.log('333')
    }
    if (Array.isArray(e)) {
      dependArray(e)
    }
  }
}

實(shí)驗(yàn)結(jié)果:

  • 如果頁面沒有使用 data 中的數(shù)據(jù),那么三種情況的 dep.depend() 都不會觸發(fā)。(因?yàn)闆]有觸發(fā) getter)
  • 如果頁面僅僅使用到了 b,b 是簡單類型的數(shù)據(jù),沒有子層級, 1會觸發(fā), 2、3不觸發(fā);
  • 如果頁面僅僅使用到了 c,c 是數(shù)組類型的數(shù)據(jù),而且數(shù)組中的項(xiàng)是對象,1、2、3會觸發(fā) 2不觸發(fā);
  • 如果頁面僅僅使用到了 d,d 是對象類型的數(shù)據(jù),有子層級,1、2會觸發(fā), 3不觸發(fā);

結(jié)論: 由上面的結(jié)果,可以得到結(jié)論:

  • 閉包中的dep 關(guān)注的是對象屬性;
  • Observer實(shí)例上的dep,關(guān)注的是對象中屬性值是對象或者是數(shù)組的情況;

2. 通知更新

在打包輸出的dist文件夾中,找到完整的 Vue.js 源碼,全局搜索一下dep.notify()(通知更新的方法)。僅四處做了依賴收集。

// 第 1 種情況
// 當(dāng)觸發(fā)對象屬性 setter 的時候,`閉包中的dep`會通知更新。
dep.notify()
// 還有三處,都是使用 `ob.dep.notify()` 的方式會通知更新。
// 場景分別為:
// 1. 改寫數(shù)組的七個方法
// 2. set 方法
// 3. del 方法

結(jié)論:

  • 閉包中的dep 用于由對象本身修改而觸發(fā) setter 函數(shù)導(dǎo)致閉包中的 Dep 通知所有的 Watcher 對象。
  • Observer實(shí)例上的dep 則是在對象本身增刪屬性或者數(shù)組變化的時候被觸發(fā)的 Dep。

思考

看到上述的內(nèi)容,可以想到兩種 dep 其實(shí)是各司其職。

  • 閉包中的 dep 用于管理對象本身修改而觸發(fā)的依賴。
  • Observer 實(shí)例上的 dep 用于對象本身增刪屬性或者數(shù)組變化的時候被觸發(fā)的依賴。

它可以用于彌補(bǔ) Object.defineProperty() 的缺陷。

  • 對象屬性 新增或者刪除;
  • 數(shù)組方法不支持響應(yīng)式;
  • 通過數(shù)組索引修改數(shù)據(jù)項(xiàng);

拓展一

如果把 Observer 中初始化 dep 的代碼注釋掉,那么:$set,$del,重寫的七種數(shù)組方法 都將失效。

class Observer {
  constructor(value) {
    // this.dep = new Dep(); // 正確的寫法
    this.dep = { depend() {}, notify() {} }
    /* ... */
  }
}

拓展二

既然源碼中的 $set,$del,重寫的七種數(shù)組方法 ,通知更新,使用的是ob.dep.notify()。 那我們是否可以自己手動通知嗎?

理論是可行的,但是 Vue 源碼相對來說,做了更多特殊場景的考慮。所以用官方提供的 API 更可靠。

手動通知的案例

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <title>lazy_tomato</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="item in list" :key="item">{{item}}</li>
      </ul>
      <h2 @click="foo">點(diǎn)擊我通過數(shù)組索引添加數(shù)據(jù)</h2>
      <h2 @click="bar">點(diǎn)擊我手動更新通知依賴更新</h2>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
    <script>
      new Vue({
        el: '#app',
        data() {
          return {
            list: [1, 2, 3],
          }
        },
        methods: {
          // 點(diǎn)擊我向數(shù)組中添加數(shù)據(jù)
          foo() {
            let length = this.list.length
            this.list[length] = 'tomato' + length // 通過數(shù)組索引修改數(shù)據(jù),默認(rèn)是無法通知依賴更新的。
            console.log(this.list)
          },
          // 點(diǎn)擊我手動更新通知更新
          bar() {
            this.list.__ob__.dep.notify()
          },
        },
      })
    </script>
  </body>
</html>

效果圖:

end

  • 本文就 Dep 的初始化和基礎(chǔ)的使用場景做了學(xué)習(xí)。

再總結(jié)一下:

  • 閉包中的dep 用于由對象本身修改而觸發(fā) setter 函數(shù)導(dǎo)致閉包中的 Dep 通知所有的 Watcher 對象。
  • Observer實(shí)例上的dep 則是在對象本身增刪屬性或者數(shù)組變化的時候被觸發(fā)的 Dep。

更多關(guān)于Vue2 Observer與dep閉包的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

通河县| 鄯善县| 修水县| 南岸区| 富蕴县| 赤壁市| 洛南县| 五指山市| 冷水江市| 巢湖市| 石楼县| 青神县| 建昌县| 株洲市| 永安市| 北辰区| 舒城县| 宁阳县| 读书| 长泰县| 金阳县| 莱西市| 沈丘县| 莫力| 达州市| 芦溪县| 崇义县| 沁水县| 呼玛县| 长春市| 象州县| 安乡县| 莱芜市| 彰化县| 都江堰市| 班玛县| 湘阴县| 寿光市| 大姚县| 泸西县| 霍城县|