data - windows document javascript




Best practice comunemente accettate in merito all'organizzazione del codice in JavaScript (19)

"Scrivi come un matto e spero solo che funzioni per il meglio?", Ho visto un progetto come questo che è stato sviluppato e gestito da soli 2 sviluppatori, una grande applicazione con un sacco di codice javascript. Inoltre, c'erano scorciatoie diverse per ogni possibile funzione jquery a cui si possa pensare. Ho suggerito che organizzino il codice come plugin, poiché questo è l'equivalente jquery di classe, modulo, spazio dei nomi ... e dell'intero universo. Ma le cose sono andate molto peggio, ora hanno iniziato a scrivere plugin per sostituire ogni combinazione di 3 linee di codice utilizzate nel progetto. Personalmente penso che jQuery sia il diavolo e non dovrebbe essere usato su progetti con un sacco di javascript perché ti incoraggia a essere pigro e non pensare di organizzare il codice in alcun modo. Preferisco leggere 100 righe di javascript piuttosto che una riga con 40 funzioni jQuery concatenate (non sto scherzando). Contrariamente alla credenza popolare è molto facile organizzare il codice javascript in equivalenti a domini e classi. Questo è ciò che fanno YUI e Dojo. Puoi facilmente arrotolare il tuo se vuoi. Trovo l'approccio di YUI molto migliore ed efficiente. Ma di solito hai bisogno di un buon editor con supporto per gli snippet per compensare le convenzioni di denominazione YUI se vuoi scrivere qualcosa di utile.

Poiché i framework JavaScript come jQuery rendono le applicazioni web lato client più ricche e più funzionali, ho iniziato a notare un problema ...

Come diavolo mantieni questo organizzato?

  • Metti tutti i tuoi gestori in un punto e scrivi le funzioni per tutti gli eventi?
  • Creare funzioni / classi per avvolgere tutte le tue funzionalità?
  • Scrivi come un matto e spera solo che funzioni per il meglio?
  • Rinunciare e ottenere una nuova carriera?

Menziono jQuery, ma in realtà è un codice JavaScript in generale. Sto scoprendo che quando le linee su linee iniziano ad accumularsi, diventa più difficile gestire i file di script o trovare quello che stai cercando. Molto probabilmente i più grandi elementi che ho trovato è che ci sono così tanti modi per fare la stessa cosa, è difficile sapere qual è l'attuale best practice comunemente accettata.

Ci sono raccomandazioni generali sul modo migliore per mantenere i tuoi file .js belli e ordinati come il resto della tua applicazione? O è solo una questione di IDE? C'è un'opzione migliore là fuori?

MODIFICARE

Questa domanda era intesa per essere più sull'organizzazione di codice e non sull'organizzazione di file. Ci sono stati alcuni ottimi esempi di unione di file o di suddivisione del contenuto.

La mia domanda è: qual è l'attuale metodo di best practice comunemente accettato per organizzare il tuo codice attuale? Qual è il tuo modo, o anche un modo consigliato per interagire con gli elementi della pagina e creare codice riutilizzabile che non sia in conflitto tra loro?

Alcune persone hanno elencato spazi dei nomi che è una buona idea. Quali sono altri modi, più specificamente di trattare gli elementi sulla pagina e mantenere il codice organizzato e ordinato?


È possibile suddividere gli script in file separati per lo sviluppo, quindi creare una versione di "rilascio" in cui riunirli tutti insieme ed eseguire YUI Compressor o qualcosa di simile su di esso.


Controlla JavasciptMVC .

Puoi :

  • suddividere il codice in livelli di modello, visualizzazione e controller.

  • comprimi tutto il codice in un unico file di produzione

  • codice auto-generato

  • creare ed eseguire test unitari

  • e molto altro...

Meglio ancora, usa jQuery, quindi puoi sfruttare anche altri plugin jQuery.


Crea classi false e assicurati che tutto ciò che può essere gettato in una funzione separata che abbia senso sia fatto così. Assicurati anche di commentare molto e di non scrivere il codice spagghetti, piuttosto di tenerlo tutto in sezioni. Ad esempio, qualche codice senza senso che descrive i miei ideali. Ovviamente nella vita reale scrivo anche molte librerie che fondamentalmente comprendono la loro funzionalità.

$(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.
    }
};

Dojo ha avuto il sistema del modulo dal primo giorno. In realtà è considerato una pietra angolare del Dojo, la colla che tiene tutto insieme:

Utilizzando i moduli Dojo raggiunge i seguenti obiettivi:

  • Namespace per codice Dojo e codice personalizzato ( dojo.declare() ): non inquinano lo spazio globale, coesistono con altre librerie e il codice utente non compatibile con Dojo.
  • Caricamento dei moduli in modo sincrono o asincrono per nome ( dojo.require() ).
  • Le build personalizzate analizzano le dipendenze dei moduli per creare un singolo file o un gruppo di file interdipendenti (i cosiddetti livelli) per includere solo ciò di cui ha bisogno l'applicazione web. Le build personalizzate possono includere anche moduli Dojo e moduli forniti dal cliente.
  • Accesso basato su CDN trasparente a Dojo e codice utente. Sia AOL che Google portano Dojo in questo modo, ma alcuni clienti lo fanno anche per le loro applicazioni web personalizzate.

Il buon principio di OO + MVC farebbe sicuramente molto per gestire una complessa applicazione javascript.

Fondamentalmente sto organizzando la mia app e javascript sul seguente design familiare (che esiste già dai miei giorni di programmazione desktop a Web 2.0)

Descrizione per i valori numerici sull'immagine:

  1. Widget che rappresentano le viste della mia applicazione. Questo dovrebbe essere estensibile e separato in modo ordinato risultando in una buona separazione che MVC cerca di ottenere piuttosto che trasformare il mio widget in un codice spaghetti (equivalente nell'applicazione web di mettere un grande blocco di Javascript direttamente in HTML). Ogni widget comunica tramite gli altri ascoltando l'evento generato da altri widget riducendo così il forte accoppiamento tra i widget che potrebbero portare a un codice ingestibile (ricorda il giorno in cui aggiungi onclick ovunque indicando funzioni globali nel tag dello script? Urgh ...)
  2. Modelli di oggetti che rappresentano i dati che voglio inserire nei widget e passare avanti e indietro sul server. Incapsulando i dati nel modello, l'applicazione diventa agnostica del formato dei dati. Ad esempio: mentre naturalmente in Javascript questi modelli di oggetti sono per lo più serializzati e deserializzati in JSON, se in qualche modo il server utilizza XML per la comunicazione, tutto ciò che devo cambiare sta cambiando il livello di serializzazione / deserializzazione e non necessariamente ha bisogno di cambiare tutte le classi di widget .
  3. Classi di controller che gestiscono la logica aziendale e la comunicazione al server + occasionalmente il livello di memorizzazione nella cache. Questo strato controlla il protocollo di comunicazione verso il server e inserisce i dati necessari nei modelli oggetto
  4. Le classi sono impacchettate ordinatamente nei corrispondenti spazi dei nomi. Sono sicuro che tutti sappiamo quanto possa essere brutto lo spazio dei nomi globale in Javascript.

In passato, avrei separato i file in js e usato la pratica comune per creare principi OO in Javascript. Il problema che ho scoperto presto è che ci sono molti modi per scrivere JS OO e non è necessariamente che tutti i membri del team abbiano lo stesso approccio. Con l'aumentare della squadra (nel mio caso più di 15 persone), questo si complica poiché non esiste un approccio standard per Javascript orientato agli oggetti. Allo stesso tempo, non voglio scrivere il mio schema e ripetere parte del lavoro che sono sicuramente più intelligente di quello che ho risolto.

jQuery è incredibilmente bello come Javascript Framework e lo adoro, tuttavia quando il progetto diventa più grande, ho chiaramente bisogno di una struttura aggiuntiva per la mia app web, in particolare per facilitare la standardizzazione della pratica OO. Per me stesso, dopo diversi esperimenti, trovo che l'infrastruttura YUI3 Base e Widget ( http://yuilibrary.com/yui/docs/widget/ e http://yuilibrary.com/yui/docs/base/index.html ) fornisce esattamente quello di cui ho bisogno Poche ragioni per cui li uso

  1. Fornisce supporto per Namespace. Un vero bisogno di OO e organizzazione ordinata del tuo codice
  2. Supporta la nozione di classi e oggetti
  3. Fornisce un mezzo standard per aggiungere variabili di istanza alla classe
  4. Supporta ordinatamente l'estensione di classe
  5. Fornisce costruttore e distruttore
  6. Fornisce il rendering e l'associazione degli eventi
  7. Ha un framework per widget di base
  8. Ogni widget ora è in grado di comunicare tra loro utilizzando un modello basato su eventi standard
  9. Soprattutto, offre a tutti gli ingegneri uno standard OO per lo sviluppo di Javascript

Contrariamente a molti punti di vista, non devo necessariamente scegliere tra jQuery e YUI3. Questi due possono coesistere pacificamente. Mentre YUI3 fornisce il modello OO necessario per la mia app Web complessa, jQuery fornisce al mio team l'astrazione JS facile da usare che tutti noi amiamo e conosciamo.

Utilizzando YUI3, sono riuscito a creare pattern MVC separando le classi che estendono Base come Model, classi che ampliano Widget come View e, naturalmente, sono presenti classi di controller che eseguono le chiamate logiche e server side necessarie.

Il widget può comunicare tra loro utilizzando il modello basato su eventi e ascoltando l'evento e svolgendo l'attività necessaria in base all'interfaccia predefinita. In poche parole, mettere la struttura OO + MVC in JS è una gioia per me.

Solo un disclaimer, non lavoro per Yahoo! e semplicemente un architetto che sta cercando di affrontare lo stesso problema posto dalla domanda originale. Penso che se qualcuno troverà un framework OO equivalente, funzionerebbe altrettanto bene. Principalmente, questa domanda si applica anche ad altre tecnologie. Grazie a Dio per tutte le persone che hanno ideato OO Principles + MVC per rendere i nostri giorni di programmazione più gestibili.


Ispirato ai post precedenti ho realizzato una copia di Rakefile e delle directory dei produttori distribuite con WysiHat (un RTE menzionato da changelog) e ho apportato alcune modifiche per includere il controllo del codice con JSLint e il minification con YUI Compressor .

L'idea è di usare Sprockets (da WysiHat) per unire più JavaScripts in un unico file, controllare la sintassi del file unito con JSLint e ridurlo a YUI Compressor prima della distribuzione.

Prerequisiti

  • Java Runtime
  • gemma di rubino e rastrello
  • Dovresti sapere come inserire un JAR in Classpath

Adesso fallo

  1. Scarica Rhino e metti il ​​JAR ("js.jar") nel classpath
  2. Scarica YUI Compressor e metti il ​​JAR (build / yuicompressor-xyz.jar) sul classpath
  3. Scarica WysiHat e copia la directory "fornitore" nella radice del tuo progetto JavaScript
  4. Scarica JSLint per Rhino e inseriscilo nella directory "fornitore"

Ora crea un file chiamato "Rakefile" nella directory root del progetto JavaScript e aggiungi ad esso il seguente contenuto:

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

Se hai fatto tutto correttamente, dovresti essere in grado di utilizzare i seguenti comandi nella tua console:

  • rake merge - per unire file JavaScript diversi in uno solo
  • rake check - per verificare la sintassi del tuo codice (questa è l'attività di default , quindi puoi semplicemente digitare rake )
  • rake minify - per preparare la versione minificata del tuo codice JS

Sulla fusione di origine

Utilizzando Sprockets, il pre-processore JavaScript è possibile includere (o require ) altri file JavaScript. Utilizzare la seguente sintassi per includere altri script dal file iniziale (denominato "main.js", ma è possibile modificarlo nel Rakefile):

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

E poi...

Dai un'occhiata a Rakefile fornito con WysiHat per impostare il test dell'unità automatica. Bella roba :)

E ora per la risposta

Questo non risponde alla domanda originale molto bene. Lo so e mi dispiace, ma l'ho postato qui perché spero possa essere utile a qualcun altro per organizzare il loro casino.

Il mio approccio al problema è quello di fare la stessa modellazione orientata agli oggetti che posso e separare le implementazioni in file diversi. Quindi i conduttori dovrebbero essere il più corti possibile. L'esempio con List singleton è anche bello.

E i namespace ... beh, possono essere imitati da una struttura dell'oggetto più profonda.

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

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

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

Non sono un grande fan delle imitazioni, ma questo può essere utile se hai molti oggetti che vorresti uscire dall'ambito globale.


L'organizzazione del codice richiede l'adozione di convenzioni e standard di documentazione:
1. Codice spazio dei nomi per un file fisico;

Exc = {};


2. Classi di gruppo in questi spazi dei nomi javascript;
3. Impostare prototipi o funzioni o classi correlate per rappresentare oggetti del mondo reale;

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


4. Impostare le convenzioni per migliorare il codice. Ad esempio, raggruppa tutte le sue funzioni o metodi interni nel suo attributo di classe di un tipo di oggetto.

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


5. Realizza documentazione di spazi dei nomi, classi, metodi e variabili. Se necessario, discutete anche di parte del codice (alcuni FI e Fors, di solito implementano una logica importante del codice).

/**
  * 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;
    ...
};


Questi sono solo alcuni consigli, ma ciò ha aiutato molto nell'organizzazione del codice. Ricorda che devi avere la disciplina per avere successo!


Organizzare il tuo codice in un modo NameSpace di Jquery centric può apparire come segue ... e non si scontrerà con altre API di Javascript come 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>

Spero che questo ti aiuti.


Penso che questo sia legato, forse, al DDD (Domain-Driven Design). L'applicazione su cui sto lavorando, anche se manca un'API formale, fornisce alcuni suggerimenti in merito al codice lato server (nomi di classi / file, ecc.). Armato di ciò, ho creato un oggetto di livello superiore come contenitore per l'intero dominio del problema; poi, ho aggiunto spazi dei nomi dove necessario:

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

Qualche giorno fa, i ragazzi di 37Signals hanno WysiHat , con una svolta. Hanno creato una libreria che raggruppa i file javascript usando una sorta di comandi pre-processore.

L'ho usato da quando ho separato i miei file JS e alla fine li ho uniti come uno solo. In questo modo posso separare le preoccupazioni e, alla fine, avere solo un file che passa attraverso la pipe (gzip, non meno).

Nei tuoi modelli, controlla se sei in modalità sviluppo e includi i file separati e, se in produzione, includi quello finale (che dovrai "costruire" tu stesso).


Sarebbe molto più carino se javascript avesse gli spazi dei nomi integrati, ma trovo che organizzare cose come Dustin Diaz here mi aiuti molto.

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

Ho messo diversi "namespace" e talvolta singole classi in file separati. Di solito parto da un file e siccome una classe o uno spazio dei nomi diventano abbastanza grandi da giustificarlo, lo separo nel suo stesso file. Usare uno strumento per combinare tutti i file per la produzione è un'ottima idea.


Sono sorpreso che nessuno abbia menzionato i framework MVC. Sto usando Backbone.js per modulare e disaccoppiare il mio codice, ed è stato inestimabile.

Ci sono un bel po 'di questi tipi di quadri là fuori, e la maggior parte di essi sono abbastanza piccoli. La mia opinione personale è che se stai scrivendo più di un paio di righe di jQuery per roba da UI appariscente, o vuoi una ricca applicazione Ajax, un framework MVC ti renderà la vita molto più facile.


Sono stato in grado di applicare correttamente il Pattern Module Javascript a un'applicazione Ext JS al mio precedente lavoro. Ha fornito un modo semplice per creare codice ben incapsulato.



È possibile utilizzare jquery mx (utilizzato in javascriptMVC) che è un set di script che consente di utilizzare modelli, viste e controller. L'ho usato in un progetto e mi ha aiutato a creare javascript strutturato, con dimensioni di script minime a causa della compressione. Questo è un esempio di controller:

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

Puoi anche utilizzare solo il lato controller di jquerymx se non sei interessato alla vista e alle parti del modello.


Non parli di quale sia il tuo linguaggio lato server. O, più pertinentemente, quale framework stai usando, se ce ne sono, sul lato server.

IME, organizzo le cose sul lato server e lascio tutto scuotere sulla pagina web. Il framework ha il compito di organizzare non solo JS che ogni pagina deve caricare, ma anche frammenti JS che funzionano con il markup generato. Tali frammenti di solito non si desidera vengano emessi più di una volta, motivo per cui sono astratti nel framework di quel codice per occuparsi di quel problema. :-)

Per le pagine finali che devono emettere il proprio JS, di solito trovo che ci sia una struttura logica nel markup generato. Tale JS localizzato può spesso essere assemblato all'inizio e / o alla fine di tale struttura.

Nota che niente di tutto ciò ti assolve dalla scrittura di JavaScript efficiente! :-)


Pigro Carica il codice necessario su richiesta. Google fa qualcosa del genere con il loro google.loader


Uso una sceneggiatura personalizzata ispirata al comportamento di Ben Nolan (non riesco più a trovare un collegamento corrente con questo, purtroppo) per archiviare la maggior parte dei miei gestori di eventi. Questi gestori di eventi sono attivati ​​dagli elementi className o Id, ad esempio. Esempio:

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

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

});

Mi piace includere al volo la maggior parte delle mie librerie Javascript, tranne quelle che contengono un comportamento globale. Uso l'helper segnaposto headscreens () di Zend Framework per questo, ma puoi anche usare javascript per caricare altri script al volo con Ajile ad esempio.





formatting