Lectura recomendada:
Implementar el enlace de datos bidireccional JS muy simple
MVVM es un modelo de desarrollo muy popular para el front-end web. El uso de MVVM puede hacer que nuestro código se centre más en tratar con la lógica de negocios en lugar de preocuparse por las operaciones DOM. Actualmente, los famosos marcos MVVM incluyen Vue, Avalon, React, etc. Estos marcos tienen sus propias ventajas, pero la idea de implementación es aproximadamente la misma: enlace de datos + Vista de actualización. Por curiosidad y voluntad de luchar, también escribí la biblioteca MVVM más simple (MVVM.JS) en esta dirección, con un total de más de 2,000 líneas de código. El nombre y el uso de instrucciones son similares a Vue. Aquí compartiré los principios de implementación y mis ideas de organización de código.
Clasificación de ideas
MVVM es conceptualmente un patrón que realmente separa las vistas de la lógica de datos, y ViewModel es el foco de todo el patrón. Para implementar ViewModel, debe asociar el modelo de datos (modelo) y la vista (Vista). Toda la idea de implementación se puede resumir simplemente en 5 puntos:
Implemente un compilador para escanear y extraer instrucciones para cada nodo de un elemento;
Implemente un analizador para analizar las instrucciones en el elemento, que puede actualizar la intención de la instrucción al DOM a través de una función de actualización (un módulo que puede ser específicamente responsable de la vista refrescante puede ser requerida en el medio). Por ejemplo, al analizar el nodo <p v-show = "isshow"> </p>, primero obtenga el valor de ISSHOW en el modelo y luego cambie Node.Style.display de acuerdo con ISSHOW para controlar la pantalla y ocultación de los elementos;
La implementación de un observador puede vincular la función de actualización de cada instrucción en analizador con los campos correspondientes al modelo;
Implemente un observador para monitorear el cambio de valor de todos los campos del objeto, y una vez que ocurre el cambio, se puede obtener el último valor y se puede activar la devolución de llamada de notificación;
Use Observador para establecer un monitoreo del modelo en el observador. Cuando cambia un valor en el modelo, se activa el monitoreo. Después de que el observador obtiene el nuevo valor, llama a la función de actualización asociada en el paso 2, lo que puede lograr el propósito de actualizar la vista mientras cambia los datos.
Ejemplo de efecto
Primero, echemos un vistazo al ejemplo de uso final, que es similar a la instanciación de otros marcos MVVM:
<div id = "mobile-list"> <h1 v-text = "title"> </h1> <ul> <li v-For = "item in Brands"> <b v-text = "item.name"> </b> <span v-show = "showrank"> rango: {{item.rank}} </span> </li> </ul> </div> var elemento = = document.querySelector ('#mobile-list'); var vm = new mvvm (element, {'title': 'mobile list', 'showRank': true, 'brands': [{'name': 'apple', 'rank': 1}, {'name': 'galaxy', ' 3}]}); vm.set ('título', 'Lista de rango móvil 3 Top 3'); // => <h1> Top 3 List de rango móvil </h1>División de módulos
Dividí MVVM en cinco módulos para implementar: compilador de módulos de compilación, analizador, ver el actualizador del módulo de actualización, el observador del módulo de suscripción de datos y el observador del módulo de escucha de datos. El proceso se puede describir brevemente como: después de que el compilador compila el comando, la información de instrucciones se entrega al analizador para analizar. El analizador actualiza el valor inicial y suscribe al observador para los cambios en los datos. Observador monitorea los cambios de datos y luego los alimenta al observador. El observador notifica el actualizador del resultado de cambio y encuentra la función de actualización correspondiente para actualizar la vista.
El proceso anterior se muestra en la figura:
La siguiente es una descripción de los principios básicos de la implementación de estos cinco módulos (el código solo se publica en las partes clave, vaya a mi github para la implementación completa)
1. Compilar compilador del módulo
La responsabilidad del compilador es principalmente para escanear y extraer instrucciones para cada nodo del elemento. Debido a que el proceso de compilación y análisis atravesará todo el árbol de nodos muchas veces, para mejorar la eficiencia de la compilación, en el constructor MVVM, primero convertirá el elemento en una copia de los fragmentos de documentos. El objeto de compilación es este fragmento de documento y no debe ser un elemento objetivo. Después de compilarse todos los nodos, el fragmento del documento se agrega al nodo real original.
VM.complieElement Implementa la extracción de escaneo e instrucciones de todos los nodos del elemento:
vm.compileElement = function (fragment, root) {var nodo, childNodes = fragment.childnodes; // escane el nodo infantil para (var i = 0; i <childNodes.length; i ++) {node = childnodes [i]; if (this.hasdirective (node)) {this. Escanee el nodo infantil del nodo infantil if (node.childnodes.length) {this.compileElement (nodo, falso);}} // escanee los nodos secundarios if (root) if (root) {this.compileLeLLNodes ();}}El método VM.compileAllNodes compilará cada nodo en este. $ Uncompilenodes (deje la información de instrucciones al analizador). Después de compilar un nodo, se eliminará de la cola de caché. Al mismo tiempo, verifique esto. $ Uncompilenodes.length When longitud === 0, significa que toda la compilación se completa. Puede agregar fragmentos de documentos al nodo real.
2. Parser de módulo de análisis de instrucciones
Cuando el compilador del compilador extrae las instrucciones de cada nodo, se puede enviar al analizador para analizar. Cada instrucción tiene un método de análisis diferente. Todas las instrucciones solo necesitan hacer dos cosas: una es actualizar el valor de datos a la vista (estado inicial), y la otra es suscribir la función de actualización al monitoreo de cambio del modelo. Aquí usamos el análisis de V "como ejemplo para describir el método de análisis general de una directiva:
parser.parsevText = function (nodo, modelo) {// Obtenga el valor inicial definido en el modelo var text = this. $ modelo [modelo]; // actualiza el texto nodo.textContent = text; // La función de actualización correspondiente: // updater.updatenodeTcontent (nodo, texto); // suscribir al cambio del modelo en el reloj de relojero. {node.TextContent = Last; // updater.updatenodeTextContent (nodo, text);});}3. Observador del módulo de suscripción de datos
En el ejemplo anterior, Watcher proporciona un método de reloj para suscribirse a los cambios de datos. Un parámetro es el modelo de campo del modelo y el otro es la función de devolución de llamada. La función de devolución de llamada se activa a través del observador. El nuevo valor último y antiguo valor antiguo se pasan en el parámetro. Después de que el observador obtiene el nuevo valor, puede encontrar la devolución de llamada correspondiente (función de actualización) del modelo para actualizar la vista. Las funciones de modelo y actualización son una relación de uno a muchos, es decir, un modelo puede tener tantas funciones de devolución de llamada (funciones de actualización) que lo manejan, por ejemplo: V-text = "title" y V-html = instrucciones de "título" comparten un campo de modelo de datos.
Agregar suscripción de datos a Watcher.watch El método de implementación es:
watcher.watch = function (field, callback, context) {var callbacks = this. $ watchCallbacks; if (! Object.hasownproperty.call (this. $ model, campo)) {console.warn ('el campo:' + campo + 'no existe en el modelo!'); return;} // Cree una matriz de cache if (! callbacks [field] [];} // Cache Callbacks [Field] .Push ([Callback, context]);}Cuando cambia el campo de campo del modelo de datos, el observador desencadena todas las devoluciones de llamada en la matriz de caché que se han suscrito al campo.
4. Observador del módulo de monitoreo de datos
Observer es la base central de toda la implementación de MVVM. Leí un artículo que dice que OO (Object.observe) encenderá la revolución vinculante de datos y traerá una gran influencia al front-end. ¡Desafortunadamente, el draft de ES7 ha abandonado OO! ¡No hay soporte de navegador en la actualidad! Afortunadamente, también hay objeto. DefineProperty que puede simular un observador simple interceptando los descriptores de acceso (Get and Set) de las propiedades del objeto:
// Intercept the get and set methods of the prop property of the object Object.defineProperty(object, prop, {get: function() {return this.getValue(object, prop);},set: function(newValue) {var oldValue = this.getValue(object, prop);if (newValue !== oldValue) {this.setValue(object, newValue, prop); // activar el cambio de devolución de llamada this.RiggerChange (prop, newValue, OldValue);}}});Luego hay otra pregunta: ¿cómo monitorear las operaciones de matriz (empuje, cambio, etc.)? Todos los marcos MVVM se implementan reescribiendo el prototipo de la matriz:
Observer.ReWritearRayMethods = function (Array) {var self = this; var arrayproto = array.prototype; var arraymethods = object.create (arrayproto); var métodos = 'push | pop | shift | deshift | sort | sort | reverse'.split (' | ''); métodss.forEach (function (método) {object.defineProperty (ArrayMethods, Method, function () {var i = arguments.length; var original = arrayproto [método]; var args = new array (i); while (i--) {argumentos [i] = argumentos [i];} var product = original.apply (this, args); // thort thatback. método); return resultado;});}); array .__ proto__ = arraymethods;}Este método de implementación se hace referencia a Vue. Creo que se usa muy bien, pero el atributo de longitud de la matriz no se puede escuchar, por lo que en MVVM, la matriz. Se debe evitar la operación de longitud
5. Ver actualizador de módulos de actualización
El actualizador es el más fácil entre los cinco módulos, solo debe ser responsable de la función de actualización correspondiente a cada instrucción. Después de una serie de lanzamiento y dejando el resultado final al actualizador para las actualizaciones de la vista o del evento, por ejemplo, la función de actualización de V-Text es:
updater.updatenodeTextContent = function (nodo, text) {node.TextContent = text;}Actualización de la función de V: Estilo:
updater.updateNodeStyle = function (nodo, propiedad, valor) {node.style [propperty] = valor;}Implementación de la unión de datos bidireccionales
La unión de datos bidireccionales de los elementos de formulario es una de las características más importantes de MVVM:
De hecho, el principio de implementación de esta función mágica también es muy simple. Solo hay dos cosas que hacer: una es actualizar el valor del formulario cuando los datos cambian, y el otro es actualizar los datos cuando el valor del formulario cambia, de modo que el valor de los datos esté vinculado al valor del formulario.
Los cambios de datos en los valores de formulario de actualización se pueden realizar fácilmente utilizando el módulo de observador mencionado anteriormente:
watcher.watch (modelo, función (último, antiguo) {input.value = last;}); 'Para actualizar los datos en los cambios de formulario, solo necesita escuchar los eventos que vale la pena cambiar el formulario en tiempo real y actualizar los campos correspondientes del modelo de datos:
modelo var = this. $ modelo; input.AdDeventListenr ('Change', function () {Model [Field] = this.Value;}); 'Otros formularios radio, casilla de verificación y selección son el mismo principio.
Lo anterior, se han explicado todo el proceso y las ideas básicas de implementación de cada módulo. La primera vez que publiqué un artículo en la comunidad, mis habilidades de expresión de idiomas no son muy buenas. Si hay palabras incorrectas y cosas malas escritas, ¡espero que todos puedan criticarlas y corregirlas!
Conclusión
Estoy probando este simple MVVM.JS porque usé Vue.js en mi proyecto de marco, pero acabo de utilizar su sistema de instrucciones. Muchas funciones solo se usaron aproximadamente una cuarta parte. Pensé que sería suficiente para implementar la unión de datos y la refresión de la vista. Como resultado, no encontré una biblioteca de JavaScript, así que creé esa rueda yo mismo.
Aunque las funciones y la estabilidad son mucho menores que los marcos MVVM populares como Vue, y la implementación del código puede ser relativamente rugosa, se ha agregado muchos conocimientos al construir este progreso de la rueda en el lanzamiento!
En la actualidad, mi MVVM.JS solo implementa la función más básica. Continuaré mejorándolo y fortaleciéndolo en el futuro. Si está interesado, discuta y mejora juntos ~