Allgemein anerkannte Best Practices für die Codeorganisation in JavaScript [geschlossen]


561

Da JavaScript-Frameworks wie jQuery clientseitige Webanwendungen umfangreicher und funktionaler machen, habe ich ein Problem festgestellt ...

Wie in aller Welt halten Sie das organisiert?

  • Platzieren Sie alle Ihre Handler an einem Ort und schreiben Sie Funktionen für alle Ereignisse?
  • Funktion / Klassen erstellen, um alle Ihre Funktionen einzuschließen?
  • Schreiben Sie wie verrückt und hoffen Sie nur, dass es zum Besten funktioniert?
  • Aufgeben und eine neue Karriere machen?

Ich erwähne jQuery, aber es ist wirklich jeder JavaScript-Code im Allgemeinen. Ich stelle fest, dass es schwieriger wird, die Skriptdateien zu verwalten oder das zu finden, wonach Sie suchen, wenn sich Zeilen für Zeilen häufen. Möglicherweise ist das größte Problem, das ich gefunden habe, dass es so viele Möglichkeiten gibt, dasselbe zu tun. Es ist schwer zu wissen, welches die derzeit allgemein anerkannte Best Practice ist.

Gibt es allgemeine Empfehlungen, wie Sie Ihre .js- Dateien am besten so schön und ordentlich wie den Rest Ihrer Anwendung halten können? Oder ist das nur eine Frage der IDE? Gibt es da draußen eine bessere Option?


BEARBEITEN

Diese Frage sollte sich mehr mit der Code-Organisation und nicht mit der Dateiorganisation befassen. Es gab einige wirklich gute Beispiele für das Zusammenführen von Dateien oder das Aufteilen von Inhalten.

Meine Frage lautet: Was ist die derzeit allgemein anerkannte Best-Practice-Methode zum Organisieren Ihres tatsächlichen Codes? Was ist Ihre Art oder sogar eine empfohlene Art, mit Seitenelementen zu interagieren und wiederverwendbaren Code zu erstellen, der nicht miteinander in Konflikt steht?

Einige Leute haben Namespaces aufgelistet, was eine gute Idee ist. Was sind andere Möglichkeiten, insbesondere mit Elementen auf der Seite umzugehen und den Code organisiert und ordentlich zu halten?


Jemand, der sich tatsächlich die Zeit genommen hat, über die Code-Organisation selbst zu sprechen, und nicht "nur", mit welchem ​​Tool er seine JS-Dateien verkettet und komprimiert: stackoverflow.com/questions/16736483/…
Adrien Be

Antworten:


183

Es wäre viel schöner, wenn in Javascript Namespaces eingebaut wären, aber ich finde, dass das Organisieren von Dingen wie Dustin Diaz, die hier beschrieben werden , mir sehr hilft.

var DED = (function() {

    var private_var;

    function private_method()
    {
        // do stuff here
    }

    return {
        method_1 : function()
            {
                // do stuff here
            },
        method_2 : function()
            {
                // do stuff here
            }
    };
})();

Ich habe verschiedene "Namespaces" und manchmal einzelne Klassen in separate Dateien eingefügt. Normalerweise beginne ich mit einer Datei und da eine Klasse oder ein Namespace groß genug wird, um dies zu rechtfertigen, trenne ich sie in eine eigene Datei. Die Verwendung eines Tools zum Kombinieren aller Dateien für die Produktion ist ebenfalls eine hervorragende Idee.


24
Normalerweise bezeichne ich das als "Crockford Way". +1 von mir
Matt Briggs

4
Sie können sogar noch ein bisschen weiter gehen. Siehe diesen Link: wait-till-i.com/2007/08/22/…
MKroehnert

4
@MattBriggs heißt sonst das module patternund basiert auf dem IIFE pattern.
Adrien Be

Müssen Sie Klassen nicht irgendwie exportieren? Wie wird ein Objekt von außerhalb eines solchen Moduls erstellt? Oder sollte createNewSomething()das Rückgabeobjekt eine Methode enthalten , sodass die Objekterstellung ausschließlich innerhalb des Moduls erfolgt? Hm ... Ich würde erwarten, dass Klassen (Konstruktoren) von außen sichtbar sind.
Robsch

@robsch Sein Beispiel nimmt keine Parameter an, aber die meisten würden. In meinem Beispiel hier erfahren
Sie,

88

Ich versuche zu vermeiden, dass Javascript in den HTML-Code aufgenommen wird. Der gesamte Code ist in Klassen eingekapselt und jede Klasse befindet sich in einer eigenen Datei. Für die Entwicklung habe ich separate <script> -Tags, um jede js-Datei einzuschließen, aber sie werden für die Produktion zu einem einzigen größeren Paket zusammengeführt, um den Overhead der HTTP-Anforderungen zu verringern.

Normalerweise habe ich für jede Anwendung eine einzige 'Haupt'-JS-Datei. Wenn ich also eine "Umfrage" -Anwendung schreiben würde, hätte ich eine JS-Datei mit dem Namen "Umfrage.js". Dies würde den Einstiegspunkt in den jQuery-Code enthalten. Ich erstelle während der Instanziierung jQuery-Referenzen und übergebe sie dann als Parameter an meine Objekte. Dies bedeutet, dass die Javascript-Klassen "rein" sind und keine Verweise auf CSS-IDs oder Klassennamen enthalten.

// file: survey.js
$(document).ready(function() {
  var jS = $('#surveycontainer');
  var jB = $('#dimscreencontainer');
  var d = new DimScreen({container: jB});
  var s = new Survey({container: jS, DimScreen: d});
  s.show();
});

Ich finde auch Namenskonventionen wichtig für die Lesbarkeit. Zum Beispiel: Ich stelle allen jQuery-Instanzen 'j' voran.

Im obigen Beispiel gibt es eine Klasse namens DimScreen. (Angenommen, dies dimmt den Bildschirm ab und öffnet ein Warnfeld.) Es benötigt ein div-Element, das vergrößert werden kann, um den Bildschirm abzudecken, und fügt dann ein Warnfeld hinzu, sodass ich ein jQuery-Objekt übergebe. jQuery hat ein Plug-In-Konzept, aber es schien einschränkend (z. B. Instanzen sind nicht persistent und können nicht aufgerufen werden) ohne wirklichen Vorteil. Die DimScreen-Klasse wäre also eine Standard-Javascript-Klasse, die zufällig jQuery verwendet.

// file: dimscreen.js
function DimScreen(opts) { 
   this.jB = opts.container;
   // ...
}; // need the semi-colon for minimizing!


DimScreen.prototype.draw = function(msg) {
  var me = this;
  me.jB.addClass('fullscreen').append('<div>'+msg+'</div>');
  //...
};

Ich habe einige ziemlich komplexe Anwendungen mit diesem Ansatz erstellt.


15
Ich finde, dass die Verwendung $als Variablennamenpräfix eine üblichere Praxis ist, aber ich könnte mich irren. Also, $s = $('...')anstatt jS = $('...')nur eine Frage der Präferenz, denke ich. Interessant, da die ungarische Notation als Codegeruch angesehen wird. Es ist seltsam, wie unterschiedlich einige meiner JavaScript-Codekonventionen / -einstellungen von meinen C # / Java-Codierungskonventionen sind.
Jamie Barrow

9
@jamie In diesem Fall ist es kein Code-Geruch, es ist genau einer der wenigen Fälle, in denen Ungarisch gut ist . Vielleicht möchten Sie dies lesen .
Dan Abramov

3
@ DanAbramov danke für den Link. Ich muss wirklich alle Blogs von Joel lesen, er erklärt die Dinge so gut. Verdient definitiv den Ruhm / Ruf, den er hat. Ich werde von nun an Systems Hungarianals Code-Geruch und Apps Hungarianals Übung bezeichnen :)
Jamiebarrow

Ich denke, in der C # -Welt könnte es auch ein großartiger Artikel sein, um die Verwendung von zu fördern var, jetzt wo ich darüber nachdenke. Bei den meisten Argumenten gegen die Verwendung varsind Sie sich nicht sicher, welchen Typ die Rückgabe hat, aber ich denke, das Argument sollte eher dagegen sein, die Klasse der zurückgegebenen Daten nicht zu kennen. Wenn Sie Apps Hungarian verwenden, sollten Sie diese Bedenken nicht haben ... interessant.
Jamie Barrow

3
@Marnen: Ich verstehe Ihren Standpunkt, aber er ist als Leitfaden für den Programmierer nicht nutzlos. Das Präfix $ erinnert mich daran, was es ist, wenn ich meinen Code später lese, und hilft so beim schnelleren Verständnis.
Sean

39

Sie können Ihre Skripte für die Entwicklung in separate Dateien aufteilen und dann eine "Release" -Version erstellen, in der Sie sie alle zusammen stopfen und YUI Compressor oder ähnliches ausführen .


Manchmal gibt es nicht benötigte Javascript-Skripte. Es ist verschwenderisch, diese an den Kunden zu senden. Ich denke, es ist wichtig, nur das zu senden, was benötigt wird. Natürlich ist es für eine Web-App, die den ganzen Tag verwendet wird, wie z. B. eine Intranet-App, möglicherweise besser, den gesamten Stapel beim Laden der ersten Seite auf einmal zu senden.
DOK

2
Die @ DOK-Kompilierung sollte die Entfernung nicht verwendeter Inhalte beinhalten.
Aehlke

Es gibt auch ein Konzept des verzögerten Ladens, um den Bandbreitenbedarf zu reduzieren, indem Sie die erste Seite laden und dann die erforderlichen Skriptdateien asynchron laden (wie in anderen Antworten auf diese Frage erwähnt). Dies erfordert jedoch möglicherweise mehr Anforderungen und ist möglicherweise weniger verwendbar. @DOK, wenn der JS zwischengespeichert ist, ist eine mittlere Anforderung möglicherweise besser als einige kleine.
Jamie Barrow

27

Inspiriert von früheren Beiträgen habe ich eine Kopie von Rakefile- und Vendor- Verzeichnissen erstellt, die mit WysiHat verteilt wurden (eine von Changelog erwähnte RTE), und einige Änderungen vorgenommen, um die Codeprüfung mit JSLint und die Minimierung mit YUI Compressor einzuschließen .

Die Idee ist, Sprockets (von WysiHat) zu verwenden, um mehrere JavaScripts in einer Datei zusammenzuführen, die Syntax der zusammengeführten Datei mit JSLint zu überprüfen und sie vor der Verteilung mit YUI Compressor zu minimieren.

Voraussetzungen

  • java Laufzeit
  • Rubin und Rechen Edelstein
  • Sie sollten wissen, wie Sie eine JAR in Classpath einfügen

Mach jetzt

  1. Laden Sie Rhino herunter und legen Sie das JAR ("js.jar") in Ihren Klassenpfad
  2. Laden Sie YUI Compressor herunter und fügen Sie die JAR (build / yuicompressor-xyz.jar) in Ihren Klassenpfad ein
  3. Laden Sie WysiHat herunter und kopieren Sie das Verzeichnis "vendor" in das Stammverzeichnis Ihres JavaScript-Projekts
  4. Laden Sie JSLint for Rhino herunter und legen Sie es im Verzeichnis "vendor" ab

Erstellen Sie nun eine Datei mit dem Namen "Rakefile" im Stammverzeichnis des JavaScript-Projekts und fügen Sie den folgenden Inhalt hinzu:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

Wenn Sie alles richtig gemacht haben, sollten Sie in der Lage sein, die folgenden Befehle in Ihrer Konsole zu verwenden:

  • rake merge - um verschiedene JavaScript-Dateien zu einer zusammenzuführen
  • rake check- um die Syntax Ihres Codes zu überprüfen (dies ist die Standardaufgabe , sodass Sie einfach eingeben können rake)
  • rake minify - um eine minimierte Version Ihres JS-Codes vorzubereiten

Beim Zusammenführen der Quelle

Mit Sprockets, dem JavaScript-Vorprozessor, können Sie requireandere JavaScript-Dateien einschließen (oder ). Verwenden Sie die folgende Syntax, um andere Skripte aus der ursprünglichen Datei (mit dem Namen "main.js", aber Sie können dies in der Rake-Datei ändern) einzuschließen:

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

Und dann...

Schauen Sie sich Rakefile an, das mit WysiHat geliefert wurde, um die automatisierten Unit-Tests einzurichten. Gutes Zeug :)

Und jetzt zur Antwort

Dies beantwortet die ursprüngliche Frage nicht sehr gut. Ich weiß es und es tut mir leid, aber ich habe es hier gepostet, weil ich hoffe, dass es für jemand anderen nützlich sein kann, sein Chaos zu organisieren.

Mein Ansatz für das Problem besteht darin, so viel objektorientierte Modellierung wie möglich durchzuführen und Implementierungen in verschiedene Dateien aufzuteilen. Dann sollten die Handler so kurz wie möglich sein. Das Beispiel mit ListSingleton ist auch schön.

Und Namespaces ... nun, sie können durch eine tiefere Objektstruktur imitiert werden.

if (typeof org === 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

Ich bin kein großer Fan von Imitationen, aber dies kann hilfreich sein, wenn Sie viele Objekte haben, die Sie aus dem globalen Bereich entfernen möchten.


18

Die Code-Organisation erfordert die Annahme von Konventionen und Dokumentationsstandards:
1. Namespace-Code für eine physische Datei;

Exc = {};


2. Gruppenklassen in diesen Namespaces Javascript;
3. Legen Sie Prototypen oder verwandte Funktionen oder Klassen für die Darstellung realer Objekte fest.

Exc = {};
Exc.ui = {};
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};
Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    ...
};


4. Legen Sie Konventionen fest, um den Code zu verbessern. Gruppieren Sie beispielsweise alle internen Funktionen oder Methoden in dem Klassenattribut eines Objekttyps.

Exc.ui.domTips = function (dom, tips) {
    this.dom = gift;
    this.tips = tips;
    this.internal = {
        widthEstimates: function (tips) {
            ...
        }
        formatTips: function () {
            ...
        }
    };
    ...
};


5. Dokumentieren Sie Namespaces, Klassen, Methoden und Variablen. Besprechen Sie bei Bedarf auch einen Teil des Codes (einige FIs und Fors implementieren normalerweise eine wichtige Logik des Codes).

/**
  * Namespace <i> Example </i> created to group other namespaces of the "Example".  
  */
Exc = {};
/**
  * Namespace <i> ui </i> created with the aim of grouping namespaces user interface.
  */
Exc.ui = {};

/**
  * Class <i> maskdInput </i> used to add an input HTML formatting capabilities and validation of data and information.
  * @ Param {String} mask - mask validation of input data.
  */
Exc.ui.maskedInput = function (mask) {
    this.mask = mask;
    ...
};

/**
  * Class <i> domTips </i> used to add an HTML element the ability to present tips and information about its function or rule input etc..
  * @ Param {String} id - id of the HTML element.
  * @ Param {String} tips - tips on the element that will appear when the mouse is over the element whose identifier is id <i> </i>.
  */
  Exc.ui.domTips = function (id, tips) {
    this.domID = id;
    this.tips = tips;
    ...
};


Dies sind nur einige Tipps, aber das hat bei der Organisation des Codes sehr geholfen. Denken Sie daran, Sie müssen Disziplin haben, um erfolgreich zu sein!


13

Das Befolgen guter OO-Designprinzipien und Designmuster trägt wesentlich dazu bei, dass Ihr Code einfach zu warten und zu verstehen ist. Aber eines der besten Dinge, die ich in letzter Zeit entdeckt habe, sind Signale und Slots, auch bekannt als Publish / Subscribe. Werfen Sie einen Blick auf http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html für eine einfache jQuery - Implementierung.

Die Idee wird in anderen Sprachen für die GUI-Entwicklung gut verwendet. Wenn irgendwo in Ihrem Code etwas Bedeutendes passiert, veröffentlichen Sie ein globales synthetisches Ereignis, das andere Methoden in anderen Objekten möglicherweise abonnieren. Dies ergibt eine ausgezeichnete Trennung von Objekten.

Ich denke, Dojo (und Prototype?) Haben eine eingebaute Version dieser Technik.

Siehe auch Was sind Signale und Slots?


Ich habe das in jQuery gemacht. JS verfügt über ein integriertes Ereignismodell, sodass Sie nicht wirklich viel Framework-Unterstützung benötigen.
Marnen Laibow-Koser

12

Ich konnte das Javascript-Modulmuster bei meinem vorherigen Job erfolgreich auf eine Ext JS-Anwendung anwenden . Es bot eine einfache Möglichkeit, gut gekapselten Code zu erstellen.


11

Dojo hatte das Modulsystem vom ersten Tag an. Tatsächlich wird es als Eckpfeiler von Dojo angesehen, dem Klebstoff, der alles zusammenhält:

Verwendung von Modulen Dojo erreicht folgende Ziele:

  • Namespaces für Dojo-Code und benutzerdefinierten Code (dojo.declare() ) - Verschmutzen Sie nicht den globalen Speicherplatz, koexistieren Sie nicht mit anderen Bibliotheken und dem nicht Dojo-fähigen Code des Benutzers.
  • Laden von Modulen synchron oder asynchron nach Namen (dojo.require() ).
  • Benutzerdefinierte Builds durch Analyse der Modulabhängigkeiten, um eine einzelne Datei oder eine Gruppe von voneinander abhängigen Dateien (sogenannte Layer) zu erstellen, die nur die Anforderungen Ihrer Webanwendung enthalten. Benutzerdefinierte Builds können auch Dojo-Module und vom Kunden bereitgestellte Module enthalten.
  • Transparenter CDN-basierter Zugriff auf Dojo und Benutzercode. Sowohl AOL als auch Google führen Dojo auf diese Weise, aber einige Kunden tun dies auch für ihre benutzerdefinierten Webanwendungen.

9

Schauen Sie sich JavasciptMVC an .

Du kannst :

  • Teilen Sie Ihren Code in Modell-, Ansichts- und Controller-Ebenen auf.

  • Komprimieren Sie den gesamten Code in eine einzige Produktionsdatei

  • Code automatisch generieren

  • Erstellen und Ausführen von Komponententests

  • und vieles mehr...

Das Beste ist, dass jQuery verwendet wird, sodass Sie auch andere jQuery-Plugins nutzen können.


Ja, ich habe jmvc verwendet und es ist ziemlich gut - docs könnten aber besser sein
meouw

9

Mein Chef spricht immer noch von den Zeiten, als sie modularen Code (C-Sprache) geschrieben haben, und beschwert sich darüber, wie beschissen der Code heutzutage ist! Es wird gesagt, dass Programmierer Assembly in jedem Framework schreiben können. Es gibt immer eine Strategie, um die Code-Organisation zu überwinden. Das Grundproblem ist bei Leuten, die Java-Skript als Spielzeug behandeln und nie versuchen, es zu lernen.

In meinem Fall schreibe ich js-Dateien auf der Basis eines UI-Themas oder eines Anwendungsbildschirms mit einem geeigneten init_screen (). Mit der richtigen ID-Namenskonvention stelle ich sicher, dass auf der Ebene der Stammelemente keine Namensraumkonflikte auftreten. In der unauffälligen window.load () binde ich die Dinge basierend auf der ID der obersten Ebene zusammen.

Ich verwende ausschließlich Java-Skriptabschlüsse und -muster, um alle privaten Methoden zu verbergen. Danach gab es nie ein Problem mit widersprüchlichen Eigenschaften / Funktionsdefinitionen / Variablendefinitionen. Bei der Arbeit mit einem Team ist es jedoch oft schwierig, die gleiche Strenge durchzusetzen.


9

Ich bin überrascht, dass niemand MVC-Frameworks erwähnt hat. Ich habe Backbone.js verwendet , um meinen Code zu modularisieren und zu entkoppeln, und er war von unschätzbarem Wert.

Es gibt einige dieser Arten von Frameworks, und die meisten von ihnen sind auch ziemlich klein. Meine persönliche Meinung ist, dass ein MVC-Framework Ihr Leben viel einfacher macht, wenn Sie mehr als nur ein paar Zeilen jQuery für auffällige UI-Inhalte schreiben oder eine umfangreiche Ajax-Anwendung wünschen.


8

"Schreiben Sie wie verrückt und hoffen Sie nur, dass es zum Besten funktioniert?", Habe ich ein Projekt wie dieses gesehen, das von nur 2 Entwicklern entwickelt und gepflegt wurde, eine riesige Anwendung mit viel Javascript-Code. Darüber hinaus gab es verschiedene Verknüpfungen für jede mögliche jquery-Funktion, die Sie sich vorstellen können. Ich schlug vor, den Code als Plugins zu organisieren, da dies das jquery-Äquivalent von Klasse, Modul, Namespace ... und dem gesamten Universum ist. Aber es wurde noch schlimmer, jetzt fingen sie an, Plugins zu schreiben, die jede Kombination von 3 im Projekt verwendeten Codezeilen ersetzten. Persönlich denke ich, dass jQuery der Teufel ist und nicht für Projekte mit viel Javascript verwendet werden sollte, da es Sie dazu ermutigt, faul zu sein und nicht daran zu denken, Code in irgendeiner Weise zu organisieren. Ich würde lieber 100 Zeilen Javascript lesen als eine Zeile mit 40 verketteten jQuery-Funktionen (I ' Ich mache keine Witze. Entgegen der landläufigen Meinung ist es sehr einfach, Javascript-Code in Entsprechungen zu Namespaces und Klassen zu organisieren. Das machen YUI und Dojo. Sie können ganz einfach Ihre eigenen rollen, wenn Sie möchten. Ich finde den Ansatz von YUI viel besser und effizienter. Normalerweise benötigen Sie jedoch einen netten Editor mit Unterstützung für Snippets, um YUI-Namenskonventionen zu kompensieren, wenn Sie etwas Nützliches schreiben möchten.


3
Ich würde Ihnen in Bezug auf wirklich lange, verkettete Befehle zustimmen, aber einer der besten Teile von jQuery ist, dass Javascript nicht in HTML enthalten ist. Sie können Ereignishandler für alle Ihre Elemente einrichten, ohne IDs hinzufügen zu müssen, oder für <was auch immer> -Ereignisse für Ihre Elemente. Wie immer ist die
Überbeanspruchung

Ich habe an großen, gut organisierten Projekten in jQuery gearbeitet. Ich weiß nicht, warum Sie denken, dass es der Organisation im Wege steht.
Marnen Laibow-Koser

7

Ich erstelle Singletons für alles, was ich wirklich nicht mehrmals auf dem Bildschirm instanziieren muss, eine Klasse für alles andere. Und alle von ihnen werden in den gleichen Namespace in der gleichen Datei gestellt. Alles wird kommentiert und mit UML-Zustandsdiagrammen gestaltet. Der Javascript-Code ist frei von HTML, daher gibt es kein Inline-Javascript, und ich verwende JQuery, um browserübergreifende Probleme zu minimieren.


3
Gutes Kommentieren ist der Schlüssel - ich bin froh, dass du es gesagt hast, damit ich es nicht musste. Ich würde konsistente Namenskonventionen hinzufügen, eine Art leicht verständliche Organisationsstrategie für Variablen & amp; Funktionen und, wie Sie bereits erwähnt haben, eine umsichtige Verwendung von Klassen im Vergleich zu Singletons.
Matt Lohkamp

Nein. Wenn Sie Kommentare benötigen, ist Ihr Code im Allgemeinen nicht lesbar genug. Bemühen Sie sich, Code zu schreiben, der keine Kommentare benötigt.
Marnen Laibow-Koser

Wenn Sie UML- und Statusdiagramme benötigen, bedeutet dies wahrscheinlich, dass Ihre Architektur aus dem Code nicht klar genug ist. Downvoting.
Marnen Laibow-Koser

1
@Marnen Gut geschriebene Projekte enthalten Kommentare, die beschreiben, WARUM, nicht unbedingt WAS. Der Code beschreibt bereits das WAS, aber oft brauchen Sie etwas, um das WARUM zu beschreiben. Upvoting.
Cypher

@Cypher Gut geschriebene Projekte haben klar genug Code, dass Sie normalerweise das "Warum" und nicht nur das "Was" erkennen können. Ich würde einem Kommentar nicht trauen, der mir das "Warum" sagt, da ich keine Garantie dafür habe, dass er mit dem Code synchronisiert ist. Lassen Sie den Code selbst dokumentieren.
Marnen Laibow-Koser

6

In meinem letzten Projekt -Viajeros.com- habe ich eine Kombination mehrerer Techniken verwendet. Ich würde nicht wissen, wie man eine Web-App organisiert - Viajeros ist eine Social-Networking-Site für Reisende mit genau definierten Abschnitten, sodass es einfach ist, den Code für jeden Bereich zu trennen.

Ich verwende die Namespace-Simulation und das verzögerte Laden von Modulen gemäß dem Site-Abschnitt. Bei jedem Laden einer Seite deklariere ich ein "vjr" -Objekt und lade immer eine Reihe allgemeiner Funktionen (vjr.base.js). Dann entscheidet jede HTML-Seite mit einem einfachen: Welche Module benötigt werden?

vjr.Required = ["vjr.gallery", "vjr.comments", "vjr.favorites"];

Vjr.base.js ruft jeden vom Server ab und führt ihn aus.

vjr.include(vjr.Required);
vjr.include = function(moduleList) {
  if (!moduleList) return false;
  for (var i = 0; i < moduleList.length; i++) {
    if (moduleList[i]) {
      $.ajax({
        type: "GET", url: vjr.module2fileName(moduleList[i]), dataType: "script"
      });
    }
  }
};

Jedes "Modul" hat diese Struktur:

vjr.comments = {}

vjr.comments.submitComment = function() { // do stuff }
vjr.comments.validateComment = function() { // do stuff }

// Handlers
vjr.comments.setUpUI = function() {
    // Assign handlers to screen elements
}

vjr.comments.init = function () {
  // initialize stuff
    vjr.comments.setUpUI();
}

$(document).ready(vjr.comments.init);

Aufgrund meiner begrenzten Javascript-Kenntnisse weiß ich, dass es bessere Möglichkeiten geben muss, dies zu verwalten, aber bis jetzt funktioniert es hervorragend für uns.


6

Das Organisieren Ihres Codes in einer Jquery-zentrierten NameSpace-Methode sieht möglicherweise wie folgt aus ... und steht auch nicht in Konflikt mit anderen Javascript-APIs wie Prototype, Ext.

<script src="jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script type="text/javascript">

var AcmeJQ = jQuery.noConflict(true);
var Acme = {fn: function(){}};

(function($){

    Acme.sayHi = function()
    {
        console.log('Hello');
    };

    Acme.sayBye = function()
    {
        console.log('Good Bye');
    };
})(AcmeJQ);

// Usage
//          Acme.sayHi();
// or
// <a href="#" onclick="Acme.sayHi();">Say Hello</a>


</script>

Hoffe das hilft.


Das kommt mir wie eine kleine Frachtkult vor. jQuery.fnist ein Zeiger auf jQuery.prototype, da $()tatsächlich eine neue Instanz der jQuery-Konstruktorfunktion zurückgegeben wird. Wenn Sie jQuery ein Plugin hinzufügen, müssen Sie lediglich den Prototyp erweitern. Aber was Sie tun, ist nicht das, und es gibt sauberere Wege, um dasselbe zu erreichen.
Adam Lassek

Ich glaube, er erstellt einfach statische Funktionen. Ich erinnere mich, dass ich in jQuerys Dokumenten gesehen habe, dass diese Art der Deklaration statischer Funktionen akzeptabel ist
Alex Heyd

6

Ein guter Principal von OO + MVC würde definitiv einen großen Beitrag zur Verwaltung einer komplexen Javascript-App leisten.

Grundsätzlich organisiere ich meine App und mein Javascript nach dem folgenden bekannten Design (das von meinen Desktop-Programmiertagen bis zum Web 2.0 existiert).

JS OO und MVC

Beschreibung für die numerischen Werte auf dem Bild:

  1. Widgets, die die Ansichten meiner Anwendung darstellen. Dies sollte erweiterbar und ordentlich getrennt sein, was zu einer guten Trennung führt, die MVC zu erreichen versucht, anstatt mein Widget in einen Spaghetti-Code umzuwandeln (entspricht in der Web-App dem Einfügen eines großen Blocks Javascript direkt in HTML). Jedes Widget kommuniziert über andere, indem es das von anderen Widgets erzeugte Ereignis abhört, wodurch die starke Kopplung zwischen Widgets verringert wird, die zu nicht verwaltbarem Code führen kann (erinnern Sie sich an den Tag, an dem überall ein Klick hinzugefügt wurde, der auf globale Funktionen im Skript-Tag verweist? Urgh ...)
  2. Objektmodelle, die die Daten darstellen, die ich in die Widgets einfügen und an den Server weiterleiten möchte. Durch die Kapselung der Daten in ihr Modell wird die Anwendung zur Datenformatunabhängigkeit. Beispiel: Während diese Objektmodelle in Javascript natürlich meistens serialisiert und in JSON deserialisiert werden, muss ich nur die Serialisierungs- / Deserialisierungsschicht ändern und nicht unbedingt alle Widget-Klassen ändern, wenn der Server XML für die Kommunikation verwendet .
  3. Controller-Klassen, die die Geschäftslogik und die Kommunikation mit dem Server verwalten + gelegentlich die Caching-Schicht. Diese Schicht steuert das Kommunikationsprotokoll zum Server und fügt die erforderlichen Daten in die Objektmodelle ein
  4. Klassen werden ordentlich in die entsprechenden Namespaces eingeschlossen. Ich bin sicher, wir alle wissen, wie unangenehm der globale Namespace in Javascript sein kann.

In der Vergangenheit habe ich die Dateien in eigene js getrennt und die gängige Praxis verwendet, um OO-Prinzipien in Javascript zu erstellen. Das Problem, dass ich bald feststellte, dass es mehrere Möglichkeiten gibt, JS OO zu schreiben, und dass nicht unbedingt alle Teammitglieder den gleichen Ansatz verfolgen. Wenn das Team größer wird (in meinem Fall mehr als 15 Personen), wird dies kompliziert, da es keinen Standardansatz für objektorientiertes Javascript gibt. Gleichzeitig möchte ich nicht mein eigenes Framework schreiben und einige der Arbeiten wiederholen, von denen ich sicher bin, dass sie klüger sind, als ich gelöst habe.

jQuery ist unglaublich gut als Javascript Framework und ich liebe es, aber wenn das Projekt größer wird, brauche ich eindeutig eine zusätzliche Struktur für meine Web-App, insbesondere um die Standardisierung der OO-Praxis zu erleichtern. Nach mehreren Experimenten stelle ich fest, dass die Infrastruktur von YUI3 Base und Widget ( http://yuilibrary.com/yui/docs/widget/ und http://yuilibrary.com/yui/docs/base/index.html ) zur Verfügung steht genau das was ich brauche. Einige Gründe, warum ich sie benutze.

  1. Es bietet Namespace-Unterstützung. Ein echtes Bedürfnis nach OO und ordentlicher Organisation Ihres Codes
  2. Es unterstützt die Vorstellung von Klassen und Objekten
  3. Es bietet eine standardisierte Möglichkeit, Ihrer Klasse Instanzvariablen hinzuzufügen
  4. Es unterstützt die Klassenerweiterung ordentlich
  5. Es bietet Konstruktor und Destruktor
  6. Es bietet Render- und Ereignisbindung
  7. Es hat Basis-Widget-Framework
  8. Jedes Widget kann jetzt mithilfe eines ereignisbasierten Standardmodells miteinander kommunizieren
  9. Am wichtigsten ist, dass alle Ingenieure einen OO-Standard für die Javascript-Entwicklung erhalten

Im Gegensatz zu vielen Ansichten muss ich mich nicht unbedingt zwischen jQuery und YUI3 entscheiden. Diese beiden können friedlich nebeneinander existieren. Während YUI3 die erforderliche OO-Vorlage für meine komplexe Web-App bereitstellt, bietet jQuery meinem Team dennoch eine benutzerfreundliche JS-Abstraktion, die wir alle lieben und mit der wir alle vertraut sind.

Mit YUI3 habe ich es geschafft, ein MVC-Muster zu erstellen, indem Klassen getrennt wurden, die die Basis als Modell erweitern, Klassen, die Widget als Ansicht erweitern, und natürlich Controller-Klassen, die die erforderlichen logischen und serverseitigen Aufrufe ausführen.

Widget kann mithilfe eines ereignisbasierten Modells miteinander kommunizieren, das Ereignis abhören und die erforderliche Aufgabe basierend auf einer vordefinierten Schnittstelle ausführen. Einfach ausgedrückt, es ist eine Freude für mich, JS eine OO + MVC-Struktur zuzuweisen.

Nur ein Haftungsausschluss, ich arbeite nicht für Yahoo! und einfach ein Architekt, der versucht, mit dem gleichen Problem umzugehen, das sich aus der ursprünglichen Frage ergibt. Ich denke, wenn jemand ein gleichwertiges OO-Framework findet, würde dies auch funktionieren. Grundsätzlich gilt diese Frage auch für andere Technologien. Gott sei Dank für all die Leute, die sich OO Principles + MVC ausgedacht haben, um unsere Programmiertage überschaubarer zu gestalten.


5

Ich verwende Dojos Paketverwaltung ( dojo.requireund dojo.provide) und Klassensystem ( dojo.declaredas auch eine einfache Mehrfachvererbung ermöglicht), um alle meine Klassen / Widgets in separate Dateien zu modularisieren. Dies sorgt nicht nur dafür, dass Ihr Code organisiert bleibt, sondern ermöglicht es Ihnen auch, Klassen / Widgets faul / just in time zu laden.


3

Vor ein paar Tagen haben die Jungs von 37Signals eine RTE-Steuerung mit einem Twist veröffentlicht. Sie erstellten eine Bibliothek, die Javascript-Dateien mit einer Art Vorprozessorbefehlen bündelt.

Ich benutze es seitdem, um meine JS-Dateien zu trennen und sie am Ende als eine zusammenzuführen. Auf diese Weise kann ich Bedenken trennen und am Ende nur eine Datei haben, die durch die Pipe geht (komprimiert, nicht weniger).

Überprüfen Sie in Ihren Vorlagen, ob Sie sich im Entwicklungsmodus befinden, und fügen Sie die separaten Dateien hinzu. Wenn Sie sich in der Produktion befinden, fügen Sie die endgültige Datei hinzu (die Sie selbst "erstellen" müssen).


1
getsprockets.org ist der direkte Link
Matt Gardner

3

Erstellen Sie gefälschte Klassen und stellen Sie sicher, dass alles, was in eine separate, sinnvolle Funktion geworfen werden kann, getan wird. Achten Sie auch darauf, viel zu kommentieren und keinen Spagghetti-Code zu schreiben, sondern alles in Abschnitten zu halten. Zum Beispiel ein Unsinnscode, der meine Ideale darstellt. Natürlich schreibe ich im wirklichen Leben auch viele Bibliotheken, die im Grunde genommen ihre Funktionalität umfassen.

$(function(){
    //Preload header images
    $('a.rollover').preload();

    //Create new datagrid
    var dGrid = datagrid.init({width: 5, url: 'datalist.txt', style: 'aero'});
});

var datagrid = {
    init: function(w, url, style){
        //Rendering code goes here for style / width
        //code etc

        //Fetch data in
        $.get(url, {}, function(data){
            data = data.split('\n');
            for(var i=0; i < data.length; i++){
                //fetching data
            }
        })
    },
    refresh: function(deep){
        //more functions etc.
    }
};


2

Ich denke, das hängt vielleicht mit DDD (Domain-Driven Design) zusammen. Die Anwendung, an der ich arbeite, hat zwar keine formale API, gibt jedoch über den serverseitigen Code (Klassen- / Dateinamen usw.) Hinweise darauf. Aus diesem Grund habe ich ein Objekt der obersten Ebene als Container für die gesamte Problemdomäne erstellt. Dann habe ich bei Bedarf Namespaces hinzugefügt:

var App;
(function()
{
    App = new Domain( 'test' );

    function Domain( id )
    {
        this.id = id;
        this.echo = function echo( s )
        {
            alert( s );
        }
        return this;
    }
})();

// separate file
(function(Domain)
{
    Domain.Console = new Console();

    function Console()
    {
        this.Log = function Log( s )
        {
            console.log( s );
        }
        return this;
    }
})(App);

// implementation
App.Console.Log('foo');

2

Für die JavaScript-Organisation wurde Folgendes verwendet

  1. Ordner für all Ihr Javascript
  2. Javascript auf Seitenebene erhält eine eigene Datei mit demselben Namen wie die Seite. ProductDetail.aspx wäre ProductDetail.js
  3. Im Javascript-Ordner für Bibliotheksdateien habe ich einen lib-Ordner
  4. Legen Sie verwandte Bibliotheksfunktionen in einem lib-Ordner ab, den Sie in Ihrer gesamten Anwendung verwenden möchten.
  5. Ajax ist das einzige Javascript, das ich außerhalb des Javascript-Ordners verschiebe und einen eigenen Ordner erhalte. Dann füge ich zwei Unterordner Client und Server hinzu
  6. Der Client-Ordner ruft alle .js-Dateien ab, während der Server-Ordner alle serverseitigen Dateien abruft.

Schön für die Dateiorganisation. Ich mache das mit Code. Aber am Ende kompiliere ich meinen Code in einer ... sagen wir dll. Sie benötigen dies auch mit Javascript, oder Sie fordern am Ende 15 JS-Dateien pro Seite an.
Graffic

Es ist nichts Falsches daran, 15 JS-Dateien pro Seite anzufordern. Ihr Browser wird sie trotzdem für nachfolgende Anfragen zwischenspeichern.
Marnen Laibow-Koser

@ MarnenLaibow-Koser Das einzige Problem beim Anfordern von 15 JS-Dateien auf einer Seite ist, wie viele HTTP-Anforderungen der Browser gleichzeitig verarbeiten kann. Wenn Sie sie also in einer Datei bündeln, kann der Browser gleichzeitig andere erforderliche Dateien anfordern.
Ich wurde

Das stimmt, aber nach den ersten Treffern befinden sie sich im Cache des Browsers, sodass keine HTTP-Verbindungen erforderlich sind.
Marnen Laibow-Koser

2

Ich benutze dieses kleine Ding. Sie erhalten die Include-Direktive für JS- und HTML-Vorlagen. Es beseitigt das Chaos vollständig.

https://github.com/gaperton/include.js/

$.include({
    html: "my_template.html" // include template from file...
})
.define( function( _ ){ // define module...
    _.exports = function widget( $this, a_data, a_events ){ // exporting function...
        _.html.renderTo( $this, a_data ); // which expands template inside of $this.

        $this.find( "#ok").click( a_events.on_click ); // throw event up to the caller...
        $this.find( "#refresh").click( function(){
            widget( $this, a_data, a_events ); // ...and update ourself. Yep, in that easy way.
        });
    }
});

2

Sie können jquery mx (in javascriptMVC verwendet) verwenden, eine Reihe von Skripten, mit denen Sie Modelle, Ansichten und Controller verwenden können. Ich habe es in einem Projekt verwendet und mir geholfen, strukturiertes Javascript mit minimalen Skriptgrößen aufgrund von Komprimierung zu erstellen. Dies ist ein Controller-Beispiel:

$.Controller.extend('Todos',{
  ".todo mouseover" : function( el, ev ) {
   el.css("backgroundColor","red")
  },
  ".todo mouseout" : function( el, ev ) {
   el.css("backgroundColor","")
  },
  ".create click" : function() {
   this.find("ol").append("<li class='todo'>New Todo</li>"); 
  }
})

new Todos($('#todos'));

Sie können auch nur die Controller- Seite von jquerymx verwenden, wenn Sie nicht an den Ansichts- und Modellteilen interessiert sind.


1

Ihre Frage hat mich Ende letzten Jahres geplagt. Der Unterschied - Weitergabe des Codes an neue Entwickler, die noch nie von privaten und öffentlichen Methoden gehört hatten. Ich musste etwas Einfaches bauen.

Das Endergebnis war ein kleines Framework (ca. 1 KB), das Objektliterale in jQuery übersetzt. Die Syntax ist visuell einfacher zu scannen, und wenn Ihr js wirklich groß wird, können Sie wiederverwendbare Abfragen schreiben, um Dinge wie verwendete Selektoren, geladene Dateien, abhängige Funktionen usw. zu finden.

Ein kleines Framework hier zu veröffentlichen ist unpraktisch, deshalb habe ich einen Blog-Beitrag mit Beispielen geschrieben (Mein erstes. Das war ein Abenteuer!). Gerne können Sie einen Blick darauf werfen.

Für alle anderen hier, die ein paar Minuten Zeit haben, würde ich mich sehr über Feedback freuen!

FireFox wird empfohlen, da es toSource () für das Objektabfragebeispiel unterstützt.

Prost!

Adam


0

Ich verwende ein benutzerdefiniertes Skript, das von Ben Nolans Verhalten inspiriert ist (ich kann leider keinen aktuellen Link mehr finden), um die meisten meiner Event-Handler zu speichern. Diese Ereignishandler werden beispielsweise durch die Elemente className oder Id ausgelöst. Beispiel:

Behaviour.register({ 
    'a.delete-post': function(element) {
        element.observe('click', function(event) { ... });
    },

    'a.anotherlink': function(element) {
        element.observe('click', function(event) { ... });
    }

});

Ich möchte die meisten meiner Javascript-Bibliotheken im laufenden Betrieb einbinden, mit Ausnahme derjenigen, die globales Verhalten enthalten. Ich verwende hierfür den Platzhalter-Helfer headScript () von Zend Framework , aber Sie können auch Javascript verwenden, um beispielsweise andere Skripte im laufenden Betrieb mit Ajile zu laden .


Ist es das, wonach du gesucht hast? koders.com/javascript/…
DOK

Ja, das ist der eine! :) Der Code hinter dem Link scheint jedoch etwas neuer zu sein als die Version, von der ich inspiriert wurde. Danke für deinen Einsatz!
Aron Rotteveel

0

Sie erwähnen nicht, was Ihre serverseitige Sprache ist. Oder, genauer gesagt, welches Framework Sie - falls vorhanden - auf der Serverseite verwenden.

IME, ich organisiere die Dinge auf der Serverseite und lasse alles auf die Webseite schütteln. Das Framework hat die Aufgabe, nicht nur JS zu organisieren, die jede Seite laden muss, sondern auch JS-Fragmente, die mit generiertem Markup arbeiten. Solche Fragmente, die normalerweise nicht mehr als einmal ausgegeben werden sollen, werden in das Framework abstrahiert, damit dieser Code dieses Problem löst. :-)

Bei Endseiten, die ihre eigene JS ausgeben müssen, stelle ich normalerweise fest, dass das generierte Markup eine logische Struktur aufweist. Solche lokalisierten JS können häufig am Anfang und / oder Ende einer solchen Struktur zusammengesetzt werden.

Beachten Sie, dass Sie nichts davon davon abhält, effizientes JavaScript zu schreiben! :-)


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.