2015 votes

Comment fonctionne la liaison de données dans AngularJS ?

Comment fonctionne la liaison de données dans le AngularJS cadre ?

Je n'ai pas trouvé de détails techniques sur leur site . La façon dont cela fonctionne lorsque les données sont propagées de la vue au modèle est plus ou moins claire. Mais comment AngularJS suit-il les modifications des propriétés du modèle sans setters ni getters ?

J'ai découvert qu'il y a Observateurs de JavaScript qui peuvent faire ce travail. Mais ils ne sont pas pris en charge dans Internet Explorer 6 y Internet Explorer 7 . Alors comment AngularJS sait-il que j'ai modifié par exemple ce qui suit et que j'ai répercuté ce changement sur une vue ?

myobject.myproperty="new value";

10 votes

Sachez que depuis angular 1.0.0rc1, vous devez spécifier ng-model-instant ( docs-next.angularjs.org/api/ ) pour que votre modérateur soit mis à jour en permanence. Sinon, il sera mis à jour lors d'un événement flou.

8 votes

Le lien de Marcello est apparemment cassé, alors le voici à nouveau : github.com/mhevery/angular.js/blob/master/docs/content/guide/

6 votes

@orian, ce lien est mauvais. mis à jour vers (je suppose) est le même - docs.angularjs.org/guide/databinding

2758voto

Misko Hevery Points 25631

AngularJS se souvient de la valeur et la compare à une valeur précédente. Il s'agit d'une vérification de base. S'il y a un changement de valeur, il déclenche l'événement de changement.

El $apply() qui est ce que vous appelez lorsque vous passez d'un monde non AngularJS à un monde AngularJS, appelle $digest() . Un condensé n'est qu'une simple vérification de routine. Il fonctionne sur tous les navigateurs et est totalement prévisible.

Pour opposer dirty-checking (AngularJS) et change listeners ( KnockoutJS y Backbone.js ) : Alors que le dirty-checking peut sembler simple, et même inefficace (j'y reviendrai plus tard), il s'avère qu'il est sémantiquement correct tout le temps, alors que les change listeners ont beaucoup de cas de coin bizarres et ont besoin de choses comme le suivi des dépendances pour le rendre plus sémantiquement correct. Le suivi des dépendances de KnockoutJS est une fonctionnalité intelligente pour un problème qu'AngularJS n'a pas.

Les problèmes des auditeurs de changement :

  • La syntaxe est atroce, car les navigateurs ne la prennent pas en charge de manière native. Oui, il existe des proxies, mais ils ne sont pas sémantiquement corrects dans tous les cas, et bien sûr, il n'y a pas de proxies sur les anciens navigateurs. L'essentiel est que le dirty-checking vous permet de faire POJO tandis que KnockoutJS et Backbone.js vous obligent à hériter de leurs classes et à accéder à vos données au moyen d'accesseurs.
  • Changez la coalescence. Supposons que vous ayez un tableau d'éléments. Supposons que vous vouliez ajouter des éléments dans un tableau. Pendant que vous bouclez pour ajouter, à chaque fois que vous ajoutez, vous déclenchez des événements sur les changements, ce qui rend l'interface utilisateur. C'est très mauvais pour les performances. Ce que vous voulez, c'est mettre à jour l'interface utilisateur une seule fois, à la fin. Les événements de changement sont trop fins.
  • Les auditeurs de changement se déclenchent immédiatement sur un setter, ce qui est un problème, car l'auditeur de changement peut modifier les données, ce qui déclenche d'autres événements de changement. Ce n'est pas une bonne chose, car dans votre pile, plusieurs événements de modification peuvent se produire en même temps. Supposons que vous ayez deux tableaux qui doivent être synchronisés pour une raison quelconque. Vous pouvez seulement ajouter à l'un ou l'autre, mais à chaque fois que vous ajoutez, vous déclenchez un événement de changement, qui a maintenant une vue incohérente du monde. Il s'agit d'un problème très similaire au verrouillage des threads, que JavaScript évite puisque chaque callback s'exécute exclusivement et jusqu'à la fin. Les événements de modification rompent avec ce principe, car les paramètres peuvent avoir des conséquences importantes qui ne sont ni prévues ni évidentes, ce qui crée à nouveau le problème des fils. Il s'avère que ce que vous voulez faire, c'est retarder l'exécution du listener et garantir qu'un seul listener s'exécute à la fois, ce qui permet à n'importe quel code d'être libre de modifier les données, tout en sachant qu'aucun autre code ne s'exécute pendant ce temps.

Qu'en est-il des performances ?

Il peut donc sembler que nous soyons lents, puisque le dirty-checking est inefficace. C'est là que nous devons examiner des chiffres réels plutôt que de nous contenter d'arguments théoriques, mais définissons d'abord quelques contraintes.

Les humains le sont :

  • Lent - Tout ce qui est plus rapide que 50 ms est imperceptible pour les humains et peut donc être considéré comme "instantané".

  • Limitée - On ne peut pas vraiment montrer plus de 2000 éléments d'information à un humain sur une seule page. Tout ce qui dépasse ce chiffre est une très mauvaise interface utilisateur, et les humains ne peuvent pas traiter ces informations de toute façon.

La vraie question est donc la suivante : Combien de comparaisons pouvez-vous faire sur un navigateur en 50 ms ? Il est difficile de répondre à cette question car de nombreux facteurs entrent en jeu, mais voici un cas test : http://jsperf.com/angularjs-digest/6 ce qui crée 10 000 spectateurs. Sur un navigateur moderne, cela prend un peu moins de 6 ms. Sur Internet Explorer 8 il faut environ 40 ms. Comme vous pouvez le constater, ce n'est pas un problème, même sur les navigateurs lents de nos jours. Il y a un bémol : les comparaisons doivent être simples pour tenir dans le temps imparti... Malheureusement, il est beaucoup trop facile d'ajouter une comparaison lente dans AngularJS, il est donc facile de construire des applications lentes lorsque vous ne savez pas ce que vous faites. Mais nous espérons avoir une réponse en fournissant un module d'instrumentation, qui vous montrerait quelles sont les comparaisons lentes.

Il s'avère que les jeux vidéo et les GPU utilisent l'approche dirty-checking, notamment parce qu'elle est cohérente. Tant qu'ils dépassent le taux de rafraîchissement de l'écran (typiquement 50-60 Hz, ou toutes les 16,6-20 ms), toute performance supérieure à ce taux est un gaspillage, donc il vaut mieux dessiner plus de choses que d'augmenter le nombre de FPS.

5 votes

N'obtiendriez-vous pas des performances accrues en utilisant de nombreuses lunettes plus petites ?

0 votes

Cette stratégie est intéressante, mais elle ne fonctionne que lorsque le changement passe par la boucle d'événements d'AngularJS. Si le changement n'est pas effectué en tant qu'événement de réponse de l'interface utilisateur d'AngularJS, par exemple un écouteur d'événement DOM, il n'est pas détecté. Mais c'est un compromis équitable....

2 votes

@Misko Hevery : Pourriez-vous donner un exemple de cas où les proxies ne sont pas sémantiquement corrects ?

327voto

MW. Points 2575

Misko a déjà donné une excellente description du fonctionnement des liaisons de données, mais je voudrais ajouter mon point de vue sur le problème de performance de ces liaisons.

Comme l'a dit Misko, c'est autour de 2000 liaisons que l'on commence à rencontrer des problèmes, mais de toute façon, on ne devrait pas avoir plus de 2000 informations sur une page. C'est peut-être vrai, mais toutes les liaisons de données ne sont pas visibles pour l'utilisateur. Dès que vous commencez à construire une sorte de widget ou de grille de données avec des liaisons bidirectionnelles, vous pouvez facilement atteindre 2000 fixations, sans avoir une mauvaise UX.

Prenons l'exemple d'une boîte combo dans laquelle vous pouvez saisir du texte pour filtrer les options disponibles. Ce type de contrôle pourrait comporter ~150 éléments et rester très utilisable. S'il dispose d'une fonctionnalité supplémentaire (par exemple, une classe spécifique sur l'option actuellement sélectionnée), vous commencez à avoir 3 à 5 liaisons par option. Mettez trois de ces widgets sur une page (par exemple, l'un pour sélectionner un pays, l'autre pour sélectionner une ville dans ledit pays, et le troisième pour sélectionner un hôtel) et vous êtes déjà quelque part entre 1000 et 2000 liaisons.

Ou considérez une grille de données dans une application web d'entreprise. Il n'est pas déraisonnable de prévoir 50 lignes par page, chacune d'entre elles pouvant comporter 10 à 20 colonnes. Si vous construisez cette grille avec ng-repeats et/ou si vous avez des informations dans certaines cellules qui utilisent des liaisons, vous pourriez approcher les 2000 liaisons avec cette seule grille.

Je trouve que c'est un énorme La seule solution que j'ai pu trouver jusqu'à présent est de construire des widgets sans utiliser de liaison bidirectionnelle, au lieu d'utiliser ngOnce, de désenregistrer les observateurs et d'autres astuces similaires, ou de construire des directives qui construisent le DOM avec jQuery et la manipulation du DOM. J'ai le sentiment que cela va à l'encontre de l'objectif de l'utilisation d'Angular en premier lieu.

J'aimerais bien entendre des suggestions sur d'autres façons de gérer cette situation, mais je devrais peut-être écrire ma propre question. Je voulais la mettre dans un commentaire, mais elle s'est avérée être bien trop longue pour cela...

TL;DR
La liaison des données peut entraîner des problèmes de performance sur les pages complexes.

26 votes

Oui, je suis d'accord. La principale responsabilité de notre application est d'afficher les connexions entre les différentes entités. Une page donnée peut avoir 10 sections. Chaque section a un tableau. Chaque tableau a 2-5 filtres typeahead. Chaque tableau comporte 2 à 5 colonnes, chacune avec 10 lignes. Très rapidement, nous nous heurtons à des problèmes de perforation, et nous nous contentons des options "trucs similaires".

1 votes

Nous avons des problèmes similaires avec notre grille de données avec 50 comme taille de page. Je constate qu'il y a environ 5 à 8 000 liaisons enregistrées sur cette page et, comme prévu, les performances sont médiocres. Avez-vous vraiment essayé l'option suggérée dans votre message ?

0 votes

@MW. - On dirait que vous dites que l'ajout d'une classe "selected" à une option de sélection ajouterait une liaison de données supplémentaire pour chaque option. N'auriez-vous pas besoin d'une seule liaison de données sur l'élément de sélection lui-même pour y parvenir ?

82voto

Pete BD Points 4774

Voici ce que j'ai compris. Elle peut très bien être fausse !

  1. Les éléments sont surveillés en passant une fonction (renvoyant l'élément à à surveiller) à la fonction $watch méthode.
  2. Les modifications des éléments surveillés doivent être effectuées dans un bloc de code enveloppé par la balise $apply méthode.
  3. A la fin de la $apply le site $digest La méthode est invoquée et passe chacune des surveillances et vérifie si elles ont été modifiées depuis la la dernière fois que le $digest couru.
  4. Si des changements sont trouvés, le condensé est invoqué à nouveau jusqu'à ce que tous les changements se stabilisent.

Dans le cadre d'un développement normal, la syntaxe de liaison de données dans le HTML indique au compilateur AngularJS de créer les montres pour vous et les méthodes du contrôleur sont exécutées à l'intérieur de $apply déjà. Pour le développeur d'applications, tout est donc transparent.

4 votes

Quand la méthode d'application est-elle déclenchée ?

3 votes

@EliseuMonar La boucle digest s'exécute à la suite d'un événement ou de l'appel de $apply(), elle n'est pas appelée périodiquement en fonction d'une minuterie. cf. Comment fonctionne la fonction $watch d'AngularJS ? y comment fonctionne la liaison et la digestion dans AngularJS ?

1 votes

@remi, je ne suis pas concerné par la dernière version d'AngularJS. Utilisent-ils déjà des proxies ou Object.observe ? Si non, ils sont encore dans l'ère du dirty checking, qui construit une boucle temporisée pour voir si les attributs du modèle ont changé.

63voto

darkporter Points 7552

Je me suis moi-même posé cette question pendant un certain temps. Sans setters, comment AngularJS avis de modification du $scope objet ? Est-ce qu'il les interroge ?

Voici ce qu'il fait en réalité : Tout endroit "normal" où vous modifiez le modèle a déjà été appelé depuis les entrailles de AngularJS Il appelle donc automatiquement $apply pour vous après l'exécution de votre code. Disons que votre contrôleur a une méthode qui est connectée à ng-click sur un élément. Parce que AngularJS câble l'appel de cette méthode pour vous, il a une chance de faire un $apply à l'endroit approprié. De même, pour les expressions qui apparaissent directement dans les vues, celles-ci sont exécutées par AngularJS donc il fait le $apply .

Lorsque la documentation parle de devoir appeler $apply manuellement pour le code à l'extérieur de AngularJS il s'agit de code qui, lorsqu'il est exécuté, ne provient pas de AngularJS lui-même dans la pile d'appels.

18voto

fauverism Points 459

Le Dirty checking sera déprécié dans Angular 2.0. J'aimerais que Misko revienne sur cette réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X