フロントエンドノート-vue 2.x data初期化およびObserver

6486 ワード

前提:すでにcloneを降りてvueソースコード
vueのdataプロパティは、実際の使用で最も一般的なものであり、よく言われている双方向バインドです.この記事では、dataプロパティの初期化と双方向バインドにおけるmodelバインド部分について説明します.
まず全体の論理過程を概説する.
1、vueインスタンスの初期化
2.インスタンスcreatedライフサイクルの前に、props、methods、data、computed、watchを初期化するinitState関数を実行する
3、data初期化では、vueはdataにオブザーバーObserverをバインドし、dataのフィールドが更新されるたびに依存コレクタDepに応答更新をトリガーするよう通知する
次に本題を言います:dataはnew Vueの中で初期化したので、具体的な経路を見てみましょう
1、new Vue初期化位置:/src/core/instance/index.jsのinitMixin関数で
2、initMixin関数は/src/core/instance/state.jsで同じファイルのinitState関数が呼び出されました
3、initState関数ではまず、ユーザがdata属性を設定したか否かを判断し、以下の図
4、initData関数を直接見てみましょう
vueを使用する場合、dataは2つの方法で定義できます.1つは関数で1つのオブジェクトを返すことであり、もう1つは直接1つのオブジェクトを定義することです.
1.initData関数では、まずdataタイプが関数であるか否かを判断する、関数であればgetData関数を呼び出すのはただである.call関数をクリックしてオブジェクトを返し、オブジェクトを最初にインスタンスにコピーします.data上.
2、ここでisPlainObject関数は、dataオブジェクトのtoString関数を呼び出してObjectか否かを判断し、_toString.call(obj) === '[object Object]'
3、keysはdataのすべてのフィールド、propsはインスタンスのprops、methodsはインスタンスのmethodesであり、whileはdataの各keyを遍歴し始め、まずmethodsに重複するkeyがあるかどうかを判断し、それからpropsに重複するkeyがあるかどうかを判断する.ここでは、isReserved関数により、保持キー「」が使用されたか否かを判断すると「$」
4、key文字列にキー文字が含まれていないので、次にproxy関数を呼び出します.この関数の仕事はkeyのget、set属性を設定してvueインスタンスに構成することです.ここではproxyのvueを見ます.keyのget、setメソッドは実際に取得、変更されたのはvue.data.key.
双方向バインドObserver
次に、vue双方向バインドのいずれかのmodelバインドが再開され、上記のソースコードの最後の行のコールバックに続いてobserve関数でmodelバインドが開始されます.上記を続ける前にObserveが何をしたか見てみましょう
まず、人の公式解釈を見てみましょう.観察者類は各観察対象に付加されます.接続すると、オブジェクトのプロパティキーは、依存関係を収集し、更新を割り当てるgetter/setterに変換されます.このObserver類についてお話しします.
1、constructorでvalue(datakey)、dep(依存コレクター)、vmCount(このオブザーバーのノードを購読)を初期化
2、次にdef関数を呼び出し、dataに__を定義するob__プロパティ、dataをobserverにバインドしたと理解できます.ob__このoberverを指して
3、次にvalueが配列であるかどうかを判断します.valueがオブジェクトである場合、thisが呼び出されます.walk関数:walk関数は、オブジェクトのすべてのkeyを取得し、keyを巡回してdefineReactive関数を実行します.
defineReactive関数を見てみましょう(1枚の図が切れないならソースコードに直接行きましょう)
/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  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: 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()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })
}

面接では、さまざまな文章で言う双方向バインドのget、setがこの関数で実現されますので、解析してみましょう
1、newはdep依存収集(後述)
2.getownPropertyDescriptorでdataのkeyに対応するアクセサ属性を取得してpropertyに付与し、現在のkeyがdataに存在して構成可能であるかどうかを判断する(configurableはtrueである)
3、アクセサ属性のget、set属性を取得し、setterが存在するかgetterが存在しないか、および実参各戸が2つしかないと判断した場合にvalをdata中のkeyに対応するvalue(walkでは2つのパラメータが伝達されている)
4、ここではshallowフィールドを見ました.ここではvalの深さ浅さ遍歴(オブジェクトであれば)を観察者にバインドすると理解しています.ではwalk関数には伝則がデフォルトではありません!shallowはtureです.observeの後で議論します
5、次は誰もが知っているget、setに着きます.まずget属性を言います.私はそれを見て、get属性が設定されているかどうかを判断します(第3条で取得します).あればcall getter関数、なければval値を返します.次にDepのtarget(その役割は後述するwatcherと組み合わせて詳細に説明する)を判断し、ここではgetのたびにDepのsubsにサブエレメントが含まれ、配列であれば配列の各サブエレメントがDepのsubsにサブエレメントがサブエレメントにサブスクリプションされると理解する.
6、setプロパティを見て、まずgetterでvalueを取得し、新しいvalueと古い値==全等、新しいvalueと古いvalueのgetterメソッドが書き換えられた場合(getのvalueが一致しないたびに)何もしないと判断する.sthKey=oldValueは同じ値を繰り返し割り当て、応答式がトリガーされない理由です.
7、次にユーザがカスタマイズするsetterがあるか否かを判断し、あればsetterとする.call()は、ない場合は通常の付与操作です.
8、ここでは、新しいvalueを再び深さ遍歴して、サブ要素ごとに観察者を登録するのを見ました.
9、最後にdep.notify()です.この関数はdataのデータsetの後、観察者がmodel to viewを完了する応答式操作を更新すると購読者に伝えます.
10、ここまでdataのkeyごとのget、set属性の書き換えが終わり、双方向バインドも半分完了した.
11、配列であれば、vueはどのように応答しているのでしょうか.まずhasProtoこれはブール型だと判断しました.それは戻りました.proto__オブジェクトにあるかどうか
ここでaugmentはhasProtoに基づいて上の2つの異なる関数を割り当てます.protoAugemntは簡単にvalueの__をproto__arrayMethods(arrayMethodsはArray.prototype)、copyAugmentはarrayKeys(arrayKeysはarrayMethodsのOwnPropertyName)の各keyを巡回し、def関数を呼び出し、dataのkey属性(つまりArray.prototypeを定義する)を定義します.
次に、配列全体を巡り、サブ要素ごとにobserve関数を呼び出します.
observe関数の実装を見てみましょう.
1、まず関数はvalueが対象であってVNodeノードではないと判断した
2、Observerオブジェクトを初期化し、valueに__が含まれているかどうかを判断するob__属性であり、この属性はObserverオブジェクトであり、いずれも満たされている場合、初期化されたObserverオブジェクトはvalueの_を指します.ob__プロパティ、次にshouldObserve(定数デフォルトはtrue)、サービス側レンダリング、valueが配列またはオブジェクト、valueオブジェクトが拡張可能かどうか(isExtensible es 6構文)、vueインスタンスかどうかを判断し、以上の判断を経てobは新しいObserverオブジェクト(valueによって初期化)を指す
3、現在のasRootDataの現在のvalueがルートノードのdataであるかどうか、およびobオブジェクトが存在するかどうかを判断すると、obはこのオブザーバーのノードに1を加え、最終的にobオブジェクトに戻る.
以上が筆者の理解であり,次にvueの各モジュールをできるだけ遍歴し,何か提案があれば提出を歓迎する.