¿Cuáles son los métodos para crear observadores en Vue?
Introducción:
Este artículo comprende en profundidad los principios de respuesta de los documentos oficiales de Vue (.vuejs.org/v2/guide/reactivity.html) y restaura la Proceso de implementación a través del código fuente.
El principio de respuesta se puede dividir en dos pasos, a saber, el proceso de recopilación de dependencias y el proceso de generación de activadores. En el proceso de recopilación de dependencias, hay tres clases muy importantes: Watcher, Dep y Observer. Este artículo explica principalmente Observer.
Este artículo explica parte del contenido de Observer que no se cubrió en el artículo anterior. Primero echemos un vistazo a esta imagen en el sitio web oficial:
La función principal de Observer es. para implementar El proceso de tocar -Datos (obtener) - Recopilar como dependencia en la figura es el proceso de recopilación de dependencias.
O tome el siguiente código como ejemplo para organizar:
(Nota: deslice hacia la izquierda y hacia la derecha para ver el código completo, lo mismo a continuación)
varvm = newVue({
el: '#demo',
datos: {
primer nombre: Hola',
nombre completo:'
},
ver: {
firstName(val) {
this.fullName = val + 'TalkingData'; p>
},
}
}) en el código fuente.
Al restaurar el proceso de creación de instancias de Vue, el código fuente se restaura a la clase de observador paso a paso desde el principio, en orden (se omite una gran cantidad de código que no se analiza en esta publicación de blog):
/ / src/ core/instance/index.js
functionVue(opciones) {
if (process.env.NODE_ENV ! == 'producción'&&
!(thisinstanceofVue )
){
warn('Vue es un constructor y debe llamarse usando la palabra clave `new`')
}
this._init(opciones)
}
// src/core/instance/init.js
Vue.prototype._init = función (¿opciones?: Objeto) {
constvm: Componente = this
// ...
initState(vm)
/ / .. .
}
// src/core/instance/state.js
exportfunctioninitState(vm: Componente) {
// ...
constopts = vm.$opciones
if(opts.data) {
initData(vm)
}
// ...
}
functioninitData(vm: Componente) {
letdata = vm.$options. datos p>
datos = vm._data = typeofdata === 'función' getData(datos, vm)
: datos {}
// . ..
// Observar datos
observe(data, true/* asRootData */)
}En el método initData, comienza a "observar" los elementos de datos. Datos, haciendo que todos los datos en un elemento de datos sean observables. A continuación, mire el código del método de observación:
// src/core/observer/index.js
functionobserve(value: any, asRootData: ?boolean): observador |void {
//Si no es un objeto, devuelve el valor directamente
//. Si no es un objeto, regresa directamente
if(!isObject(value) ||valuestanceofVNode) {
return
}
letob:Observer | void
if(hasOwn(value , '__ob__') && value.__ob__stanceofObserver) {
// Si hay una instancia, devuelve la instancia
ob = value.__ob__
} elseif(
//// Asegúrese de que el valor sea solo un objeto, no una función o expresión regular, etc.
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(valor) || isPlainObject(valor)) &&
Object.isExtensible(valor) &&
!value._isVue
){p>ob = newObserver(valor)
}
if(asRootData && ob) {
ob.vmCount++
}
returnob
}El propósito del método de observación es para proporcionar datos Cree una instancia de observador, cree una instancia de observador y devuélvala. Si los datos tienen un atributo ob, significa que ya existe una instancia de observador, por lo que devolverá la instancia de observador existente; Veamos qué sucede durante el nuevo Observador(valor):
exportclassObserver{
valor: cualquiera;
dep: dep;
vmCount : número; // El número de máquinas virtuales enraizadas con este objeto $data
constructor(valor: cualquiera) {
this.value = value
this. dep = newDep()
this.vmCount = 0
def(valor, '__ob__', esto)
if(Array. isArray(valor)) {
//...
this.observeArray(valor)
} else{
esto.walk(valor)
}
}
}
caminar (obj: Objeto) {
constkeys = Objeto .keys( obj)
for(leti = 0; i < claves.length; i++) {
defineReactive(obj, claves[i], obj[claves[i ]]) p>
}
}
}
}
observeArray (elementos: Array
for(leti = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
}
}
}Como se puede ver en el código fuente, en el ejemplo hay dos Principales juicios en el proceso de conversión de Observer. Si es un objeto de matriz, se vuelve a llamar al método observador para cada elemento de la matriz; si es un objeto que no es de matriz, se itera sobre cada propiedad del objeto y se llama al método defineReactive. ¡El método defineReactive es el corazón de esto! La recopilación de dependencias se realiza agregando get/set usando el método Object.defineProperty para cada propiedad que deba observarse. Una vez recopiladas las dependencias, cada propiedad tendrá un Dep para contener todos los objetos de observador.
En el ejemplo al principio del artículo, agregamos get/set para firstName y fullName, y cada propiedad tiene una instancia Dep para contener todos los objetos observadores. El siguiente es el código fuente de defineReactive:
exportfunctiondefineReactive(
obj: Object,
key: string,
val: any ,
customSetter?: ?Función,
¿superficial?: Valor booleano
)configurable === false) {
retorno
}
/// Cumplir con los requisitos de getters/setters predefinidos
//Compruebe si la propiedad tiene un getter/setter previamente configurado
//Si está configurado, ejecute el set getter/setter en los métodos get/set posteriores
constgetter = property && property.get
/ // Si la propiedad tiene un getter/ setter configurado previamente, ejecute set getter/setter en métodos get/setter posteriores.
get
Constgetter = propiedad &&. property.get
constsetter = propiedad && property.set
// Determine si existe una propiedad llamando al método observe de la propiedad nuevamente Nodos secundarios
//Si hay nodos secundarios, también recopile las dependencias de los nodos secundarios
letchildOb = !shallow && observe(val)
Object.defineProperty (obj, clave, {
enumerable: verdadero,
configurable: verdadero,
obtener: functionreactiveGetter() {
// Si el atributo originalmente tiene un método getter, ejecútelo
constvalue = getter ?call(obj) : val
if(Dep.target) {
// ejecutar colección de dependencias
dep.depend()
if(childOb) {
// Si el objeto secundario tiene objetos secundarios, también realiza dependencias en la colección de objetos secundarios
childOb.dep.depend()
//Si el atributo es una matriz, recopila dependencias para cada elemento
// Si Cada elemento sigue siendo una matriz, entonces recursividad
if(Array.isArray(value)) {
dependArray(value)<
}
}
}
}
valor de retorno
},
establecer: functionreactiveSetter(newVal) {
// Si el atributo originalmente tiene un método getter, ejecútelo
Obtenga el valor actual a través del método getter y compárelo con el nuevo valor
. p>
// Si los valores antiguos y nuevos son iguales, no es necesario realizar las siguientes operaciones
constvalue = getter ? getter.call(obj): val? p>
/* eslint-disable no-self-compare */
if(newVal === valor || (newVal !== newVal && valor ! == valor)) { p>
return
}
/* eslint-enable no-self-compare */
if(process.env.NODE_ENV! == 'producción'&& customSetter) {
customSetter()
}
if(setter) {
// if atributo Si originalmente hay un método de establecimiento, ejecute el método
setter.call(obj, newVal)
} else{
// Si la propiedad no tiene un setter originalmente, luego simplemente asigne un valor
val = newVal
}
// Determine si el nuevo valor tiene hijos, si es así, continúe observando los niños
childOb = ! superficial && observe(newVal)
// Notificar a todos los observadores del estado actualizado
dep.notify
()
}
})
}} Creo que a través de los casos de este artículo, dominas el método. Para obtener más información, sigue. ¡Gxl .net!
Lectura recomendada:
Cómo optimizar el empaquetado de webpack 4.0
Realizar conversión json y mayúsculas y minúsculas de palabras clave de matriz