web-dev-qa-db-fra.com

Grande organisation d'application web backbone.js

Je travaille actuellement sur une grande application Web basée sur backbone.js et j'ai eu beaucoup de problèmes avec l'organisation, les "zombies", etc., donc j'ai décidé de faire un refactor majeur du code. J'ai déjà écrit un tas de fonctions d'aide pour gérer les "zombies"; cependant, je voudrais commencer dès le début et créer une structure/organisation agréable pour le code. Je n'ai pas trouvé beaucoup de bons tutoriels/exemples sur l'organisation backbone.js à grande échelle, donc j'ai en quelque sorte commencé à zéro et j'aimerais voir si je peux obtenir des opinions sur où j'ai commencé.

J'ai évidemment mis en place mon code dans un espace de noms global; mais je voudrais aussi garder cet espace de noms plutôt propre. Mon principal app.js garde les fichiers de classe eux-mêmes séparés de l'espace de noms global; vous pouvez enregistrer une classe (afin qu'elle puisse être instanciée) en utilisant la fonction reg () et la fonction inst () instancie une classe à partir du tableau des classes. Ainsi, en plus des 3 méthodes, l'espace de noms MyApp n'a que Router, Model et View:

var MyApp = (function () {

    var classes = {
        Routers: {},
        Collections: {},
        Models: {},
        Views: {}
    };

    methods = {

        init: function () {
            MyApp.Router = MyApp.inst('Routers', 'App');
            MyApp.Model = MyApp.inst('Models', 'App');
            MyApp.View = MyApp.inst('Views', 'App');
            Backbone.history.start();
        },

        reg: function (type, name, C) {
            classes[type][name] = C;
        },

        inst: function (type, C, attrs) {
            return new classes[type][C](attrs || {});
        }

    };

    return methods;

}());

$(MyApp.init);

Dans les modèles, les collections, les routeurs et les vues, je travaille comme d'habitude, mais je dois ensuite enregistrer cette classe à la fin du fichier afin qu'elle puisse être instanciée ultérieurement (sans encombrer l'espace de noms) avec:

MyApp.reg('Models', 'App', Model);

Est-ce que cela semble être un moyen inutile d'organiser le code? D'autres ont-ils de meilleurs exemples sur la façon d'organiser de très gros projets avec de nombreux routeurs, collections, modèles et vues?

61
user527480

J'ai récemment travaillé sur un projet Backbone appelé GapVis ( code ici , rendu du contenu ici ). Je ne sais pas si c'est "vraiment grand", mais c'est gros et relativement complexe - 24 classes de vue, 5 routeurs, etc. Cela pourrait valoir la peine d'y jeter un œil, bien que je ne sache pas que toutes mes approches seront pertinent. Vous pouvez voir une partie de ma pensée dans le long commentaire d'introduction dans mon fichier principal app.js . Quelques choix architecturaux clés:

  • J'ai un modèle singleton State qui contient toutes les informations sur l'état actuel - la vue actuelle, les ID de modèle que nous examinons, etc. Chaque vue qui doit modifier l'état de l'application le fait en définissant des attributs sur le State, et chaque vue qui doit répondre à l'état écoute ce modèle pour les événements. Cela est même vrai pour les vues qui modifient l'état et se mettent à jour - les gestionnaires d'événements de l'interface utilisateur dans events ne rendent jamais à nouveau la vue, cela se fait à la place en liant les fonctions de rendu à l'état. Ce modèle a vraiment aidé à séparer les vues les unes des autres - les vues n'appellent jamais une méthode sur une autre vue.

  • Mes routeurs sont traités comme des vues spécialisées - ils répondent aux événements de l'interface utilisateur (c'est-à-dire en tapant une URL) en mettant à jour l'état, et ils répondent aux changements d'état en mettant à jour l'interface utilisateur (c'est-à-dire en changeant l'URL).

  • Je fais plusieurs choses similaires à ce que vous proposez. Mon espace de noms a une fonction init similaire à la vôtre et un objet settings pour les constantes. Mais j'ai également mis la plupart des classes de modèles et de vues dans l'espace de noms, car je devais y faire référence dans plusieurs fichiers.

  • J'utilise un système d'enregistrement pour mes routeurs, et j'en ai considéré un pour mes vues, comme un bon moyen d'empêcher les classes "master" (AppRouter et AppView) d'avoir à connaître chaque vue . Dans le cas AppView, cependant, il s'est avéré que l'ordre des vues enfants était important, j'ai donc fini par coder en dur ces classes.

Je dirais à peine que c'était la "bonne" façon de faire les choses, mais cela a fonctionné pour moi. J'espère que cela vous sera utile - j'ai également eu du mal à trouver des exemples de sources visibles de grands projets utilisant Backbone, et j'ai dû résoudre la plupart de ces problèmes au fur et à mesure.

32
nrabinowitz

Ces 2 ressources m'ont aidé à configurer mes applications dorsales sur un sous-sol solide:

13
jaydoubleyou

En fait, de différentes manières ont des avantages et des inconvénients de différentes manières.Le plus important est de trouver une manière appropriée d'organiser les fichiers.Voici l'organisation du projet que je fais actuellement. De cette façon, le focus sera les mêmes fichiers liés au module sont placés dans un dossier. Par exemple: le module people, ce module tous les fichiers sont placés dans le répertoire modules/base/people. Après la mise à jour et la maintenance de ce module, il suffit de se concentrer sur les fichiers de ce répertoire sur la ligne, cela n'affectera pas les fichiers en dehors du répertoire, et améliorera la maintenabilité.

J'espère que ma réponse pourra vous aider, j'espère que vous aurez de précieux conseils.

enter image description here

5
Justin wong

J'ai un espace de noms similaire à ce que vous faites (au moins pour la partie classes) et tous mes modèles, vues et contrôleurs ressemblent à ceci:

views/blocks.js:

(function(cn){
    cn.classes.views.blocks = cn.classes.views.base.extend({

        events: {},

        blocksTemplate: cn.helpers.loadTemplate('tmpl_page_blocks'),

        initialize: function(){
        },

        render: function(){
            $(this.el).html(this.blocksTemplate());
        },

        registerEvents: function(){},
        unregisterEvents: function(){}
    });
})(companyname);

Mon espace de noms JavaScript ressemble à ceci, bien que je l'améliore chaque fois que je crée une nouvelle application:

 companyname:{                                                                                                                                                                                 
   $: function(){},      <== Shortcut reference to document.getElementById                                                                                                                      
   appView: {},          <== Reference to instantiated AppView class.                                                                                                                           
   classes = {           <== Namespace for all custom Backbone classes.                                                                                                                         
     views : {},                                                                                                                                                                                
     models : {},                                                                                                                                                                               
     collections: {},                                                                                                                                                                           
     controllers : {},                                                                                                                                                                          
     Router: null                                                                                                                                                                               
   },                                                                                                                                                                                           
   models: {},          <== Instantiated models.                                                                                                                                                
   controllers: {},     <== Instantiated controllers.                                                                                                                                           
   router: {},          <== Instantiated routers.                                                                                                                                               
   helpers: {},         <== Reusable helper platform methods.                                                                                                                                   
   currentView: {},     <== A reference to the current view so that we can destroy it.                                                                                                          
   init: function(){}   <== Bootstrap code, starts the app.                                                                                                                           
 } 

Tout ce que je veux avoir toutes mes vues, je l'ai mis dans la vue de base. Mon contrôleur appellera registerEvents sur toute nouvelle vue qu'il crée (après le rendu) et unregisterEvents sur une vue juste avant de la tuer. Toutes les vues n'ont pas ces deux méthodes supplémentaires, il vérifie donc d'abord l'existence.

N'oubliez pas que toutes les vues sont livrées avec une fonction this.el.remove(); intégrée. Ce qui non seulement tue l'élément conteneur de vues, mais dissocie tous les événements qui lui sont attachés. Selon la façon dont vous créez vos vues via votre contrôleur, vous ne voudrez peut-être pas réellement tuer l'élément et faire à la place this.el.unbind () pour dissocier tous les événements.

5
Mauvis Ledford