Vee理解の白話getter/setter

9026 ワード

原文のより良い読書体験
通常のJavaScriptオブジェクトをVueインスタンスのdataオプションに渡すと、Vueはこのオブジェクトのすべてのプロパティを遍歴し、Objectを使用します.definePropertyは、これらのプロパティをすべてgetter/setterに変換します.Object.definePropertyはES 5のshimできない特性です.つまり、VueはIE 8以降のブラウザをサポートしていない理由です.
以上は深い応答式の原理から抜粋した.
では、これらの属性をすべてgetter/setterに変換するのは具体的にどのようなプロセスですか?本文は深く具体的ではなく、簡単にその過程を大体理解し、全体的に把握し、その主な構想を理解することを目的としている.
仮定コードは次のとおりです.
const vm = new Vue({
  el: '#app',
  data: {
    msg: 'hello world'
  }
})

Dataオプションは、オブジェクトまたはメソッドを受信できます.ここでは、オブジェクトを例に挙げます(実際には最後にオブジェクトに変換されます).
まず、このオブジェクトのすべてのキー値ペアがvm._dataにマウントされます(また、vm._dataオブジェクトには__ob__keyがあり、一時的に無視できます).これにより、vm._data.msgでデータにアクセスできます.
しかし、通常はvm.msgでデータにアクセスしていますが、どうすればいいのでしょうか.実はエージェントをして、dataキー値ペアのvm[key]へのアクセスをvm.にエージェントしました.data[key]上
proxy(vm, `_data`, key)

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

通常vm._data(下線変数)は内部プログラムとして使用され、外部に露出するAPIはvm.$data、実はこの2つも1つのもので、エージェントをして、コードは大体このようにします:
const dataDef = {}
dataDef.get = function () { return this._data }

Object.defineProperty(Vue.prototype, '$data', dataDef)

if (process.env.NODE_ENV !== 'production') {
  dataDef.set = function () {
    warn(
      'Avoid replacing instance root $data. ' +
      'Use nested data properties instead.',
      this
    )
  }
}

簡単な理解はvmにアクセスすることです.data.msgはvm.へのアクセスですdata.msg.開発環境でvmに直接data = xxx vm.$data.msg=xxx`のような賦値で、後者は大丈夫です)
そこで,vm.msg,vm._data.msg,vm.$data.msgおよびvm._data.msgの3つの方法でデータを取得/変更できる理由を理解し,最も原始的なデータはvm.$data.msgであり,他の2つがエージェントである.dataのデータ、vm.msgはVueが外部に提供するAPIであり、一般的にはthis.$dataを直接使用する開発が多く、便利であり、data全体を取得するには、this.dataではなくvm._data.msgを使用する必要がある.
次にgetter/setter
demoを少し追加します.
const vm = new Vue({
  el: '#app',
  data: {
    msg: 'hello world'
  },
  computed: {
    msg2() {
      return this.msg + '123'
    }
  }
})

msg 2はmsgに依存し、msgが変化するとmsg 2の値が自動的に更新される必要があり、msgの変化はthis.msgのsetterで監視することができるが、msg 2がmsgに依存していることをどのように知っているのだろうか.
直感的には、すべてのcomputedオブジェクトのキー値ペアを遍歴し、分析することは理論的に可能であるように思われますが、ASTを解析する必要があるのか、あるいは正則的にマッチングして、this.$data.msgが使われているかどうかを見てみましょう.これは面倒くさいでしょう.そして、プログラムにmsg 2が使われていなければ、それは余計なことではありませんか.
実際、Vueが初期化されると、this._data.msgのキー値ペアごとにgetter/setterが設定され、コードは次のようになります.
// obj    vm._data
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
  defineReactive(obj, keys[i])
}

Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  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) {
    const value = getter ? getter.call(obj) : val
    /* eslint-disable no-self-compare */
    if (newVal === value || (newVal !== newVal && value !== value)) {
      return
    }
    /* eslint-enable no-self-compare */
    if (process.env.NODE_ENV !== 'production' && customSetter) {
      customSetter()
    }
    // #7981: for accessor properties without setter
    if (getter && !setter) return
    if (setter) {
      setter.call(obj, newVal)
    } else {
      val = newVal
    }
    childOb = !shallow && observe(newVal)
    dep.notify()
  }
})

Vue応答式のコアは,getterの場合は依存を収集し,setterの場合は依存更新をトリガーする.
また、上記のcomputed msg 2を例にとると、msg 2の値を初めて取る場合(値を取る行為でなければならないことに注意し、templateでもプログラムでもよい)、必ず値dataを取る必要があり、これはmsgのgetterをトリガーし、このときmsg 2がmsgに依存していることを確認することができる.
msgはどんなものに頼ることができますか?今のところ3つあるようだ
  • templateテンプレート
  • computed
  • watch

  • 私たちはvm._dataを印刷することができて、Watcherインスタンスの配列で、直接インスタンスのexpression値を見て、実はこの式をトリガする時、msgのgetterをトリガします
    この式は上記の3つのケースに対応しており、msgが変更された場合、これらの式は再評価する必要があるため、これらの依存項目は保存されるため、ソースコードにはこのWatcherクラスが定められている.
    A watcher parses an expression, collects dependencies, and fires callback when the expression value changes. This is used for both the $watch() api and directives.
    watcher.deps配列はこのwatcherの依存項を表し、値はDepインスタンスであり、Watcherインスタンスの式に関連するdataデータとして理解できる.なお、deps配列は空である可能性があり、templateではdataに依存せず、computedではこのcomputedデータはまだ取得されていません(例えば私はmsg 2を定義したが、プログラムには役に立たない.このときdepsは空である.これは私がmsgを変更した場合、msg 2に通知する必要はないことを示している.msg 2はまったく使われていないからだ.しかし、コンソールにvm.msg 2を入力し、msgのgetterをトリガーし、依存収集を行った.このときdepsは空ではない.これは私がmsg 2を使用したことを示している.次回はmsg更新時にはmsg 2に変更を通知する必要があります)
    watchでは、depsが空ではない場合を試してみましたが、ソースコードの確認をさらに確認する必要があります.
    deps配列要素はDepインスタンスであり、このインスタンスにはsubs属性があり、Watcherインスタンス配列であり、このDepに依存する項目を表す
    WatcherとDepは理解しにくいので、一時的には理解できますが、Depとdataはフックされ、各Depインスタンスはdataのキー値ペアに対応し、WatcherインスタンスはDepに依存し、3つの状況が依存します.つまり、以上の3つです(データが新しい場合、この3つだけが同時に更新されるか、または同時に応答する必要があるか考えてみてください)
    まとめ:私たちはthis.msgのすべてのキー値に対してgetter/setterを設定し、getterの場合は依存を収集します(依存項目は上記の3つで、どんな場合でも依存を収集するわけではありません.例えばフックにmsgを印刷すると、依存していないので、ソースコードには複雑な判断があります).setterの場合は収集の依存をトリガーします.データを更新し、これらを理解すれば、Vueの応答式の原理を初歩的に理解することができる.
    最後に見たら、私の公衆番号「私たちは勝手に覚えています」に注目してください.
    転載先:https://juejin.im/post/5cb51871f265da0368145069