2011-11-03 6 views
61

Attualmente sto lavorando a una grande app web costruita su backbone.js e ho avuto un sacco di problemi con l'organizzazione, "zombi", ecc. Quindi ho deciso di fare un importante refactoring del codice. Ho già scritto un sacco di funzioni di supporto per affrontare gli "zombi"; tuttavia, mi piacerebbe ricominciare dall'inizio e creare una buona struttura/organizzazione per il codice. Non ho trovato molti tutorial/esempi sull'organizzazione backbone.js su larga scala, quindi ho iniziato da zero e vorrei vedere se riesco a ottenere qualche opinione su dove ho iniziato.Grande organizzazione di app web backbone.js

Ho ovviamente impostato il mio codice all'interno di un namespace globale; ma mi piacerebbe anche mantenere lo spazio dei nomi piuttosto pulito. La mia app.js principale mantiene i file di classe separati dallo spazio dei nomi globale; è possibile registrare una classe (in modo che possa essere istanziata) utilizzando la funzione reg() e la funzione inst() crea un'istanza di una classe dall'array delle classi. Così, oltre i 3 metodi, lo spazio dei nomi MyApp ha solo Router, modello e Vista:

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); 

all'interno dei modelli, del Collezionismo, router e Visualizzazioni, io lavoro come al solito, ma poi bisogno di registrare che di classe alla fine del il file in modo che potesse essere un'istanza in un momento successivo (senza ingombrare lo spazio dei nomi) con:

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

questo sembra un modo inutile per organizzare il codice? Gli altri hanno esempi migliori di come organizzare progetti davvero grandi con molti router, collezioni, modelli e viste?

+0

forse http://codereview.stackexchange.com è più adatto per questo tipo di domanda - ma tenerci aggiornati (link post);) – sled

+0

È possibile organizzare il codice in moduli diversi. Puoi consultare https://github.com/juggy/backbone-module Piccola auto-promozione: p – Julien

risposta

32

Recentemente ho lavorato a un progetto Backbone chiamato GapVis (code here, rendered content here). Non so se è "veramente grande", ma è big-ish e relativamente complesso - 24 classi vista, 5 router, ecc. Potrebbe valere la pena dare un'occhiata, anche se non so che tutti i miei approcci saranno pertinente. Puoi vedere alcuni dei miei pensieri nel lungo commento introduttivo nel mio main app.js file. Qualche chiave scelte architettoniche:

  • Ho un modello Singleton State che contiene tutte le informazioni correnti di stato - la vista corrente, quello ids modello che stiamo guardando, ecc Ogni opinione che ha bisogno di modificare lo stato dell'applicazione fa impostando gli attributi su State e ogni vista che deve rispondere allo stato ascolta quel modello per gli eventi. Questo vale anche per le viste che modificano lo stato e l'aggiornamento: i gestori di eventi dell'interfaccia utente in non rieseguono mai il rendering della vista, ciò avviene invece attraverso il binding delle funzioni di rendering allo stato. Questo schema ha davvero aiutato a mantenere le viste separate l'una dall'altra: le viste non chiamano mai un metodo su un'altra vista.

  • I miei router vengono trattati come visualizzazioni specializzate: rispondono agli eventi dell'interfaccia utente (ad esempio digitando un URL) aggiornando lo stato e rispondono alle modifiche dello stato aggiornando l'interfaccia utente (ovvero modificando l'URL).

  • Faccio molte cose simili a quello che stai proponendo. Il mio spazio dei nomi ha una funzione init simile alla tua e un oggetto settings per le costanti. Ma ho inserito la maggior parte del modello e visualizzato le classi nello spazio dei nomi, perché avevo bisogno di riferirmi a più file.

  • Io uso un sistema di registrazione per i miei router e ne ho considerato uno per le mie visualizzazioni, come un buon modo per mantenere le classi "master" (AppRouter e AppView) da essere a conoscenza di ogni vista. Nel caso AppView, tuttavia, è emerso che l'ordine delle visualizzazioni secondarie era importante, quindi ho finito con l'hard-coding di quelle classi.

Direi a malapena che questo era il modo "giusto" di fare le cose, ma ha funzionato per me. Spero che sia utile - ho anche avuto difficoltà a trovare esempi di sorgenti visibili di grandi progetti usando Backbone, e ho dovuto risolvere la maggior parte di questo mentre procedevo.

+0

Grazie per tutte queste informazioni; Sono contento che entrambi abbiamo avuto processi di pensiero simili - almeno convince che non sono pazzo haha. Mi piace decisamente la tua idea di usare gli eventi più spesso e di usare i router semplicemente come "viste url". Immagino di non essere ancora sicuro se il mio sistema di registrazione per tutte le classi sia utile ... Non ingombra il namespace globale ma importa? Ci sono problemi di prestazioni associati a ingombrare lo spazio dei nomi MyApp con i file di classe? – user527480

+0

Penso che probabilmente stai pensando troppo a quella parte, se il tuo obiettivo principale è quello di mantenere pulito lo spazio dei nomi - non ci sono problemi di prestazioni che io conosca, il problema riguarda più l'organizzazione del codice. C'è un po 'di compressione nell'uso di 'var MyClass' invece di' ns.MyClass', che di solito non è mungito, ma è minimo, e comunque stai usando nomi di stringhe. – nrabinowitz

5

ho spazio dei nomi simile a quello che stai facendo (almeno per le classi parte) e tutti i miei modelli, visualizzazioni e controller simile a questa:

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); 

namespace mio JavaScript assomiglia a questo, anche se io non migliorare su di essa ogni volta che costruire una nuova app:

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.                               
} 

tutto quello che voglio tutti i miei punti di vista di avere, ho messo nella vista di base. Il mio controller chiamerà registerEvents su qualsiasi nuova vista che crea (dopo il rendering) e unregisterEvents su una vista prima che la uccida. Non tutte le viste hanno questi due metodi aggiuntivi, quindi prima controlla l'esistenza.

Non dimenticare che tutte le viste sono dotate di un this.el.remove(); integrato. Che non solo uccide l'elemento contenitore di viste ma non associa tutti gli eventi ad esso associati. A seconda di come stai creando le tue viste attraverso il tuo controller, potresti non volere realmente uccidere l'elemento e farlo this.el.unbind() invece di separare tutti gli eventi.

+0

in questo caso non voglio uccidere l'elemento e fare this.el.unbind()? Ho un metodo simile a quello successivo: if (this.onClose) this.onClose(); $ (this.el) .remove(); $ (this.el) .unbind(); – ccsakuweb

+2

'.remove' fa' .unbind' automaticamente quindi dovresti essere bravo. –

5

Infatti, in modi diversi hanno vantaggi e svantaggi di modi diversi. La cosa più importante è trovare un modo adeguato di organizzare i file. Di seguito è l'organizzazione del progetto che sto facendo attualmente. In questo modo l'attenzione sarà gli stessi file relativi al modulo sono collocati in una cartella. Ad esempio: il modulo persone, questo modulo tutti i file sono collocati nella directory moduli/base/persone. Dopo l'aggiornamento e la manutenzione di questo modulo, è necessario concentrarsi solo sui file presenti in questa directory sulla linea, non interessare i file all'esterno della directory e migliorare la manutenibilità.

Spero che la mia risposta possa darti un aiuto, spero che tu abbia qualche consiglio prezioso.

enter image description here

+1

Anche io ho una cartella con i moduli, ma penso che ogni cartella dei moduli abbia bisogno della cartella dei modelli e della cartella delle viste almeno – ccsakuweb

+0

A cosa serve il context.js? hai github? – alejandro