Cómo entender el principio de enlace bidireccional de datos vue
Curso recomendado: Vue. Tutorial
p>Patrón MVC
El patrón MVC anterior era vinculante unidireccional, es decir, el modelo estaba vinculado a la Vista. Cuando usamos código JavaScript para actualizar el modelo, la Vista lo hará automáticamente. actualizar
Patrón MVVM
El modo MVVM es el modo Model_View_ViewModel. Permite que los cambios en la Vista se reflejen automáticamente en ViewModel y viceversa. La comprensión del enlace bidireccional es que cuando el usuario actualiza la Vista, los datos del Modelo también se actualizarán automáticamente. Esta situación es un enlace bidireccional. Más detalladamente, se basa en un enlace unidireccional, agregando eventos de cambio (entrada) a elementos de entrada como entrada y texto (activando eventos de cambio y actualizando el estado de la Vista), modificando así dinámicamente el modelo.
El principio del enlace bidireccional
El enlace bidireccional de los datos de Vue se logra mediante el secuestro de datos combinado con el modelo editor-suscriptor
Ya Ya sabes, para implementar el enlace de datos bidireccional, primero debemos escuchar el secuestro de datos, por lo que debemos configurar un observador para escuchar todas las propiedades. Si hay un cambio en el atributo, debe informar al suscriptor y al observador si es necesario actualizarlo. Dado que hay muchos suscriptores, necesitamos un departamento de suscriptores de mensajes para recopilar estos suscriptores y luego administrarlos de manera uniforme entre los observadores oyentes y los observadores suscriptores. A continuación, también necesitamos un analizador de comandos Compile para escanear y analizar cada elemento del nodo, inicializar los comandos relevantes correspondientes al observador del suscriptor (como v-model, v-on) y reemplazar los datos de la plantilla o vincular la función correspondiente. el observador suscriptor recibe el cambio de propiedad correspondiente, ejecutará la función de actualización correspondiente para actualizar la vista.
Por lo tanto, realizamos los siguientes 3 pasos para implementar el enlace de datos bidireccional:
(1) Implementar un observador de escucha para secuestrar y escuchar todas las propiedades y, cuando esté disponible, notificar a los suscriptores. de cualquier cambio.
(2) Implementar observadores de suscriptores. Cada observador está vinculado a una función de actualización. El observador puede recibir notificaciones de cambios de propiedad y ejecutar las funciones correspondientes para actualizar la vista.
(3) Implemente un compilador analizador que escaneará y analizará las instrucciones relevantes de cada nodo (v-model, v-on, etc. si el nodo existe v-model, v -on y). Otras instrucciones, el compilador del analizador inicializará los datos de la plantilla de dichos nodos para que puedan mostrarse en la vista y luego inicializará los suscriptores correspondientes (Watcher).
Implementación del observador
El observador es una especie de oyente de datos y su método de implementación principal es Object.defineProperty(). Si desea escuchar todas las propiedades, puede utilizar un método recursivo para recorrer todos los valores de propiedad y realizar Object.defineProperty() en ellos
El siguiente código implementa un observador.
función Observador(datos) { this.data = datos; this.walk(datos);
}
Observador.prototype = { caminar: función( data) {
var self = this; //Aquí es donde escuchas todos los atributos de un objeto al atravesarlo
Object. Object.keys(data).forEach(function(key) {
self.defineReactive(data, key, data[key]);
});
}, defineReactive.function(data, key, val) {
var dep = new Dep(); // Recorre recursivamente todas las propiedades secundarias
var childObj = observe(val)
Object.defineProperty(datos, clave, {
enumerable: verdadero,
configurable: verdadero,
obtener: función); getter () {
if (Dep.target) {
//Agregar suscriptores aquí
console.log(Dep.target) p>
dep.addSub(Dep.target);
} return val;
},
// establecedor, cuando el valor del atributo del objeto Cuando se cambia ocurre, se activa dep.notify() en el configurador,
notifica al observador (suscriptor) del cambio de datos y ejecuta la función de actualización del suscriptor correspondiente para actualizar la vista
<. p> set: establecimiento de función (newVal) {if (newVal === val) {
return; > val = newVal ;
// Escucha si el nuevo valor es un objeto
Notifica a los observadores de los cambios.
childObj = observar(newVal)
dep.notify()
}
}
});
}
}; función observar(valor, vm) { if (!valor || tipo de valor !== 'objeto') {
return;
} return new Observer(value);
}; // El departamento de suscriptores de mensajes es el principal responsable de recopilar suscriptores y luego ejecuta la comunicación con los suscriptores cuando las propiedades cambian. función
función Dep () {
this.subs = []
}
Dep.prototype = { /**;
* [Suscriptor agrega suscriptor]
* @param {[Watcher]} sub [suscriptor]
*/
addSub: function(sub) {
this.subs.push(sub);
}, // Notificar al suscriptor de los cambios en los datos
notificar: function() {
this.subs.forEach(función (sub) {
sub.update();
});
}
};
Dep.target = null; En el observador, cuando miré por primera vez el código fuente de otras personas, había una cosa que no entendía: ¿Dónde está Dep. ¿De dónde viene el objetivo? Creo que algunas personas tienen la misma pregunta que yo. No hay necesidad de apresurarse aquí, cuando se trata de observadores, se dará cuenta de que Dep.target proviene de observadores.
Implementación de un observador
Un observador es un suscriptor. Se utiliza para recibir mensajes de actualización de los observadores y procesarlos para ejecutar la función de actualización vinculada por el observador.
ejecutar();
}, ejecutar: función() {
valor var = this.vm.data[this.exp]
var oldVal = este.valor;
if (valor ! == oldVal) {
este.valor = valor
this.cb.call(this.vm, valor, oldVal);
}
}, get: function() {
Dep.target = this // caché mismo
}, get: function() {
Dep.target = this; // caché en sí
}
}, get: function() {
Dep.target = this; // caché en sí
}.target = this; // caché en sí mismo
var value = this.vm.data[ this.exp] // Fuerza la función get en el oyente
Dep.target = null; //Liberarse
valor de retorno;
} p>
}; En el proceso de estudiar el código, descubrí que lo más complicado es comprender los parámetros de estas funciones y luego, después de exportarlos, las funciones de estas funciones son fáciles de entender. vm, que es el objeto SelfValue que se escribirá más adelante, es equivalente a un objeto de nuevo Vue en Vue. exp es una instrucción para el valor del atributo como v-model o v-on:click del nodo nodo.
Como puede ver en el código anterior, en la función getter de Watcher, Dep.target apunta a sí mismo, el objeto Watcher. En la función getter,
var value = this.vm.data[this.exp] // Fuerza la función get en el oyente.
Cuando se obtiene vm.data[this.exp] aquí, se llamará a la función get de Object.defineProperty en el objeto observador
get: function getter () { p >
if (Dep.target) {
// Agregue suscriptores aquí. Agregue suscriptores aquí
console.log(Dep.target)
dep.addSub(Dep.target
}
return); val;
} Esto agrega el observador a los suscriptores y resuelve el problema anterior de dónde viene Dep.target.
Implementar compilación
El objetivo principal de la compilación es obtener el nodo dom vinculado al nuevo SelfVue (es decir, la identificación vinculada a la etiqueta el) y atravesar todos los nodos secundarios. del nodo, para encontrar todas las directivas v y "{{}}" en ellas. ".
(1) Si el nodo secundario contiene una directiva v, es decir, es un nodo de elemento, agregue un evento de escucha para el elemento.
(Si v-on, use node.addEventListener('click'); si v-model, use node.addEventListener('input')). A continuación, inicialice el elemento de plantilla y cree un observador para vincular este nodo de elemento.
(2) Si el nodo secundario es un nodo de texto, es decir, "{{ data }}", use expresiones regulares para eliminar los datos en "{{ data }}", y luego use var initText = this. vm[exp] y reemplace los datos que contiene con initText.
Implementación de MVVM
MVVM puede ser el "jefe" de Observer, Compile y Watcher. Necesita organizar Observer, Compile y Watche para hacer las siguientes cosas.
(1) Observer implementa el secuestro de datos del propio modelo de MVVM, monitorea los cambios en los atributos de los datos y emite notificaciones cuando ocurren cambios
(2) Compile e implemente el análisis de comandos, inicialice vistas, suscríbase a los cambios de datos y funciones de actualización de enlace
(3) Por un lado, el observador recibe los cambios de datos pasados por el observador a través de dep y, por otro lado, notifica al compilador sobre la actualización de la vista.
Finalmente, lo que abstrae este MVVM es el constructor de Vue en vue, que puede construir una instancia de vue.
Por último, escribir un html para probar nuestra funcionalidad
lt;! DOCTYPE htmlgt;lt;html lang="es"gt;lt;headgt;
lt;meta charset="UTF-8"gt;
lt;titlegt;selfvuelt;/ titlegt;lt;/headgt;lt;stylegt;
#app {
text-align: center;
}lt;/stylegt;lt;bodygt ;
lt;div id="app"gt;
lt;h2gt;{{title}}lt;/h2gt;
lt;entrada v -model="nombre"gt;
lt; {{nombre}}lt;
lt; haga clic="clickMe"gt; ¡haz clic en mí! lt;/buttongt;
lt;/divgt;lt;/bodygt;lt;script src="js/observer.js"gt;/scriptgt;
lt;script src ="js/watcher.js"gt; /scriptgt;
lt; script src="js/mvvm.js"gt;lt;/scriptgt;
lt;script type="text/javascript"gt;
var aplicación = new SelfVue({
el: '#app',
data: {
título: 'hola mundo',
nombre: 'canfoo'
},
métodos: {
clickMe: function () {
this.title = 'hola mundo' ; p>
p>
}
},
montado: función () {
ventana.setTimeout(() =gt; {
this.title = 'hola';
}, }
}.1000); p> }); lt;/scriptgt;lt;/htmlgt; Primero ejecute new SelfVue(...) en mvvm. En mvvm.js,
observe(this.data);
new Compile(options.el, this); primero inicializa un observador oyente para escuchar este objeto. El valor de atributo de datos.
Luego inicialice un analizador. Compile, vincule este nodo, analice las instrucciones v-, "{{}}" (cada instrucción corresponde a un observador), inicialice los datos de la plantilla e inicialice el suscriptor correspondiente y agregue el suscriptor a Suscriptores(Dep). Esto logra una unión bidireccional.
Si el elemento vinculado al modelo v (
lt; input v-model="name"gt; es decir, el valor del cuadro de entrada cambia, se activará
node.addEventListener('input', function(e) { <
var newValue = e.target.value;
if ( val === nuevoValue ) {
return;
}
self.mtem.addEventListener('entrada', función(e) { <
var nuevoValor = e.target.value
if (val === nuevoValor) {
retorno;
self.vm[exp] = newValue;
val = newValue.
}); self.vm[exp] = newValue; mvvm y disparadores Observer escucha la propiedad de nombre del objeto, que es el configurador de Object.defineProperty () en Observer. Este configurador tiene una función dep.notify, que se usa para notificar a los suscriptores, y el observador ejecutará la actualización vinculada. funciona después de recibir la notificación
Finalmente, se ve así: