2010-09-08 5 views
41

Wie organisieren Sie große JS/jQuery-Codebasen auf Ihrer gesamten Website? Es gibt viele gute Quellen, wie man Teile deines Codes organisiert, aber nichts wirklich darüber, wie man alles zusammenfügt und jedes Teil an Ort und Stelle bringt: Seitenweite Code-Organisation, mehrere Seiten mit dem gleichen Code, DRY bleiben mit loser Kopplung, usw.Wie organisieren Sie große JS/jQuery-Codebasen auf Ihrer gesamten Website?

Unten ist, wie ich damit umgehen. Ich habe es nie gemocht, meinen Code so zu organisieren, weil ich denke, dass es schlampig ist und zu Problemen bei der Wartbarkeit/Skalierung führen kann, aber ich weiß es nicht wirklich besser.

Ich weiß, dass jeder seine eigenen Anforderungen hat und es keine schlüsselfertigen Lösungen gibt, aber ich würde gerne einige Meinungen darüber hören, was ich falsch mache, WARUM ich es falsch mache, und Vorschläge dazu wie man mehr wartbaren Code schreibt.

Was ich denke, ich bin wirklich versucht, zu bekommen:

  1. Wie gehen Sie mit Logik beschäftigen, die Sie Bedarf an mehreren Stellen verwenden, auf mehrere Seiten?

  2. Wie organisieren Sie den seitenspezifischen Code ? Ist ein Namespacing jeder Seite in ein globales Objekt eine gute Idee? 1.

  3. Was tun Sie, von Anfang an zu sicherzustellen, dass Sie nicht ständig Umschreiben Ihrer Organisation Muster wie Ihre App größer und größer wird? Ich bin wahrscheinlich auf meiner 4. Iteration dieses Ding zu schreiben.2.

Jede Seite erhält die Hauptdatei application.js. Jede weitere Seite hat ihre eigene application.pagename.js Datei. Ich benutze serverseitige Logik, um die Dateien einzuschließen (erste Überprüfung, um zu sehen, wenn man sogar für die Seite existiert - einige Seiten brauchen JS nicht), und dann sie in der Reihenfolge anzustellen.

So meine Homepage wie folgt aussieht:

<script src="js/application.js"></script> 
<script src="js/application.index.js"></script> 
<script> 
    MyApp.init(); 
    MyApp.index.init(); 
</script> 

meine URL-Konvention ist/Seite/Unterseite/id /. Ich habe ungefähr 10 Seiten und eine ganze Reihe von Unterseiten, jede Unterseite erfordert ihre eigene Logik. siehe das letzte Beispiel in diesem Post.

Der Großteil meines Codes ist bereits entweder in jQuery UI-Widgets oder jQuery-Plugins modularisiert, also würde ich sagen, dass 75% des Codes in diesen Dateien ein Widget benötigt und es initiert.

Ich benutze requireJS, um Widgets nach Bedarf zu ziehen.

// application.js 
var MyApp = { 
    init: function(){ 
     var self = this; 

     // these widgets are available on every single page 
     // notice the call to jquery.deparam.js - i'll use this later to init subpage logic. 
     require(['js/widget1.js', 'js/widget2.js', 'js/widget3.js', 'js/jquery.deparam.js'], function(){ 

      // deparam the query string. I'll use this later. 
      self.querystring = $.deparam.querystring(); 

      // init widgets once the document is ready 
      $(function(){ 
       $("#widget1").widget1(); 
       $("#widget2").widget2(); 

       // make these bindings available immediately as well. 
       self.rebindable(); 
      }); 
     }); 
    }, 

    // I use jQuery UI Dialog extensively as a window manager, and each dialog is loaded 
    // via an AJAX request. I'll call this method after each AJAX request to 
    // rebind some key widgets. 
    rebindable: function(){ 
     $("#widget3").widget3(); 
    } 
}; 

// application.index.js 
// home page specific stuff. this file is only included on the home page. 
MyApp.index = { 

    // my convention is that init is automatically called after the script 
    // is included in a page, outside of a doc.ready statement. 
    init: function(){ 
     var self = this; 

     require(['js/widget4.js'], function(){ 
      $(function(){ 
       self.widget4($("#foo")); 
      }); 
     }); 
    }, 

    // passing elements to each method allows me to call this init code 
    // outside of the index page. I can require() this file, and only init 
    // widget4, and even use a different element. 
    widget4: function(element){ 
     var config = { 
      something: "custom to the home page" 
     }; 

     element.widget4(config); 
    } 
}; 


// application.foo.js 
// page "foo" stuff 
MyApp.foo = { 

    init: function(){ 
     var self = this; 

     // this page happens to use the same widget3 and configuration present 
     // in MyApp.index. this is where things can get sloppy and unmaintainable 
     // really quickly. 
     require(['js/application.index.js'], function(){ 
      $(function(){ 
       MyApp.index.widget3($("#bar")); 
      }); 
     }); 

     // page "foo" has three subpages (or actions) and require 
     // their own logic. url convention: /foo/subpage1/ 
     // init whichever page we're on... 
     switch(self.querystring.subpage){ 
      case "subpage1": 
       self.subpage1.init(); 
       break; 
      case "subpage2": 
       self.subpage2.init(); 
       break; 
      case "subpage3": 
       self.subpage3.init(); 
       break; 
     } 
    }, 

    subpage1: function(){ 
     init: function(){ 
      var self = this; 

      // once the DOM is ready init dialog. 
      $(function(){ 
       self.dialog($("#openDialog")); 
      }); 
     }, 

     dialog: function(element){ 
      element.bind("click", function(){ 
       $('<div></div>').dialog({ 
        open: function(){ 

         // see what i'm doing here? 
         MyApp.rebindable(); 

         // maybe more bindings specific to this 
         // dialog here 
        } 
       }); 
      }); 
     } 
    }, 

    subpage2: function(){ 
     init: function(){ 
     } 
    }, 

    subpage3: function(){ 
     init: function(){ 
     } 
    } 
}; 
+1

Ich habe diese Woche selbst darüber nachgedacht. Ich bin kürzlich einem Projektteam als Haupt-JavaScript-Codierer beigetreten. Das Projekt läuft schon seit über einem Jahr, also haben sie überall Javascript. Sie haben eine umfangreiche JavaScript-Bibliothek erstellt und einige Anstrengungen unternommen, um Namespaces zu verwenden, aber viele Seiten haben benutzerdefiniertes JavaScript, das nicht nur in Seiten und Benutzersteuerelementen geschrieben ist, sondern auch auf Seiten- oder Benutzersteuerungsdaten basiert. Ich habe die entmutigende Aufgabe, alles zu verstehen und so weit wie möglich zu standardisieren. – Silkster

+0

(Fortsetzung) Ihre Methode ist ein guter Anfang, aber ich kann sehen, wo es nicht erreichbar ist. Vielleicht können Sie eine Methode zum Konfigurieren von Seiten und Steuerelementen entwickeln, so dass Sie nicht viele Switch-Anweisungen verwenden müssen. Sie können auch Claypool (http://www.claypooljs.com/) ausprobieren, um zu sehen, ob das helfen könnte. – Silkster

+1

@Silkster der Link ist kaputt: http: //http//www.claypooljs.com/ –

Antwort

12

mir zu helfen, spezifische Fragen zu beantworten, sprechen Sie mir gestatten, ein wenig über JavaScriptMVC ‚s Features:

-Controller Ihre jQuery-Widgets verbessern, von Setup/Teardown kümmert, Erweiterbarkeit.

Anzeigen fügt Client-seitige Vorlagen hinzu, die in Ihre App integriert werden können.

Modell abstrahiert die Service-/Datenschicht, die JS-Änderungen minimiert und lokalisiert, wenn sich der Server ändert.

Steal macht Abhängigkeitsverwaltung, Komprimierung und Codebereinigung. Es nimmt sogar alle Ihre Skripte über alle Ihre Seiten hinweg auf, ermittelt gemeinsame Abhängigkeiten und kombiniert Skripte zu einer optimalen Nutzlast.

FuncUnit macht das Testen Ihrer Apps so einfach wie möglich.

DocumentJS ... na ja ... Dokumente Code

.

Jetzt auf Ihre speziellen Fragen:

Wie mit Logik an mehreren Stellen verwendet umgehen?

Ich benutze das Abhängigkeitsverwaltungssystem von StealJS, um die benötigte Funktionalität in meine Seite zu laden. Dependency Management ist bei Apps einer bestimmten Größe unbedingt erforderlich. RequireJS ist eine gute Wahl, wenn Sie es leicht bauen können.

Wie Sie Seite spezifischen Code

Seite spezifischen Code organisieren sollte so klein wie möglich sein. Es beinhaltet typischerweise das Laden von Abhängigkeiten und einen "MainController". Dieser Hauptcontroller konfiguriert die Seite so, dass sie den funktionalen/geschäftlichen Anforderungen dieser Seite entspricht. Es ist in der Regel als so etwas wie Namespaces:

App.Controllers.Main 

Wie stoppen Sie die gleichen Muster

Nun schreiben ich einen Rahmen schlage vor, mit den stabilen Muster für die Entwicklung hat. Halte deine Module/Plugins/Widgets so klein wie möglich (und testbar). Dies wird dazu führen, dass sich diese Teile viel weniger ändern.

Schließlich ....

Es scheint, Ihre größte Kampf Spannung zwischen:

  • gemeinsam genutzte Funktionalität
  • mehreren Seiten
  • rechtzeitige Ladezeiten

So Kommissionierung Ein solides Abhängigkeitsverwaltungstool ist sup äh kritisch. StealJS könnte Ihnen dabei helfen, sehr optimale Ladezeiten zu erhalten, aber Sie müssen aufgrund der größeren Anzahl von Seiten von der Standard-Ordnerorganisation von JavaScriptMVC abweichen.

RequireJS ist flexibler, aber Sie müssen eine Menge Dateien laden. Dies wird nicht nur langsam sein, sondern Sie werden auch viele große JS-Dateien erstellen, die nicht sehr organisiert sind.

Wenn Sie mit den Ladezeiten zufrieden sind und das Gefühl haben, dass Sie Code nicht in Dateien pressen, gehört er nicht dazu, Ihre aktuelle Lösung scheint zu funktionieren.

Ich denke, das Geheimnis der wartbaren Entwicklung ist, wie einfach Ihr System/Framework es Ihnen ermöglicht, Probleme zu isolieren. Es ist wichtig, deine App in kleinste Teile aufzuteilen. Außerdem sollten Sie diese Teile testen. Die Leute werden durch Nachdenken über die Funktionalität ihrer Seiten abgelenkt. Aber um die Entwicklung wirklich zu skalieren, brauchen Sie wirklich etwas, das es Ihnen ermöglicht, Ihre App in kleine Teile aufzuteilen, diese Teile einfach zu laden und die App irgendwie immer noch schnell in der Produktion laufen zu lassen.

+2

Um zu klären: RequireJS kann Laden Sie einzelne Dateien während der Entwicklung, aber es hat ein Optimierungstool (http://requirejs.org/docs/optimization.html), mit dem Sie Ihre Module in einer kleinen Menge von HTTP-Anfragen optimieren/gruppieren können. Das Optimierungstool kann auch in Dev verwendet werden, es gibt eine Möglichkeit, nur bestimmte Module auszuschließen, die Sie möglicherweise selbst debuggen möchten. – jrburke

+0

Ja, das war mein Fehler. RequireJS kann alle Skripte kombinieren. Aber ich denke nicht, dass es den automatischen Bruch zwischen mehreren Projekten stehlen kann. –

+0

"Dieser Hauptcontroller konfiguriert die Seite so, dass sie den funktionalen/geschäftlichen Anforderungen dieser Seite entspricht." Der Controller ist hier also der Haupttreiber der Logik, im Gegensatz zur serverseitigen Programmierung, bei der die Geschäftslogik im Modell beibehalten werden soll. –

Verwandte Themen