English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Vue.js原理分析之observer模块详解

介绍

observer是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭observer的支持。

注意:本文是针对[email protected]进行分析

observer模块在Vue项目中的代码位置是src/core/observer,模块共分为这几个部分:

  1. Observer: 数据的观察者,让数据对象的读写操作都处于自己的监管之下
  2. Watcher: 数据的订阅者,数据的变化会通知到Watcher,然后由Watcher进行相应的操作,例如更新视图
  3. Dep: Observer与Watcher的纽带,当数据变化时,会被Observer观察到,然后由Dep通知到Watcher

示意图如下:

Observer

Observer类定义在src/core/observer/index.js中,先来看一下Observer的构造函数

constructor (value: any) {
 this.value = value
 this.dep = new Dep()
 this.vmCount = 0
 def(value, '__ob__', this)
 if (Array.isArray(value)) {
 const augment = hasProto
 ? protoAugment
 : copyAugment
 augment(value, arrayMethods, arrayKeys)
 this.observeArray(value)
 }
 this.walk(value)
 }
}

value是需要被观察的数据对象,在构造函数中,会给value增加__ob__属性,作为数据已经被Observer观察的标志。如果value是数组,就使用observeArray遍历value,对value中每一个元素调用observe分别进行观察。如果value是对象,则使用walk遍历value上每个key,对每个key调用defineReactive来获得该key的set/get控制权。

解释下上面用到的几个函数的功能:

  • observeArray: 遍历数组,对数组的每个元素调用observe
  • observe: verifica se l'oggetto ha l'attributo __ob__, se esiste, significa che l'oggetto è già sotto l'osservazione dell'Observer, se non esiste, new Observer per osservare l'oggetto (ci sono anche alcune logiche di giudizio, che non vengono menzionate per facilitare la comprensione)
  • walk: esplora ogni key dell'oggetto, chiama defineReactive per ogni key dei dati dell'oggetto
  • defineReactive: utilizza Object.defineProperty per impostare l'attributo key dell'oggetto, in modo che sia possibile catturare le azioni set/get di questo attributo. Di solito è l'istanza dell'oggetto Watcher che esegue l'operazione get, in questo caso l'istanza dell'oggetto Watcher viene automaticamente aggiunta all'array di dipendenze dell'istanza di Dep, quando viene attivato l'operazione set dall'esterno, viene notificato a tutti i watcher dipendenti attraverso l'istanza di Dep notify

Se non si capisce bene la descrizione testuale, si può guardare la figura di seguito:

Dep

Dep è il legame tra Observer e Watcher, può anche essere considerato un sistema di iscrizione a servizio di Observer. Watcher si iscrive a un Dep dell'Observer, quando i dati osservati dall'Observer cambiano, attraverso Dep notifica a tutti i watcher iscritti.

Dep fornisce alcune interfacce:

  • addSub: accetta come parametro l'istanza di Watcher e inserisce l'istanza di Watcher nell'array che registra le dipendenze
  • removeSub: corrisponde a addSub, ha la funzione di rimuovere l'istanza di Watcher dall'array che registra le dipendenze
  • depend: su Dep.target viene memorizzato l'istanza di Watcher attualmente in esecuzione, la chiamata a depend chiama il metodo addDep dell'istanza di Watcher, la funzione di addDep può essere visualizzata nella descrizione di Watcher di seguito
  • notify: notifica a tutti i watcher nell'array di dipendenze di eseguire l'operazione di aggiornamento

Watcher

Watcher è utilizzato per iscriversi ai cambiamenti dei dati e eseguire le operazioni corrispondenti (ad esempio, aggiornare le viste). La funzione costruttore di Watcher è definita come segue:

constructor (vm, expOrFn, cb, options) {
 this.vm = vm
 vm._watchers.push(this)
 // options
 if (options) {
 this.deep = !!options.deep
 this.user = !!options.user
 this.lazy = !!options.lazy
 this.sync = !!options.sync
 }
 this.deep = this.user = this.lazy = this.sync = false
 }
 this.cb = cb
 this.id = ++uid // uid per batching
 this.active = true
 this.dirty = this.lazy // per watcher lazy
 this.deps = []
 this.newDeps = []
 this.depIds = new Set()
 this.newDepIds = new Set()
 this.expression = process.env.NODE_ENV !== 'production'
 ? expOrFn.toString()
 : ''
 if (typeof expOrFn === 'function') {
 this.getter = expOrFn
 }
 this.getter = parsePath(expOrFn)
 if (!this.getter) {
 this.getter = function () {}
 process.env.NODE_ENV !== 'production' && warn(
 `Non è possibile osservare il percorso: "${expOrFn}" ` +
 'Watcher accetta solo percorsi semplici delimitati da punti. ' +
 'Per un controllo completo, utilizzare una funzione invece.'
 vm
 )
 }
 }
 this.value = this.lazy
 ? undefined
 : this.get()
}

Nei parametri, vm rappresenta l'istanza del componente, expOrFn rappresenta il campo di dati da iscrivere (una stringa che rappresenta, ad esempio, a.b.c) o una funzione da eseguire, cb rappresenta la funzione di callback eseguita dopo l'esecuzione del watcher, options è un oggetto di opzioni che contiene configurazioni come deep, user, lazy.

L'istanza watcher ha questi metodi:

  • set: Imposta Dep.target sull'istanza corrente di watcher, chiama internamente this.getter, se in questo momento un oggetto osservato da Observer viene valutato, l'istanza corrente di watcher si iscriverà automaticamente all'istanza Dep dell'oggetto.
  • addDep: accettare il parametro dep (istanza Dep), far iscrivere il watcher corrente a dep
  • cleanupDeps: cancellare le informazioni di iscrizione registrate su newDepIds e newDep per dep
  • update: eseguire immediatamente il watcher o aggiungere il watcher alla coda per eseguire il flush in un unico momento
  • run: eseguire il watcher, chiamare this.get() per calcolare il valore e poi scatenare il callback
  • evaluate: chiamare this.get() per calcolare il valore
  • depend: esplorare this.deps, far iscrivere all'istanza watcher corrente tutti i dep
  • teardown: rimuovere tutte le iscrizioni dell'istanza watcher corrente

Metodi Array

Nel file src/core/observer/array.js, la struttura di Vue ha apportato modifiche ai metodi push, pop, shift, unshift, sort, splice, reverse degli array, automaticamente chiamando dep.notify() quando si chiamano questi metodi sugli array, risolvendo il problema di non poter scatenare l'aggiornamento dopo aver chiamato queste funzioni per modificare l'array.

Nel documento ufficiale di Vue è anche spiegato: http://cn.vuejs.org/v2/guide/list.html#metodi-di-modificazione

Sommario

Questo è tutto il contenuto dell'articolo, speriamo che il contenuto di questo articolo possa essere di aiuto a tutti per lo studio o il lavoro, se hai domande puoi lasciare un messaggio per l'interazione.

Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il diritto d'autore è dell'autore originale, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, il sito web non detiene i diritti di proprietà, non è stato elaborato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una e-mail a notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, il sito web eliminerà immediatamente il contenuto sospetto di violazione del copyright.