Cette fois, je vais vous montrer comment utiliser Vue pour implémenter Observer, et quelles sont les précautions pour utiliser Vue pour implémenter Observer. Voici un cas pratique, jetons un coup d'œil.
Introduction :
Cet article est une revue approfondie de la documentation officielle de Vue sur les principes réactifs (https://cn.vuejs. org/v2/guide /reactivity.html) et restaurez 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 et 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, passez Restaurer le processus d'instanciation de Vue, étape par étape depuis le début jusqu'au code source de la classe Observer (beaucoup de codes qui ne sont pas abordés dans cet article sont 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, les données sont traitées. "Observer" les données de l'élément rendra toutes les données 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 data a l'attribut ob, cela signifie qu'il existe. déjà une instance Observer, alors celle existante est renvoyée. Les données réactives de Vue auront un attribut ob, qui stocke l'instance Observer de l'attribut pour empêcher des liaisons répétées. Regardons ce qui se passe pendant le nouveau processus Observer (valeur) :
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, deux jugements principaux sont effectués lors de l'instanciation d'Observer. 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, parcourt chaque attribut de l'objet et appelle 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 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 !== 'production'&& 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 en mesure 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 de compléter la récursivité (jugement des sous-objets) pour chaque attribut Modèle 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.
Je pense que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web chinois de php !
Lecture recommandée :
Comment utiliser le composant d'arborescence de fichiers vue
Analyse logique sous-jacente JavaScript EventEmitter
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!