27 votes

Comment structurer les programmes Javascript dans des applications Web complexes ?

J'ai un problème, qui n'est pas facile à décrire. Je suis en train d'écrire une application web qui fait de la forte utilisation de jQuery et des appels AJAX. Maintenant je n'ai pas beaucoup d'expérience en Javascript archicture, mais je me rends compte que mon programme n'a pas une bonne structure. Je pense que j'ai trop d'identificateurs se référant à la même (au moins plus ou moins) la chose.

Prenons un oeil à l'arbitraire d'un exemplaire de l'INTERFACE utilisateur widget qui fait qu'une infime partie de la demande: Le widget peut être une partie d'une fenêtre et la fenêtre peut être une partie d'un gestionnaire de fenêtre:

  1. Les gestionnaires d'événements utilisation des éléments du DOM en tant que paramètres. L'élément DOM représente un widget dans le navigateur.
  2. Beaucoup de fois j'ai utiliser jQuery objets (en fait des wrappers autour des éléments du DOM) à faire quelque chose avec le widget. Parfois, ils sont utilisés de façon transitoire, parfois, ils sont stockés dans une variable pour plus tard fins.
  3. L'AJAX appels de fonction utilisation de la chaîne d'identifiants pour ces widgets. Ils sont traités côté serveur.
  4. A côté de cela, j'ai un widget de la classe dont les instances représentent un widget. Il est instancié par le nouvel opérateur.

Maintenant, j'ai quelque peu différents de quatre identificateurs d'objet pour la même chose, ce qui doit être synchronisé jusqu'à ce que la page est chargée à nouveau. Cela ne semble pas être une bonne chose.

Tous les conseils?



EDIT:
@Will Morgan: C'est un concepteur de formulaire qui permet de créer des formulaires web dans le navigateur. Le backend est Zope, python serveur d'applications web. Il est difficile de se faire plus explicite comme c'est un problème général j'observe tout le temps lorsque vous faites Javasscript-développement avec le trio jQuery, arborescence DOM, et de mes propres prototypes des instances de classe.

EDIT2:
Je pense qu'il serait utile de faire un exemple, même si elle est artificielle. Ci-dessous vous voyez un enregistreur de widget qui peut être utilisé pour ajouter un élément à une page web dans laquelle connecté éléments sont affichés.

makeLogger = function(){
     var rootEl = document.createElement('div');
     rootEl.innerHTML = 'Logged items:';
     rootEl.setAttribute('class', 'logger');

     var append = function(msg){
           // append msg as a child of root element.
           var msgEl = document.createElement('div');
           msgEl.innerHTML = msg;
           rootEl.appendChild(msgEl);
     };

     return {
          getRootEl: function() {return rootEl;},
          log      : function(msg) {append(msg);}
     };
};

// Usage 
var logger = makeLogger();
var foo = document.getElementById('foo');
foo.appendChild(logger.getRootEl());
logger.log('What\'s up?');

À ce stade, j'ai un wrapper autour de la HTMLDivElement (hébergé objet). Avec l'enregistreur de données d'instance (l'objet natif) à portée de main, je peux facilement travailler avec elle par le biais de la fonction enregistreur.getRootEl().
Où je suis coincé, c'est quand j'ai le seul élément du DOM à la main et le besoin de faire quelque chose avec l'API publique retourné par la fonction makeLogger (par exemple, dans les gestionnaires d'événements). Et c'est là le bordel commence. J'ai besoin de tenir tous les objets natifs dans un référentiel ou quelque chose afin que je puisse le récupérer à nouveau. Il serait tellement plus agréable d'avoir une connexion (par exemple, une propriété de l'objet) de la hébergé objet d'origine de mon objet. Je sais que cela peut être fait, mais il a quelques inconvénients:

  • Ce genre de (circulaire) références sont potentiellement mémoire qui fuit jusqu'à IE7
  • Quand passer la hébergés objet et quand passer l' objet natif (fonctions)?

Pour l'instant, je fais le dos de référencement avec jQuery données() la méthode. Mais dans l'ensemble je n'aime pas la façon dont je dois garder une trace de la relation entre l'hébergé de l'objet et de ses natif de contrepartie.

Comment gérez-vous ce scénario?



EDIT3:
Après quelques connaissances que j'ai acquises à partir de Anurag l'exemple..
@Anurag: Si j'ai bien compris ton exemple de droite, le point critique est de mettre en place le bon (ce qui est correct dépend de vos besoins, tout de même) contexte d'exécution pour les gestionnaires d'événements. Et c'est dans votre cas, la présentation de l'objet de l'instance, ce qui est fait avec Mootool de bind() de la fonction. Afin de vous assurer que vous êtes TOUJOURS affaire avec l'enveloppe de l'objet (j'ai appelé l'objet natif) à la place de l'objet DOM, droit?

Une note pour le lecteur: Vous n'êtes pas obligé d'utiliser Mootools pour atteindre cet objectif. En jQuery, vous l'installation de votre gestionnaires d'événements avec le $.proxy() de la fonction, ou si vous utilisez un bon vieux Javascript, vous allez utiliser les appliquer propriété que chaque fonction expose.

5voto

Boldewyn Points 29961

Vous pouvez utiliser un registre mondial:

window.WidgetRegistry = {};
window.WidgetRegistry['foowidget'] = new Widget('#myID');

et quand les appels AJAX retour, ils peuvent obtenir le widget comme ceci:

var widgetID = data.widgetID;
if (widgetID in window.WidgetRegistry) {
    var widget = window.WidgetRegistry[widgetID];
}

Pour vos appels jQuery: je suppose qu'ils sont relativement peu coûteux, puisque jQuery caches objets pour les utiliser plus tard. Mais vous pouvez prolonger la ci-dessus suggère WidgetRegistry par l'aide d' .data():

var $widget = $('#myWidget');
var widgetID = 'foo';
$widget.data('widget', widgetID);

De cette façon, vous pouvez stocker le widget ID attaché à chaque objet jQuery et d'accéder à partir du registre mondial.

Des tests, si un objet jQuery possède déjà un widget:

return $('#test').data('widget') &&
       ($('#test').data('widget') in window.WidgetRegistry);

Remarque, que ce sont juste des suggestions. En fait, il ya des dizaines de façons de parvenir à une consolidation de ce genre. Si vous souhaitez combiner votre code plus loin avec jQuery, vous pouvez étendre l'objet jQuery, de sorte que vous pouvez écrire quelque chose comme:

$('#widget').widget({'foo':'bar'});
// and/or
var allWidgets = $('*:widget');
// ...

2voto

Anurag Points 66470

Pour les quatre objets qui doivent être synchronisés, vous pourriez avoir un objet unique et de passer la référence autour de dans un constructeur, ou que les arguments de la fonction.

La façon dont je résoudre ce problème est de ne jamais perdre une référence à l'objet de wrapper. Chaque fois qu'un objet DOM est nécessaire (par exemple, l'insertion dans la page), cet objet wrapper fournit. Mais s'en tenir à ce widget sur l'écran, l'objet wrapper met en place tous les cas de manipulation et AJAX code de traitement spécifique pour le widget, de sorte que la référence à la le wrapper est maintenu en tout temps dans ces gestionnaires d'événements et les rappels AJAX.

J'ai créé un exemple simple sur jsfiddle utilisant MooTools qui peut avoir du sens pour vous.

1voto

Sagi Points 5590

Je ne suis pas sûr que j'ai bien compris votre question, mais je vais essayer de pointer quelques idées.

À mon avis, vous devriez faire de la base de classe de widget, qui contient la fonctionnalité commune pour les widgets.

Prenons pour exemple AppName.Les Widgets.base(). L'une des variables d'instance est _events, qui est l'objet qui stocke les événements comme des clés et de la fonction en tant que valeurs. De cette façon, chaque classe définit les événements de ce widget, et vous pouvez facilement lier dans le constructeur. Comme pour les identificateurs de chaîne, le plus simple est d'utiliser toString().

Exemple:

namespace('AppName.Widgets'); // you can find implementations easy

AppName.Widgets.base = function() {
    if (!this._type) return;

    this._dom = $('div.widget.'+this._type);
    for (var e in this._events) {
        this._dom.bind(e, this._events[e]);
    }

    this.toString = function() { return this._type; };
}

AppName.Widgets.example = function() { // extends AppName.Widgets.base
    this._type   = 'example';
    this._events = { 'click' : function(e) { alert('click'); }  };

    AppName.Widgets.base.call(this);
}

0voto

Chad_K Points 71

Beaucoup de ce que vous pouvez ou ne pouvez pas le faire dépendra de combien vous avez de contrôle sur le javascript. Personnellement, j'ai souvent à utiliser des bibliothèques construites par d'autres, donc je peut seulement obtenir un nœud DOM travailler avec, mais j'ai vraiment besoin de mon objet. Dans ces cas-je trouver de l'aide de la fonctionnalité des données dans jQuery est très pratique. À l'aide de la fonctionnalité des données, vous pouvez le "store" de votre objet à l'intérieur de l'arbre DOM de la récupérer plus tard.

Compte tenu de votre exemple ci-dessus, voici comment vous pouvez utiliser la fonctionnalité des données à récupérer votre widget après avoir les fonctions qui utilisent uniquement le nœud DOM.

makeLogger = function(){
 var rootEl = document.createElement('div');
 rootEl.innerHTML = 'Connecté éléments:';
 rootEl.setAttribute('class', 'logger');

 var append = function(msg){
 // ajout de msg en tant qu'enfant de l'élément racine.
 var msgEl = document.createElement('div');
 msgEl.innerHTML = msg;
rootEl.appendChild(msgEl);
};

 var self = {
 getRootEl: function() {return rootEl;},
 journal : function(msg) {append(msg);}
};

 // Enregistrer une copie de la domNode
 $(rootEl).de données("logger", self);
 de retour à soi;
};

// Exemple de seulement obtenir l'arbre dom
la fonction de whatsUp (domNode){
 // Récupérer l'enregistreur de données de l'domNode
 $(domNode).de données('logger').log ("What\'s up?');
}

// Utilisation
var logger = makeLogger();
var loggerNode = logger.getRootEl();
var foo = document.getElementById('foo');
foo.appendChild(loggerNode);
whatsUp(loggerNode);

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