variable - javascript practice




Allgemein akzeptierte Best Practices zur Code-Organisation in JavaScript (19)

Da JavaScript-Frameworks wie jQuery Client-seitige Web-Anwendungen reicher und funktionaler machen, habe ich ein Problem festgestellt ...

Wie in der Welt halten Sie das organisiert?

  • Setzen Sie alle Ihre Handler an einen Ort und schreiben Sie Funktionen für alle Veranstaltungen?
  • Erstellen Sie Funktionen / Klassen, um all Ihre Funktionen zu integrieren?
  • Schreibe wie verrückt und hoffe nur, dass es für das Beste klappt?
  • Aufgeben und eine neue Karriere machen?

Ich erwähne jQuery, aber es ist wirklich jeder JavaScript-Code im Allgemeinen. Ich stelle fest, dass es immer schwieriger wird, die Skriptdateien zu verwalten oder zu finden, wonach Sie suchen. Das wahrscheinlich größte Problem, das ich gefunden habe, ist, dass es so viele Möglichkeiten gibt, das Gleiche zu tun. Es ist schwer zu wissen, welches die gängigste Best Practice ist.

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

BEARBEITEN

Bei dieser Frage ging es mehr um die Code-Organisation und nicht um die Organisation von Dateien. Es gab einige wirklich gute Beispiele für das Zusammenführen von Dateien oder Teilen von Inhalten.

Meine Frage ist: Was ist der gängigste Best Practice-Weg, um Ihren eigentlichen Code zu organisieren? Was ist Ihre Art oder sogar eine empfohlene Methode, um 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 Wege, genauer gesagt mit Elementen auf der Seite umzugehen und den Code organisiert und ordentlich zu halten?


"Schreibe wie verrückt und hoffe nur, dass es für das Beste klappt?", Ich habe ein Projekt wie dieses gesehen, das von nur zwei Entwicklern entwickelt und gewartet wurde, eine riesige Anwendung mit viel JavaScript-Code. Darüber hinaus gab es verschiedene Abkürzungen für jede denkbare jQuery-Funktion. Ich schlug vor, den Code als Plugins zu organisieren, denn das ist das Jquery-Äquivalent von Klasse, Modul, Namespace ... und dem ganzen Universum. Aber die Dinge wurden viel schlimmer, jetzt begannen sie Plugins zu schreiben, die jede Kombination von 3 Codezeilen ersetzen, die im Projekt verwendet wurden. Persönlich denke ich, dass jQuery der Teufel ist und es sollte nicht auf Projekten mit viel Javascript verwendet werden, weil es Sie ermutigt, faul zu sein und nicht daran zu denken, Code in irgendeiner Weise zu organisieren. Ich würde lieber 100 Zeilen Javascript als eine Zeile mit 40 verketteten jQuery-Funktionen lesen (ich mache keine Witze). Entgegen der landläufigen Meinung ist es sehr einfach, JavaScript-Code in Entsprechungen zu Namespaces und Klassen zu organisieren. Das tun YUI und Dojo. Sie können leicht Ihre eigenen rollen, wenn Sie möchten. Ich finde YUIs Ansatz viel besser und effizienter. Aber normalerweise brauchen Sie einen netten Editor mit Snippet-Unterstützung, um YUI-Namenskonventionen zu kompensieren, wenn Sie etwas Nützliches schreiben wollen.


Das Befolgen von guten OO-Designprinzipien und Designmustern trägt wesentlich dazu bei, dass Ihr Code leicht zu pflegen und zu verstehen ist. Aber eines der besten Dinge, die ich in letzter Zeit entdeckt habe, sind Signale und Slots aka publish / subscribe. Sehen Sie sich http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html für eine einfache jQuery-Implementierung an.

Die Idee wird in anderen Sprachen für die GUI-Entwicklung gut verwendet. Wenn etwas Bedeutendes irgendwo in Ihrem Code passiert, veröffentlichen Sie ein globales synthetisches Ereignis, das andere Methoden in anderen Objekten abonnieren können. 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?


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

Exc = {};


2. Gruppenklassen in diesen Namespaces Javascript;
3. Setze Prototypen oder verwandte Funktionen oder Klassen zum Darstellen von realen Objekten;

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. Zum Beispiel gruppieren Sie alle internen Funktionen oder Methoden in seinem Klassenattribut eines Objekttyps.

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


5. Machen Sie Dokumentation von Namespaces, Klassen, Methoden und Variablen. Wenn nötig, besprechen Sie auch einen Teil des Codes (einige FIs und Fors, sie implementieren normalerweise 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 sehr geholfen, den Code zu organisieren. Denken Sie daran, dass Sie Disziplin haben müssen, um erfolgreich zu sein!


Dojo hatte das Modulsystem vom ersten Tag an. In der Tat gilt es als Eckpfeiler von Dojo, der Klebstoff, der alles zusammenhält:

Module verwenden Dojo erreicht folgende Ziele:

  • Namespaces für Dojo-Code und benutzerdefinierten Code ( dojo.declare() ) - verschmutzen Sie nicht den globalen Platz, Koexistenz mit anderen Bibliotheken und nicht-Dojo-bewußten Code des Benutzers.
  • Laden von Modulen synchron oder asynchron nach Name ( dojo.require() ).
  • Benutzerdefinierte Builds durch Analysieren von Modulabhängigkeiten, um eine einzelne Datei oder eine Gruppe voneinander abhängiger Dateien (so genannte Layer) zu erstellen, die nur das enthält, was Ihre Webanwendung benötigt. Benutzerdefinierte Builds können Dojo-Module und vom Kunden bereitgestellte Module enthalten.
  • Transparenter CDN-basierter Zugriff auf Dojo und Benutzercode. Sowohl AOL als auch Google tragen Dojo auf diese Weise, aber einige Kunden tun das auch für ihre eigenen Webanwendungen.

Erstellen Sie gefälschte Klassen und stellen Sie sicher, dass alles, was in eine separate Funktion umgewandelt werden kann, sinnvoll ist. Achten Sie auch darauf, viel zu kommentieren und nicht Spaghetti-Code zu schreiben, sondern alles in Abschnitten zu behalten. Zum Beispiel einige Unsinn-Codes, die meine Ideale darstellen. Natürlich schreibe ich im wirklichen Leben auch viele Bibliotheken, die im Grunde 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.
    }
};

Es wäre viel schöner, wenn Javascript Namespaces eingebaut hätte, aber ich finde, dass das Organisieren von Dingen wie Dustin Diaz here 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 separaten Dateien abgelegt. Normalerweise beginne ich mit einer Datei und da eine Klasse oder ein Namespace groß genug ist, um dies zu rechtfertigen, separiere ich sie in eine eigene Datei. Ein Tool, um alle Ihre Dateien für die Produktion zu kombinieren, ist ebenfalls eine ausgezeichnete Idee.


Ich bin überrascht, dass niemand MVC-Frameworks erwähnt. Ich habe Backbone.js , 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 winzig. Meine persönliche Meinung ist, dass wenn Sie mehr als nur ein paar Zeilen jQuery für auffällige UI-Sachen schreiben, oder eine reiche Ajax-Anwendung wollen, ein MVC-Framework Ihr Leben viel einfacher machen wird.


Ich denke, dass dies DDD (Domain-Driven Design) betrifft. Die Anwendung, an der ich arbeite, gibt zwar Hinweise auf eine formale API, gibt jedoch Hinweise über den serverseitigen Code (Klassen- / Dateinamen usw.). Auf diese Weise erstellte ich ein Objekt der obersten Ebene als Container für die gesamte Problemdomäne. Dann habe ich Namespaces dort eingefügt, wo sie benötigt wurden:

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

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


Ich versuche zu vermeiden, irgendein Javascript mit dem HTML einzuschließen. 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, die jede js-Datei einschließen, aber sie werden in ein einziges größeres Paket für die Produktion zusammengeführt, um den Overhead der HTTP-Anfragen zu reduzieren.

Normalerweise habe ich für jede Anwendung eine einzige "Haupt" -JS-Datei. Also, wenn ich eine "Umfrage" -Anwendung schreiben würde, hätte ich eine js-Datei namens "survey.js". Dies würde den Einstiegspunkt in den jQuery-Code enthalten. Ich erstelle während der Instanziierung jQuery-Referenzen und gebe sie dann als Parameter an meine Objekte weiter. 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 'j' allen jQuery-Instanzen voran.

Im obigen Beispiel gibt es eine Klasse namens DimScreen. (Angenommen, dies blendet den Bildschirm aus und öffnet eine Warnmeldung.) Er benötigt ein div-Element, das vergrößert werden kann, um den Bildschirm abzudecken, und fügt dann eine Warnmeldung hinzu, sodass ich ein jQuery-Objekt übergebe. jQuery hat ein Plug-in-Konzept, aber es schien begrenzend (zB Instanzen sind nicht persistent und können nicht zugegriffen werden) ohne wirklichen Vorteil. Die DimScreen-Klasse wäre also eine Standard-JavaScript-Klasse, die zufälligerweise 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.


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

Ich benutze die Namespace-Simulation und das verzögerte Laden von Modulen gemäß dem Site-Abschnitt. Auf jeder Seite load deklariere ich ein "vjr" -Objekt und lade immer eine Menge allgemeiner Funktionen (vjr.base.js). Dann entscheidet jede HTML-Seite, welche Module mit einem einfachen benötigen:

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

Vjr.base.js bekommt jeden vom Server gezippt und führt sie 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);

Angesichts meines begrenzten Javascript-Wissens weiß ich, dass es bessere Möglichkeiten geben muss, dies zu handhaben, aber bis jetzt funktioniert es großartig für uns.


Inspiriert von früheren Posts habe ich eine Kopie von Rakefile und Lieferantenverzeichnissen erstellt, die mit WysiHat (einer RTE, die von WysiHat erwähnt wird) verteilt und einige Änderungen vorgenommen, um Code-Checking mit JSLint und Minification mit YUI Compressor zu integrieren .

Die Idee besteht darin, Ritzel (von WysiHat) zu verwenden, um mehrere JavaScripts zu 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
  • Sie sollten wissen, wie Sie ein JAR in Classpath

Mach jetzt

  1. Lade Rhino herunter und lege das JAR ("js.jar") in deinen Klassenpfad
  2. Lade YUI Compressor herunter und lege das JAR (build / yuicompressor-xyz.jar) in deinen Klassenpfad
  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 namens "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 die folgenden Befehle in Ihrer Konsole verwenden können:

  • rake merge - um verschiedene JavaScript-Dateien in einem zusammenzuführen
  • rake check - um die Syntax Ihres Codes zu überprüfen (dies ist die Standardaufgabe , also können Sie einfach rake eingeben)
  • rake minify - um eine verkleinerte Version Ihres JS-Codes zu erstellen

Beim Zusammenführen der Quelle

Mit dem JavaScript-Preprozessor von Sprockets können Sie andere JavaScript-Dateien einschließen (oder require ). Verwenden Sie die folgende Syntax, um andere Skripts aus der ursprünglichen Datei ("main.js", aber Sie können das in der Rakefile ändern) hinzuzufügen:

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

Werfen Sie einen Blick auf Rakefile, das mit WysiHat zur Verfügung gestellt wurde, um den Test der automatisierten Einheit durchzuführen. Gutes Zeug :)

Und jetzt für die Antwort

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

Meine Herangehensweise an das Problem ist, so viel objektorientierte Modellierung zu machen und Implementierungen in verschiedene Dateien zu unterteilen. Dann sollten die Handler so kurz wie möglich sein. Das Beispiel mit List singleton ist auch nett.

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 Nachahmungen, aber das kann hilfreich sein, wenn Sie viele Objekte haben, die Sie aus dem globalen Anwendungsbereich entfernen möchten.


Schauen Sie sich JavasciptMVC an .

Sie können :

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

  • Komprimiere den gesamten Code in eine einzige Produktionsdatei

  • Code automatisch generieren

  • Erstellen und Ausführen von Komponententests

  • und vieles mehr...

Das Beste daran ist, dass es jQuery verwendet, so dass Sie auch andere jQuery-Plugins nutzen können.


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



I use a custom script inspired by Ben Nolan's behaviour (I can't find a current link to this anymore, sadly) to store most of my event handlers. These event handlers are triggered by the elements className or Id, for example. Beispiel:

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

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

});

I like to include most of my Javascript libraries on the fly, except the ones that contain global behaviour. I use Zend Framework's headScript() placeholder helper for this, but you can also use javascript to load other scripts on the fly with Ajile for example.


You can use jquery mx (used in javascriptMVC) which is a set of scripts that allows you to use models, views, and controllers. I've used it in a project and helped me create structured javascript, with minimal script sizes because of compression. This is a controller example:

$.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'));

You can also use only the controller side of jquerymx if you aren't interested in the view and model parts.


You don't mention what your server-side language is. Or, more pertinently, what framework you are using -- if any -- on the server-side.

IME, I organise things on the server-side and let it all shake out onto the web page. The framework is given the task of organising not only JS that every page has to load, but also JS fragments that work with generated markup. Such fragments you don't usually want emitted more than once - which is why they are abstracted into the framework for that code to look after that problem. :-)

Für End-Seiten, die ihren eigenen JS ausgeben müssen, finde ich normalerweise, dass es eine logische Struktur im generierten Markup gibt. Ein solcher lokalisierter JS kann oft am Anfang und / oder am Ende einer solchen Struktur zusammengesetzt werden.

Beachten Sie, dass Sie dadurch kein effizientes JavaScript schreiben können! :-)


Lazy Laden Sie den benötigten Code bei Bedarf. Google macht so etwas mit ihrem google.loader







formatting