ホームページ  >  記事  >  ウェブフロントエンド  >  Observer実装プロセスのVueソースコード分析

Observer実装プロセスのVueソースコード分析

亚连
亚连オリジナル
2018-05-28 13:55:291386ブラウズ

この記事では、Vue のソースコード解析の Observer 実装プロセスを主に紹介します。Observer の主な機能は、依存関係収集のプロセスであるタッチ - データ (ゲッター) - 依存関係として収集することです。バー

はじめに:

この記事は、Vue 公式ドキュメント (https://cn.vuejs.org/v2/guide/reactivity.html) のリアクティブ原則と、ソースコードの復元。

レスポンシブの原則は、コレクションに依存するプロセスと、トリガーと再レンダリングのプロセスの 2 つのステップに分けることができます。依存関係収集プロセスには、Watcher、Dep、Observer という 3 つの非常に重要なクラスがあります。この記事では主にObserverについて解説します。

この記事では、前の記事で説明できなかったオブザーバー部分の内容について説明します。まず、公式 Web サイトのこの図を見てみましょう:

オブザーバーの主な機能は、touch -Data( getter) - 上の図のCollectedの処理が依存関係の収集の処理です。

それを整理するために、次のコードを例に挙げてみましょう:

(注: 完全なコードを表示するには、左右にスワイプします。以下同様)

varvm = newVue({
el: '#demo',
data: {
firstName: 'Hello',
fullName: ''
},
watch: {
firstName(val) {
this.fullName = val + 'TalkingData';
},
}
})

ソース コードでは、インスタンス化のプロセスがVue を復元すると最初から開始されます。Observer クラスのソース コードは次のとおりです (この記事で説明されていない多くのコードは省略されています):

// src/core/instance/index.js
functionVue(options) {
if(process.env.NODE_ENV !== 'production'&&
!(thisinstanceofVue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
// src/core/instance/init.js
Vue.prototype._init = function(options?: Object) {
constvm: Component = this
// ...
initState(vm)
// ...
}
// src/core/instance/state.js
exportfunctioninitState(vm: Component) {
// ...
constopts = vm.$options
if(opts.data) {
initData(vm)
}
// ...
}
functioninitData(vm: Component) {
letdata = vm.$options.data
data = vm._data = typeofdata === 'function'
? getData(data, vm)
: data || {}
// ...
// observe data
observe(data, true/* asRootData */)
}

initData メソッドでは、データ項目内のデータの「観察」が開始され、すべてのデータが観察可能になります。次に、observer メソッドのコードを見てみましょう。

// src/core/observer/index.js
functionobserve(value: any, asRootData: ?boolean): Observer| void{
// 如果不是对象,直接返回
if(!isObject(value) || value instanceofVNode) {
return
}
letob: Observer | void
if(hasOwn(value, '__ob__') && value.__ob__ instanceofObserver) {
// 如果有实例则返回实例
ob = value.__ob__
} elseif(
// 确保value是单纯的对象,而不是函数或者是Regexp等情况
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
// 实例化一个 Observer
ob = newObserver(value)
}
if(asRootData && ob) {
ob.vmCount++
}
returnob
}

Observer メソッドの機能は、データの Observer インスタンスを作成し、それを返すことです。データに ob 属性がある場合、それはすでに Observer が存在することを意味します。インスタンスを作成すると、既存のインスタンスが返されます。 Vue の応答データには ob 属性があり、この属性の Observer インスタンスを格納してバインドの繰り返しを防ぎます。新しい Observer(value) のプロセスで何が起こるかを見てみましょう:

exportclassObserver{
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor(value: any) {
this.value = value
this.dep = newDep()
this.vmCount = 0
def(value, '__ob__', this)
if(Array.isArray(value)) {
// ...
this.observeArray(value)
} else{
this.walk(value)
}
}
walk (obj: Object) {
constkeys = Object.keys(obj)
for(leti = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
observeArray (items: Array<any>) {
for(leti = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}

ソース コードから、Observer をインスタンス化するプロセスで 2 つの主な判断が行われることがわかります。配列の場合は、監視する配列内の項目ごとに oberser メソッドを再度呼び出します。配列以外のオブジェクトの場合は、オブジェクトの各属性を走査して、そのオブジェクトの defineReactive メソッドを呼び出します。ここでのdefineReactiveメソッドが核心です。依存関係の収集は、Object.defineProperty メソッドを使用して、監視する必要がある各プロパティに get/set を追加することで完了します。依存関係が収集された後、各プロパティにはすべての Watcher オブジェクトを保存するための Dep が含まれます。記事の冒頭の例によると、firstName と fullName に get/set がそれぞれ追加され、それぞれに監視するすべての Watcher オブジェクトを保存する Dep インスタンスがあります。以下は、defineReactive のソース コードです。

exportfunctiondefineReactive(
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
constdep = newDep()
// 获取属性的自身描述符
constproperty = Object.getOwnPropertyDeor(obj, key)
if(property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// 检查属性之前是否设置了 getter/setter
// 如果设置了,则在之后的 get/set 方法中执行设置了的 getter/setter
constgetter = property && property.get
constsetter = property && property.set
// 通过对属性再次调用 observe 方法来判断是否有子对象
// 如果有子对象,对子对象也进行依赖搜集
letchildOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: functionreactiveGetter() {
// 如果属性原本拥有getter方法则执行
constvalue = getter ? getter.call(obj) : val
if(Dep.target) {
// 进行依赖收集
dep.depend()
if(childOb) {
// 如果有子对象,对子对象也进行依赖搜集
childOb.dep.depend()
// 如果属性是数组,则对每一个项都进行依赖收集
// 如果某一项还是数组,则递归
if(Array.isArray(value)) {
dependArray(value)
}
}
}
returnvalue
},
set: functionreactiveSetter(newVal) {
// 如果属性原本拥有getter方法则执行
// 通过getter方法获取当前值,与新值进行比较
// 如果新旧值一样则不需要执行下面的操作
constvalue = 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 !== &#39;production&#39;&& customSetter) {
customSetter()
}
if(setter) {
// 如果属性原本拥有setter方法则执行
setter.call(obj, newVal)
} else{
// 如果原本没有setter则直接赋新值
val = newVal
}
// 判断新的值是否有子对象,有的话继续观察子对象
childOb = !shallow && observe(newVal)
// 通知所有的观察者,更新状态
dep.notify()
}
})
}

ソース コード内の中国語のコメントによれば、defineReactive の実行中にどのような作業が行われるかを理解できるはずです。実際、プロセス全体は再帰的であり、各プロパティにゲッター/セッターを追加します。ゲッター/セッターの場合も、オブザーバー パターンを完成させるために、プロパティごとに再帰 (サブオブジェクトを判断) する必要があります。 getter の場合、依存関係の収集、つまりソース コード内の dep.depend() を完了するために使用されます。セッターの場合、データの一部がその set メソッドをトリガーすると、更新メッセージが発行され、このデータのすべてのオブザーバーにデータが変更されようとしていることが通知されます。それがソースコードのdep.notify()です。

上記は私があなたのためにまとめたものです。

関連記事:

vue2.0 アセットファイルと静的ファイルの違いの詳細説明

Angular 5.x 学習ノート - Router アプリケーション

React Router v4 エントリーガイド

以上がObserver実装プロセスのVueソースコード分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。