Maison  >  Article  >  interface Web  >  Analyse du code source Vue du processus de mise en œuvre d'Observer

Analyse du code source Vue du processus de mise en œuvre d'Observer

亚连
亚连original
2018-05-28 13:55:291386parcourir

Cet article présente principalement le processus d'implémentation d'Observer pour l'analyse du code source de Vue. La fonction principale d'Observer est d'implémenter le processus de touch -Data (getter) - Collect as Dependency, qui est le processus de collecte de dépendances. sont intéressés Apprenons ensemble

Introduction :

Cet article est un principe réactif approfondi de la documentation officielle de Vue (https://cn.vuejs.org/v2 /guide/reactivity. html) et restaurer le processus de mise en œuvre via le code source.

Le principe réactif peut être divisé en deux étapes, le processus consistant à s'appuyer sur la collecte et le processus de déclenchement et de re-rendu. Il existe trois classes très importantes dans le processus de collecte des dépendances, à savoir Watcher, Dep et Observer. Cet article explique principalement Observer.

Cet article explique le contenu d'Observer qui n'a pas été couvert dans l'article précédent. Regardons d'abord cette photo du site officiel :

Le principal. fonction d'Observer Il réalise le processus de touch -Data(getter) - Collect as Dependency dans l'image ci-dessus, qui est le processus de collecte de dépendances.

Prenons le code suivant comme exemple pour faire le tri :

(Remarque : balayez vers la gauche ou la droite pour afficher le code complet, le même ci-dessous)

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

Dans le code source, le processus d'instanciation par restauration de Vue, étape par étape depuis le début jusqu'au code source de la classe Observer est le suivant (beaucoup de code qui est non abordé dans cet article est omis) :

// 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 */)
}

Dans la méthode initData, "l'observation" des données dans l'élément de données commence, et tout les données seront rendues observables. Ensuite, regardez le code de la méthode 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
}

La fonction de la méthode observer est de créer une instance Observer pour les données et de la renvoyer. Si les données ont l'attribut ob, s'il existe déjà une instance Observer, renvoyez l'instance existante. Les données réactives de Vue auront un attribut ob, qui stocke l'instance Observer de cet attribut pour empêcher des liaisons répétées. Regardons ce qui se passe dans le nouveau processus 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])
}
}
}

Comme vous pouvez le voir dans le code source, il y a deux choses principales à faire dans le processus d’instanciation d’un jugement par l’Observateur. S'il s'agit d'un tableau, appelez à nouveau la méthode oberser pour chaque élément du tableau à observer ; s'il s'agit d'un objet non-tableau, parcourez chaque attribut de l'objet et appelez la méthode DefinReactive sur celui-ci. La méthode DefineReactive est ici le noyau ! La collection de dépendances est complétée à l'aide de la méthode Object.defineProperty pour ajouter get/set à chaque propriété qui doit être observée. Une fois les dépendances collectées, chaque propriété aura un Dep pour enregistrer tous les objets Watcher. Selon l'exemple au début de l'article, get/set est ajouté respectivement à firstName et fullName, et chacun d'eux dispose d'une instance Dep pour enregistrer tous les objets Watcher qui les observent. Voici le code source de definitionReactive :

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()
}
})
}

D'après les commentaires chinois dans le code source, vous devriez être capable de comprendre quel travail est effectué lors de l’exécution de DefineReactive. En fait, l'ensemble du processus est récursif, ajoutant des getters/setters pour chaque propriété. Pour les getters/setters, il est également nécessaire d'effectuer une récursion (juger les sous-objets) pour chaque propriété afin de compléter le modèle d'observateur. Pour getter, il est utilisé pour compléter la collection de dépendances, c'est-à-dire dep.depend() dans le code source. Pour les setters, une fois qu'une donnée déclenche sa méthode set, un message de mise à jour sera publié, informant tous les observateurs des données qu'elles changeront également. C'est dep.notify() dans le code source.

Ce qui précède est ce que j'ai compilé pour vous. J'espère que cela vous sera utile à l'avenir.

Articles connexes :

Explication détaillée de la différence entre les actifs des fichiers de ressources vue2.0 et les fichiers statiques

Angular 5. x notes d'étude Application de routeur (routage)

Guide de la fosse React Router v4

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn