¿Cómo funciona el enlace de datos en el AngularJS
marco?
No he encontrado detalles técnicos en su sitio . Está más o menos claro cómo funciona cuando los datos se propagan de la vista al modelo. Pero, ¿cómo realiza AngularJS el seguimiento de los cambios en las propiedades del modelo sin establecedores y captadores?
Descubrí que hay observadores de JavaScript que pueden hacer este trabajo. Pero no son compatibles con Internet Explorer 6 e Internet Explorer 7 . Entonces, ¿cómo sabe AngularJS que cambié, por ejemplo, lo siguiente y reflejé este cambio en una vista?
myobject.myproperty="new value";
javascript
angularjs
data-binding
Pashec
fuente
fuente
Respuestas:
AngularJS recuerda el valor y lo compara con un valor anterior. Esta es la comprobación básica de suciedad. Si hay un cambio en el valor, se activa el evento de cambio.
El
$apply()
método, que es lo que llamas cuando estás haciendo la transición de un mundo no AngularJS a un mundo AngularJS, llama$digest()
. Un resumen es simplemente una vieja verificación sucia. Funciona en todos los navegadores y es totalmente predecible.Para contrastar la verificación sucia (AngularJS) frente a los oyentes de cambio ( KnockoutJS y Backbone.js ): Si bien la verificación sucia puede parecer simple e incluso ineficiente (lo abordaré más adelante), resulta que es semánticamente correcto todo el tiempo, mientras que los oyentes de cambios tienen muchos casos extraños en las esquinas y necesitan cosas como el seguimiento de dependencias para hacerlo más semánticamente correcto. El seguimiento de dependencia de KnockoutJS es una característica inteligente para un problema que AngularJS no tiene.
Problemas con los oyentes de cambio:
¿Qué pasa con el rendimiento?
Por lo tanto, puede parecer que somos lentos, ya que la verificación sucia es ineficiente. Aquí es donde debemos mirar los números reales en lugar de solo tener argumentos teóricos, pero primero definamos algunas restricciones.
Los humanos son:
Lento : cualquier cosa que supere los 50 ms es imperceptible para los humanos y, por lo tanto, puede considerarse "instantáneo".
Limitado : en realidad, no puede mostrar más de aproximadamente 2000 piezas de información a un humano en una sola página. Cualquier cosa más que eso es una interfaz de usuario realmente mala, y los humanos no pueden procesar esto de todos modos.
Entonces, la verdadera pregunta es esta: ¿Cuántas comparaciones puede hacer en un navegador en 50 ms? Esta es una pregunta difícil de responder ya que muchos factores entran en juego, pero aquí hay un caso de prueba: http://jsperf.com/angularjs-digest/6 que crea 10,000 observadores. En un navegador moderno, esto lleva menos de 6 ms. En Internet Explorer 8 tarda unos 40 ms. Como puede ver, esto no es un problema, incluso en navegadores lentos en estos días. Hay una advertencia: las comparaciones deben ser simples para ajustarse al límite de tiempo ... Desafortunadamente, es demasiado fácil agregar una comparación lenta en AngularJS, por lo que es fácil crear aplicaciones lentas cuando no sabes lo que estás haciendo. Pero esperamos tener una respuesta al proporcionar un módulo de instrumentación, que le mostrará cuáles son las comparaciones lentas.
Resulta que los videojuegos y las GPU utilizan el enfoque de verificación sucia, específicamente porque es consistente. Siempre que superen la frecuencia de actualización del monitor (generalmente 50-60 Hz, o cada 16.6-20 ms), cualquier rendimiento por encima de eso es un desperdicio, por lo que es mejor dibujar más cosas, que aumentar el FPS.
fuente
Misko ya dio una excelente descripción de cómo funcionan los enlaces de datos, pero me gustaría agregar mi opinión sobre el problema de rendimiento con el enlace de datos.
Como dijo Misko, alrededor de 2000 enlaces es donde comienzas a ver problemas, pero de todos modos no debes tener más de 2000 datos en una página. Esto puede ser cierto, pero no todos los enlaces de datos son visibles para el usuario. Una vez que comience a construir cualquier tipo de widget o cuadrícula de datos con enlace bidireccional, puede alcanzar fácilmente 2000 enlaces, sin tener una mala experiencia de usuario.
Considere, por ejemplo, un cuadro combinado donde puede escribir texto para filtrar las opciones disponibles. Este tipo de control podría tener ~ 150 elementos y aún ser altamente utilizable. Si tiene alguna característica adicional (por ejemplo, una clase específica en la opción seleccionada actualmente), comienza a obtener 3-5 enlaces por opción. Coloque tres de estos widgets en una página (por ejemplo, uno para seleccionar un país, el otro para seleccionar una ciudad en dicho país y el tercero para seleccionar un hotel) y ya se encuentra entre 1000 y 2000 enlaces.
O considere una cuadrícula de datos en una aplicación web corporativa. 50 filas por página no son irrazonables, cada una de las cuales podría tener 10-20 columnas. Si construye esto con ng-repeats, y / o tiene información en algunas celdas que usa algunos enlaces, podría acercarse a 2000 enlaces solo con esta cuadrícula.
Creo que esto es un gran problema cuando trabajo con AngularJS, y la única solución que he podido encontrar hasta ahora es construir widgets sin usar enlace bidireccional, en lugar de usar ngOnce, cancelar el registro de observadores y trucos similares, o construir directivas que construyen el DOM con jQuery y la manipulación del DOM. Siento que esto derrota el propósito de usar Angular en primer lugar.
Me encantaría escuchar sugerencias sobre otras formas de manejar esto, pero tal vez debería escribir mi propia pregunta. Quería poner esto en un comentario, pero resultó ser demasiado largo para eso ...
TL; DR
El enlace de datos puede causar problemas de rendimiento en páginas complejas.
fuente
Comprobando sucio el
$scope
objetoAngular mantiene un simple
array
observador en los$scope
objetos. Si inspecciona alguno$scope
, encontrará que contiene unarray
llamado$$watchers
.Cada observador es un
object
que contiene entre otras cosasattribute
nombre, o algo más complicado.$scope
como sucia.Cómo se definen los observadores
Hay muchas formas diferentes de definir un observador en AngularJS.
Puede explícitamente
$watch
unattribute
encendido$scope
.Puede colocar una
{{}}
interpolación en su plantilla (se creará un observador para usted en la actual$scope
).Puede solicitar una directiva como la
ng-model
que define al observador por usted.El
$digest
ciclo verifica a todos los observadores contra su último valorCuando interactuamos con AngularJS a través de los canales normales (ng-model, ng-repeat, etc.) la directiva activará un ciclo de resumen.
Un ciclo de digestión es un recorrido profundo y primero de
$scope
todos sus hijos . Para cada uno$scope
object
, iteramos sobre él$$watchers
array
y evaluamos todas las expresiones. Si el nuevo valor de expresión es diferente del último valor conocido, se llama a la función del observador. Esta función podría recompilar parte del DOM, recalcular un valor$scope
, activar unAJAX
request
, cualquier cosa que necesite hacer.Se recorre cada ámbito y cada expresión de observación se evalúa y se compara con el último valor.
Si se activa un observador,
$scope
está sucioSi se activa un observador, la aplicación sabe que algo ha cambiado y
$scope
está marcado como sucio.Las funciones de vigilante pueden cambiar otros atributos en
$scope
o en un padre$scope
. Si se$watcher
ha activado una función, no podemos garantizar que nuestras otras$scope
estén todavía limpias, por lo que ejecutamos todo el ciclo de resumen nuevamente.Esto se debe a que AngularJS tiene un enlace bidireccional, por lo que los datos se pueden volver a pasar al
$scope
árbol. Podemos cambiar un valor en un valor superior$scope
que ya ha sido digerido. Quizás cambiemos un valor en el$rootScope
.Si
$digest
está sucio, ejecutamos todo el$digest
ciclo nuevamenteContinuamente recorremos el
$digest
ciclo hasta que el ciclo de resumen salga limpio (todas las$watch
expresiones tienen el mismo valor que tenían en el ciclo anterior) o alcanzamos el límite de resumen. Por defecto, este límite se establece en 10.Si alcanzamos el límite de resumen, AngularJS generará un error en la consola:
El resumen es difícil para la máquina pero fácil para el desarrollador
Como puede ver, cada vez que algo cambia en una aplicación AngularJS, AngularJS verificará a cada observador en la
$scope
jerarquía para ver cómo responder. Para un desarrollador, esto es una gran ayuda para la productividad, ya que ahora necesita escribir casi ningún código de cableado, AngularJS solo notará si un valor ha cambiado y hará que el resto de la aplicación sea consistente con el cambio.Desde la perspectiva de la máquina, aunque esto es muy ineficiente y ralentizará nuestra aplicación si creamos demasiados observadores. Misko ha citado una cifra de aproximadamente 4000 observadores antes de que su aplicación parezca lenta en navegadores antiguos.
Este límite es fácil de alcanzar si
ng-repeat
supera un gran,JSON
array
por ejemplo. Puede mitigar esto usando características como el enlace único para compilar una plantilla sin crear observadores.Cómo evitar crear demasiados observadores
Cada vez que su usuario interactúa con su aplicación, cada observador en su aplicación será evaluado al menos una vez. Una gran parte de la optimización de una aplicación AngularJS es reducir la cantidad de observadores en su
$scope
árbol. Una manera fácil de hacer esto es con un enlace único .Si tiene datos que rara vez cambiarán, puede vincularlos solo una vez usando la sintaxis ::, así:
o
El enlace solo se activará cuando se represente la plantilla que contiene y se carguen los datos
$scope
.Esto es especialmente importante cuando tienes
ng-repeat
muchos artículos.fuente
Este es mi entendimiento básico. ¡Bien puede estar mal!
$watch
método.$apply
método.$apply
del$digest
método se invoca el cual pasa a través de cada uno de los relojes y controles para ver si han cambiado desde la última vez que el$digest
RAN.En el desarrollo normal, la sintaxis de enlace de datos en el HTML le dice al compilador AngularJS que cree los relojes para usted y los métodos del controlador ya se ejecutan dentro
$apply
. Entonces, para el desarrollador de la aplicación, todo es transparente.fuente
Me lo pregunté por un tiempo. Sin setters, ¿cómo se
AngularJS
notan los cambios en el$scope
objeto? ¿Los encuesta?Lo que en realidad hace es esto: cualquier lugar "normal" en el que modifique el modelo ya fue llamado desde el interior de
AngularJS
, por lo que automáticamente lo llamará$apply
después de que se ejecute su código. Digamos que su controlador tiene un método conectado ang-click
algún elemento. Debido a queAngularJS
conecta la llamada de ese método para usted, tiene la oportunidad de hacerlo$apply
en el lugar apropiado. Del mismo modo, para las expresiones que aparecen justo en las vistas, se ejecutan porAngularJS
lo que hace el$apply
.Cuando la documentación habla de tener que llamar
$apply
manualmente para obtener un código externoAngularJS
, se trata de un código que, cuando se ejecuta, no proviene deAngularJS
sí mismo en la pila de llamadas.fuente
Explicando con fotos:
El enlace de datos necesita un mapeo
La referencia en el alcance no es exactamente la referencia en la plantilla. Cuando vincula dos objetos a datos, necesita un tercero que escuche el primero y modifique el otro.
Aquí, cuando modifica el
<input>
, toca el data-ref3 . Y el mecanismo clásico de enlace de datos cambiará data-ref4 . Entonces, ¿cómo{{data}}
se moverán las otras expresiones?Los eventos conducen a $ digest ()
Angular mantiene un
oldValue
ynewValue
de cada enlace. Y después de cada evento angular , el famoso$digest()
bucle verificará la WatchList para ver si algo cambió. Estos eventos angular sonng-click
,ng-change
,$http
completada ... El$digest()
bucle de voluntad, siempre y cuando ningúnoldValue
difiere de lanewValue
.En la imagen anterior, notará que data-ref1 y data-ref2 ha cambiado.
Conclusiones
Es un poco como el huevo y el pollo. Nunca se sabe quién comienza, pero espero que funcione la mayor parte del tiempo como se esperaba.
El otro punto es que puede comprender fácilmente el impacto profundo de un enlace simple en la memoria y la CPU. Esperemos que las computadoras de escritorio sean lo suficientemente gordas para manejar esto. Los teléfonos móviles no son tan fuertes.
fuente
Obviamente, no hay una comprobación periódica de
Scope
si hay algún cambio en los objetos adjuntos. No se miran todos los objetos adjuntos al alcance. Alcance prototípicamente mantiene un $$ observadores .Scope
solo itera a través de esto$$watchers
cuando$digest
se llama.Angular agrega un observador a los observadores $$ para cada uno de estos
La función $ watch toma tres parámetros:
Hay una cosa interesante en Angular llamada Digest Cycle. El ciclo de $ digest comienza como resultado de una llamada a $ scope. $ Digest (). Suponga que cambia un modelo $ scope en una función de controlador a través de la directiva ng-click. En ese caso, AngularJS activa automáticamente un ciclo de $ digest llamando a $ digest (). Además de ng-click, hay varias otras directivas / servicios integrados que le permiten cambiar los modelos (por ejemplo, ng-model, $ timeout, etc.) y activa automáticamente un ciclo de $ digest. La implementación aproximada de $ digest se ve así.
Si utilizamos la función setTimeout () de JavaScript para actualizar un modelo de alcance, Angular no tiene forma de saber qué puede cambiar. En este caso, es nuestra responsabilidad llamar a $ apply () manualmente, lo que desencadena un ciclo de $ digest. Del mismo modo, si tiene una directiva que configura un detector de eventos DOM y cambia algunos modelos dentro de la función del controlador, debe llamar a $ apply () para garantizar que los cambios surtan efecto. La gran idea de $ apply es que podemos ejecutar algún código que no tenga conocimiento de Angular, ese código aún puede cambiar las cosas en el alcance. Si ajustamos ese código en $ apply, se encargará de llamar a $ digest (). Implementación aproximada de $ apply ().
fuente
AngularJS maneja el mecanismo de enlace de datos con la ayuda de tres potentes funciones: $ watch () , $ digest () y $ apply () . La mayoría de las veces, AngularJS llamará a $ scope. $ Watch () y $ scope. $ Digest (), pero en algunos casos puede que tenga que llamar a estas funciones manualmente para actualizar con nuevos valores.
$ reloj () : -
$ digest () -
$ apply () -
fuente
Sucedió que necesitaba vincular un modelo de datos de una persona con un formulario, lo que hice fue un mapeo directo de los datos con el formulario.
Por ejemplo, si el modelo tenía algo como:
La entrada de control del formulario:
De esa manera, si modifica el valor del controlador de objetos, esto se reflejará automáticamente en la vista.
Un ejemplo en el que pasé el modelo se actualiza a partir de los datos del servidor es cuando solicita un código postal y un código postal basado en cargas escritas, una lista de colonias y ciudades asociadas con esa vista y, de forma predeterminada, establece el primer valor con el usuario. Y esto funcionó muy bien, lo que sucede es que a
angularJS
veces lleva unos segundos actualizar el modelo, para hacer esto puedes poner una rueda giratoria mientras se muestran los datos.fuente
El enlace de datos unidireccional es un enfoque en el que se toma un valor del modelo de datos y se inserta en un elemento HTML. No hay forma de actualizar el modelo desde la vista. Se utiliza en sistemas de plantillas clásicas. Estos sistemas enlazan datos en una sola dirección.
La vinculación de datos en aplicaciones angulares es la sincronización automática de datos entre el modelo y los componentes de la vista.
El enlace de datos le permite tratar el modelo como la única fuente de verdad en su aplicación. La vista es una proyección del modelo en todo momento. Si se cambia el modelo, la vista refleja el cambio y viceversa.
fuente
Aquí hay un ejemplo de enlace de datos con AngularJS, utilizando un campo de entrada. Explicaré mas tarde
código HTML
Código AngularJS
Como puede ver en el ejemplo anterior, AngularJS utiliza
ng-model
para escuchar y ver lo que sucede en los elementos HTML, especialmente en losinput
campos. Cuando algo sucede, haz algo. En nuestro caso,ng-model
se une a nuestra vista, utilizando la notación de bigote{{}}
. Lo que se escriba dentro del campo de entrada se muestra en la pantalla al instante. Y esa es la belleza del enlace de datos, usando AngularJS en su forma más simple.Espero que esto ayude.
Vea un ejemplo de trabajo aquí en Codepen
fuente
AngularJs admite enlace de datos bidireccional .
Significa que puede acceder a la vista de datos -> Controlador y controlador -> Ver
Por ej.
1)
O / P
Puede vincular datos en
ng-model
Me gusta: -2)
Aquí, en el ejemplo anterior, cualquier entrada que dé el usuario, será visible en la
<div>
etiqueta.Si desea vincular la entrada de html al controlador: -
3)
Aquí, si desea utilizar la entrada
name
en el controlador, entonces,ng-model
une nuestro punto de vista y lo expresa en expresión{{ }}
.ng-model
son los datos que se muestran al usuario en la vista y con los que el usuario interactúa.Por lo tanto, es fácil vincular datos en AngularJs.
fuente
Angular.js crea un observador para cada modelo que creamos a la vista. Cada vez que se cambia un modelo, se agrega una clase "ng-dirty" al modelo, por lo que el observador observará todos los modelos que tengan la clase "ng-dirty" y actualizará sus valores en el controlador y viceversa.
fuente
el enlace de datos:
¿Qué es el enlace de datos?
Cada vez que el usuario cambia los datos en la vista, se produce una actualización de ese cambio en el modelo de alcance, y viceversa.
¿Como es posible?
Respuesta corta: con la ayuda del ciclo de digestión.
Descripción: Angular js establece el observador en el modelo de alcance, que activa la función de escucha si hay un cambio en el modelo.
// Código de actualización Dom con nuevo valor
});
Entonces, ¿cuándo y cómo se llama la función de observador?
La función de vigilante se llama como parte del ciclo de resumen.
El ciclo de resumen se llama activado automáticamente como parte de js angulares integrados en directivas / servicios como ng-model, ng-bind, $ timeout, ng-click y otros ... que le permiten activar el ciclo de resumen.
Función del ciclo digestivo:
es decir
$rootScope.$apply()
Nota: $ apply () es igual a $ rootScope. $ Digest () esto significa que la comprobación sucia comienza desde la raíz o la parte superior o el ámbito primario hasta todos los $ scopes secundarios en la aplicación angular js.
Las características anteriores funcionan en los navegadores IE para las versiones mencionadas también simplemente asegurándose de que su aplicación sea angular js, lo que significa que está utilizando el archivo de script de framework angularjs al que se hace referencia en la etiqueta del script.
Gracias.
fuente