Vue の応答性の原則は何ですか?

醉折花枝作酒筹
リリース: 2021-07-29 18:08:24
転載
2837 人が閲覧しました

最近、Vue の原則に関する記事をたくさん読みました。これらの記事を参考に、自分自身で Vue のソース コードを理解しようと何度も試みました。いよいよ私自身がアウトプットしてみようと思いますが、他の記事とは違った視点で皆さんに Vue を知っていただければと思います。

Vue の応答性の原則は何ですか?

Dep

var Dep = function Dep() {
  this.id = uid++
  this.subs = []
}
ログイン後にコピー

Dep の意味は当然依存関係 (つまり依存、コンピュータ分野の名詞) です。

node.js プログラムを作成するのと同じように、npm ウェアハウスの依存関係がよく使用されます。 Vue では、依存関係は特にリアクティブに処理されるデータを指します。後で説明するように、リアクティブ処理の重要な機能の 1 つは、多くの Vue 原則に関する記事で言及されている defineReactive です。

Dep が各応答データにバインドされると、応答データは依存関係 (名詞) になります。以下で Watcher を紹介するときに、応答データが監視、計算、またはテンプレートで使用される可能性があることに言及します。依存(動詞)の 3 つのケース。

#subs

Dep オブジェクトの下に配列である subs 属性がありますが、これが購読者リストを意味していることは容易に推測できます。サブスクライバは、監視関数、計算関数、またはビュー更新関数の場合があります。

Watcher

Watcher は、Dep で言及されているサブスクライバです (後の Observer オブザーバと混同しないでください)。

Watcher の機能は、一部のアプリの購読プッシュと同じように、Dep の更新にタイムリーに応答することであるため、あなた (Watcher) が特定の情報 (Dep) を購読すると、次の情報を読むよう通知されます。情報が更新されたとき。

deps

Dep が subs 属性を持つのと同様に、Watcher オブジェクトにも deps 属性があります。これは、Watcher と Dep の間に多対多の関係を構成します。相互に記録する理由は、一方のパーティがクリアされると、関連するオブジェクトを適時に更新できるためです。

Watcher の生成方法

上で何度も説明した監視テンプレート、計算テンプレート、レンダリング テンプレートによって Watcher が生成されます。これらはすべて Vue ソース コードで簡潔で理解しやすいものです。 :

  • mountComponent の vm._watcher = new Watcher(vm, updateComponent, noop);
  • initComputed の watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions )
  • $watcher's var watcher = new Watcher(vm, expOrFn, cb, options);

Observer

Observer はオブザーバーであり、再帰的に実行する責任があります。リアクティブなオブジェクト (または配列) を観察 (または処理) します。印刷された例では、反応性オブジェクトに __ob__ が付いていることがわかります。これは、観察されたことの証拠です。オブザーバーは上記の Dep や Watcher ほど重要ではなく、少し理解するだけで十分です。

walk

Observer.prototype.walk は Observer の初期化時の再帰処理の中核となるメソッドですが、このメソッドはオブジェクトの処理に使用され、Observer もあります。 prototype.observeArray 配列を処理します。

コアプロセス

上記の概念間の関係に従って、それらをどのように一致させ、応答性の高いデータ更新を実現するか?

最初に目標を設定します。当然、データが更新されると、ビューが自動的に更新されて最新のデータが表示されます。

これは上記の Dep と Watcher の関係で、データは Dep、Watcher はページ レンダリング関数をトリガーします (これが最も重要な Watcher)。

しかし、新たな疑問が生じます。ウォッチャーが自分を依存していることを、デップはどのようにして知るのでしょうか?

Vue は非常に興味深い方法を採用しています:

  • Watcher のコールバック関数を実行する前に、まず現在の Watcher が何であるかを (Dep.target を通じて) 書き留めます

  • 実行中のコールバック関数でレスポンシブ データが使用されている場合、必然的にレスポンシブ データのゲッター関数が呼び出されます。 data 現在の Watcher を書き留めて、Dep と Watcher の間の関係を確立できます。

  • その後、レスポンシブ データが更新されると、必然的にレスポンシブ データの setter 関数が呼び出されます

  • 以前に確立された関係に基づいて、ウォッチャーに対応するコールバック関数をセッター関数でトリガーできます

  • コード

  • 上記のロジックはdefineReactive関数の途中にあります。この関数には多くの入り口がありますが、最初に、より重要な観察関数について説明しましょう。

observe 関数では、新しい Observer オブジェクトが作成されます。このオブジェクトでは、Observer.prototype.walk を使用してオブジェクト内の値を 1 つずつ応答的に処理し、defineReactive 関数は使用済み。

defineReactive 関数は非常に重要で長くないため、ここに直接投稿する方が便利です。

function defineReactive(obj, key, val, customSetter, shallow) {
  var dep = new Dep()
  depsArray.push({ dep, obj, key })
  var property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  var getter = property && property.get
  var setter = property && property.set

  var childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      var 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) {
      var value = getter ? getter.call(obj) : val
      // 后半部分诡异的条件是用于判断新旧值都是 NaN 的情况
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      // customSetter 用于提醒你设置的值可能存在问题
      if ('development' !== 'production' && customSetter) {
        customSetter()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    },
  })
}
ログイン後にコピー

まず第一に、各応答値は「依存関係」であるため、最初のステップでは、クロージャーの機能を使用して各値の Dep を作成します。 (Vue 3 では、クロージャは必要ありません)

次に、3 つのコア パラメータを見てみましょう:

obj 現在、値を応答的に処理する必要があるオブジェクト

  • key value key

  • val current value

  • この値は、独自の値より前に定義されている可能性があります。 getter と setter が存在するため、Vue の応答処理を実行するときは、元の getter と setter が最初に処理されます。

  • コアプロセスで前述したように、ゲッター関数は、特に dep.depend() に依存して、Dep と Watcher の間の関係を確立します。

以下に、Dep と Watcher が相互に呼び出すためのいくつかのメソッドを示します。

Dep.prototype.depend = function depend() {
  if (Dep.target) {
    Dep.target.addDep(this)
  }
}
Watcher.prototype.addDep = function addDep(dep) {
  var id = dep.id
  if (!this.newDepIds.has(id)) {
    this.newDepIds.add(id)
    this.newDeps.push(dep)
    if (!this.depIds.has(id)) {
      dep.addSub(this)
    }
  }
}
Dep.prototype.addSub = function addSub(sub) {
  this.subs.push(sub)
}
ログイン後にコピー

通过这几个函数,可以领略到了 Dep 和 Watcher 错综复杂的关系……不过看起来迂回,简单来说,其实做的就是上面说的互相添加到多对多列表。

你可以在 Dep 的 subs 找到所有订阅同一个 Dep 的 Watcher,也可以在 Watcher 的 deps 找到所有该 Watcher 订阅的所有 Dep。

但是里面还有一个隐藏问题,就是 Dep.target 怎么来呢?先放一放,后会作出解答。先接着看看 setter 函数,其中的关键是 dep.notify()。

Dep.prototype.notify = function notify() {
  // stabilize the subscriber list first
  var subs = this.subs.slice()
  for (var i = 0, l = subs.length; i < l; i++) {
    subs[i].update()
  }
}
ログイン後にコピー

不难理解,就是 Dep 提醒他的订阅者列表(subs)里的所有人更新,所谓订阅者都是 Watcher,subs[i].update() 调用的也就是 Watcher.prototype.update。

那么来看一下 Watcher 的 update 做了什么——

Watcher.prototype.update = function update() {
  if (this.lazy) {
    this.dirty = true
  } else if (this.sync) {
    this.run()
  } else {
    queueWatcher(this)
  }
}
ログイン後にコピー

在这里我觉得有两个点比较值得展开,所以挖点坑

以上がVue の応答性の原則は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!