Il modo migliore per organizzare il codice jQuery / JavaScript (2013)

Il problema

Questa risposta è stata risolta prima ma è vecchia e non aggiornata. Ho oltre 2000 linee di codice in un singolo file e, come tutti sappiamo, questa è una pratica negativa, specialmente quando sto cercando il codice o aggiunto nuove funzionalità. Voglio organizzare meglio il mio codice, per ora e per il futuro.

Vorrei ricordare che sto costruendo uno strumento (non un sito web semplice) con un sacco di pulsanti, elementi UI, trascinamento, gocce, ascoltatori / gestori d'azione e funzioni nell'ambito globale in cui diversi utenti possono utilizzare la stessa function.

Codice di esempio

$('#button1').on('click', function(e){ // Determined action. update_html(); }); ... // Around 75 more of this function update_html(){ .... } ... 

Più codice di esempio

Conclusione

Ho davvero bisogno di organizzare questo codice per il miglior utilizzo e non ripetermi e poter aggiungere nuove funzionalità e aggiornare quelle vecchie. Lavorerò su questo da solo. Alcuni selettori possono essere 100 righe di codice altri sono 1. Ho guardato un po 'a require.js e ho trovato qualcosa di ripetitivo e in realtà scrivendo più codice di quello necessario. Sono aperto a tutte le possibili soluzioni che soddisfano questi criteri e collegano a risorse / esempi sono sempre un vantaggio.

Grazie.

Andrò su alcune cose semplici che possono, o forse non, aiutarti. Alcuni potrebbero essere evidenti, alcuni potrebbero essere estremamente arcani.

Fase 1: dividere il codice

Separare il codice in più unità modulari è un ottimo primo passo. Riagganciare ciò che funziona "insieme" e metterli nella loro piccola unità ricoperta. non preoccuparti del formato per ora, mantenerlo in linea. La struttura è un punto successivo.

Quindi, supponiamo di avere una pagina come questa:

immettere qui la descrizione dell'immagine

Sarebbe logico compartimentalizzare in modo che tutti i gestori di events / raccoglitori correlati all'intestazione siano presenti, per facilitarne la manutenzione (e non wherer procedere a setacciamenti con 1000 righe).

È quindi ansible utilizzare uno strumento come Grunt per ribuild il JS in un'unica unità.

Fase 1a: gestione della dipendenza

Utilizzare una libreria come RequireJS o CommonJS per implementare qualcosa denominato AMD . Caricamento asincrono del module consente di indicare in modo esplicito quello che dipende dal tuo codice, che poi ti consente di scaricare la chiamata della libreria nel codice. Puoi dire letteralmente "Questo ha bisogno di jQuery" e l'AMD lo caricherà e eseguirà il codice quando jQuery è disponibile .

Questo ha anche una gem nascosta: il caricamento della libreria sarà fatto il secondo il DOM è pronto, non prima. Questo non interrompe più il caricamento della tua pagina!

Fase 2: Modularizzare

Vedere il wireframe? Ho due unità pubblicitarie. Probabilmente hanno condiviso ascoltatori di events.

La tua attività in questo passaggio è identificare i punti di ripetizione nel codice e cercare di sintetizzare tutto questo nei moduli . I moduli, in questo momento, comprenderanno tutto. Ci divideremo mentre andiamo.

L'intera idea di questo passaggio è quella di andare dal passo 1 e di eliminare tutte le copie-copie, per sostituirle con unità che sono leggermente accoppiate. Quindi, invece di avere:

ad_unit1.js

  $("#au1").click(function() { ... }); 

ad_unit2.js

  $("#au2").click(function() { ... }); 

Avro:

ad_unit.js :

  var AdUnit = function(elem) { this.element = elem || new jQuery(); } AdUnit.prototype.bindEvents = function() { ... Events go here } 

page.js :

  var AUs = new AdUnit($("#au1,#au2")); AUs.bindEvents(); 

Ciò consente di compartimentare tra i tuoi events e il tuo markup oltre a sbarazzarsi della ripetizione. Questo è un passo abbastanza decente e lo estenderemo ulteriormente in seguito.

Fase 3: scegli un quadro!

Se si desidera modulare e ridurre ulteriormente le ripetizioni, ci sono un insieme di frameworks impressionanti attorno a ciò che implementano approcci MVC (Model – View – Controller). Il mio preferito è Backbone / Spine, tuttavia, c'è anche Angular, Yii, … L'elenco continua.

Un model rappresenta i tuoi dati.

Una vista rappresenta il tuo mark-up e tutti gli events ad esso associati

Un controller rappresenta la tua logica aziendale – in altre parole, il controller indica alla pagina quali viste da caricare e quali templates da utilizzare.

Questo sarà un significativo passo di apprendimento, ma il premio lo vale: favorisce un codice pulito e modulare sugli spaghetti.

Ci sono molte altre cose che puoi fare, queste sono solo linee guida e idee.

Modifiche specifiche del codice

Ecco alcuni miglioramenti specifici del tuo codice:

  $('.new_layer').click(function(){ dialog("Create new layer","Enter your layer name","_input", { 'OK' : function(){ var reply = $('.dialog_input').val(); if( reply != null && reply != "" ){ var name = "ln_"+reply.split(' ').join('_'); var parent = ""; if(selected_folder != "" ){ parent = selected_folder+" .content"; } $R.find(".layer").clone() .addClass(name).html(reply) .appendTo("#layer_groups "+parent); $R.find(".layers_group").clone() .addClass(name).appendTo('#canvas '+selected_folder); } } }); }); 

Questo è meglio scritto come:

 $("body").on("click",".new_layer", function() { dialog("Create new layer", "Enter your layer name", "_input", { OK: function() { // There must be a way to get the input from here using this, if it is a standard library. If you wrote your own, make the value retrievable using something other than a class selector (horrible performance + scoping +multiple instance issues) // This is where the view comes into play. Instead of cloning, bind the rendering into a JS prototype, and instantiate it. It means that you only have to modify stuff in one place, you don't risk cloning events with it, and you can test your Layer stand-alone var newLayer = new Layer(); newLayer .setName(name) .bindToGroup(parent); } }); }); 

Precedentemente nel tuo codice:

 window.Layer = function() { this.instance = $("<div>"); // Markup generated here }; window.Layer.prototype = { setName: function(newName) { }, bindToGroup: function(parentNode) { } } 

Improvvisamente, hai un modo per creare un livello standard da qualsiasi punto del codice senza incollare la copia. Lo stai facendo in cinque diversi luoghi. Ti ho appena salvato cinque copie.

Un altro:

// Wrapper Ruleset per azioni

 var PageElements = function(ruleSet) { ruleSet = ruleSet || []; this.rules = []; for (var i = 0; i < ruleSet.length; i++) { if (ruleSet[i].target && ruleSet[i].action) { this.rules.push(ruleSet[i]); } } } PageElements.prototype.run = function(elem) { for (var i = 0; i < this.rules.length; i++) { this.rules[i].action.apply(elem.find(this.rules.target)); } } var GlobalRules = new PageElements([ { "target": ".draggable", "action": function() { this.draggable({ cancel: "div#scrolling, .content", containment: "document" }); } }, { "target" :".resizable", "action": function() { this.resizable({ handles: "all", zIndex: 0, containment: "document" }); } } ]); GlobalRules.run($("body")); // If you need to add elements later on, you can just call GlobalRules.run(yourNewElement); 

Questo è un modo molto potente per registrare regole se si dispone di events non standard o events di creazione. Questo è anche seriamente kick-ass quando combinato con un sistema pub / sub notifica e quando legato ad un evento si spara each volta che si creano elementi. Fire'n'forget binding evento modulare!

Ecco un semplice modo per dividere il codice base corrente in più file, utilizzando require.js. Ti mostrerò come dividere il tuo codice in due file. L'aggiunta di ulteriori file sarà semplice dopo di che.

Passo 1) Nella parte superiore del codice, crea un object App (o qualsiasi nome preferito, come MyGame):

var App = {}

Passo 2) Convertire tutte le variables e le funzioni di livello superiore per appartenere all'object App.

Invece di:

var selected_layer = "";

Tu vuoi:

App.selected_layer = "";

Invece di:

 function getModified(){ ... } 

Tu vuoi:

 App.getModified = function() { } 

Tieni presente che a questo punto il codice non functionrà finché non finirà il passaggio successivo.

Passo 3) Convertire tutti i riferimenti globali e variables di funzioni per passare attraverso App.

Cambia roba come:

 selected_layer = "."+classs[1]; 

a:

 App.selected_layer = "."+classs[1]; 

e:

 getModified() 

a:

 App.GetModified() 

Fase 4) Testare il codice in questa fase – dovrebbe funzionare. Probabilmente riceverai alcuni errori in un primo momento perché hai perso qualcosa, perciò correggerai prima di passare.

Fase 5) Impostazione dei requisiti. Suppongo che tu abbia una pagina web, servita da un server web, il cui codice è in:

 www/page.html 

e jquery in

 www/js/jquery.js 

Se questi routes non sono esattamente come questo, il sotto non funziona e dovrai modificare i routes.

Scaricare requirejs e mettere require.js nella tua directory www/js .

nella tua page.html , eliminare tutti i tag di script e inserire un tag di script come:

 <script data-main="js/main" src="js/require.js"></script> 

creare www/js/main.js con il contenuto:

 require.config({ "shim": { 'jquery': { exports: '$' } } }) require(['jquery', 'app']); 

quindi metti tutto il codice appena corretto in Steps 1-3 (la cui variabile globale solo dovrebbe essere App) in:

 www/js/app.js 

Al vertice di quel file, metti:

 require(['jquery'], function($) { 

In basso mettere:

 }) 

Carica quindi page.html nel tuo browser. L'applicazione dovrebbe funzionare!

Passo 6) Crea un altro file

Qui è where il tuo lavoro si paga, puoi farlo più e più volte.

Estraete un codice da www/js/app.js che fa riferimento a $ e App.

per esempio

 $('a').click(function() { App.foo() } 

Mettilo in www/js/foo.js

Al vertice di quel file, metti:

 require(['jquery', 'app'], function($, App) { 

In basso mettere:

 }) 

Quindi cambiare l'ultima row di www / js / main.js in:

 require(['jquery', 'app', 'foo']); 

Questo è tutto! Fai questo each volta che vuoi mettere il codice nel proprio file!

Per la tua domanda e commenti, suppongo che non sei disposto a portre il codice in un framework come Backbone o utilizzare una libreria di loader come Richiedi. Vuoi solo un modo migliore per organizzare il codice che hai già nel modo più semplice ansible.

Capisco che sia fastidioso scorrere tra 2000+ righe di codice per trovare la sezione su cui si desidera lavorare. La soluzione è dividere il codice in file diversi, uno per ciascuna funzionalità. Ad esempio canvas.js , canvas.js ecc. Poi puoi unirti insieme per la produzione utilizzando Grunt, insieme a Usemin puoi avere qualcosa di simile:

Nel tuo html:

 <!-- build:js scripts/app.js --> <script src="scripts/sidebar.js"></script> <script src="scripts/canvas.js"></script> <!-- endbuild --> 

Nel tuo Gruntfile:

 useminPrepare: { html: 'app/index.html', options: { dest: 'dist' } }, usemin: { html: ['dist/{,*/}*.html'], css: ['dist/styles/{,*/}*.css'], options: { dirs: ['dist'] } } 

Se si desidera utilizzare Yeoman vi darà un codice di boilerplate per tutto questo.

Quindi per each file stesso, è necessario assicurarsi di seguire le pratiche migliori e che tutti i codici e le variables siano tutti in quel file e non dipendono da altri file. Questo non significa che non è ansible call funzioni di un file da altro, il punto è quello di avere le variables e le funzioni incapsulate. Qualcosa di simile al namespacing. Suppongo che tu non vuoi portre tutto il tuo codice per essere object orientato, ma se non ti dispiace rifatturare un po ', ti consiglierei di aggiungere qualcosa di equivalente a quello che viene chiamato model Modulo. Sembra qualcosa di simile:

sidebar.js

 var Sidebar = (function(){ // functions and vars here are private var init = function(){ $("#sidebar #sortable").sortable({ forceHelperSize: true, forcePlaceholderSize: true, revert: true, revert: 150, placeholder: "highlight panel", axis: "y", tolerance: "pointer", cancel: ".content" }).disableSelection(); } return { // here your can put your "public" functions init : init } })(); 

Quindi puoi caricare questo codice come questo:

 $(document).ready(function(){ Sidebar.init(); ... 

Questo vi permetterà di avere un codice molto più sostenibile senza wherer riscrivere troppo il codice.

Utilizza javascript MVC Framework per organizzare il codice javascript in modo standard.

I migliori framework MVC JavaScript disponibili sono:

  • Spina dorsale
  • Angolare
  • CanJS
  • brace

La selezione di un framework MVC JavaScript richiede tanti fattori da considerare. Leggi il seguente articolo di confronto che ti aiuterà a scegliere il quadro migliore basato sui fattori importnti per il tuo progetto: http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/

È inoltre ansible utilizzare RequJS con il framework per supportre il caricamento di file e moduli js Asynchrounous.
Guarda sotto per iniziare a caricare JS Module:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/

Classificare il codice. Questo metodo mi aiuta molto e funziona con qualsiasi framework js:

 (function(){//HEADER: menu //your code for your header })(); (function(){//HEADER: location bar //your code for your location })(); (function(){//FOOTER //your code for your footer })(); (function(){//PANEL: interactive links. eg: var crr = null; $('::section.panel a').addEvent('click', function(E){ if ( crr) { crr.hide(); } crr = this.show(); }); })(); 

Nel tuo editor preferito (il meglio è il command Komodo) puoi foldare compilando tutte le voci e vedrai solo i titoli:

 (function(){//HEADER: menu_____________________________________ (function(){//HEADER: location bar_____________________________ (function(){//FOOTER___________________________________________ (function(){//PANEL: interactive links. eg:___________________ 

Io suggerirei:

  1. editor / model di abbonato per la gestione degli events.
  2. orientamento dell'object
  3. namespacing

Nel tuo caso Jessica, divide l'interface in pagine o schermate. Pagine o schermate possono essere oggetti e estesi da alcune classi parentali. Gestire le interazioni tra le pagine con una class PageManager.

Suggerisco di utilizzare qualcosa come Backbone. Backbone è una libreria JavaScript RESTFUL supportta. Ik rende il codice più pulito e leggibile ed è potente se utilizzato insieme a requirejs.

http://backbonejs.org/

http://requirejs.org/

Il backbone non è una biblioteca reale. Si intende dare struttura al tuo codice javascript. È in grado di includere altre biblioteche come jquery, jquery-ui, google-maps ecc. Il backbone è a mio parere l'approccio più vicino a javascript per le strutture del controller di oggetti orientato agli oggetti e del model.

Anche per quanto riguarda il stream di lavoro. Se sviluppi le tue applicazioni in PHP, utilizza la libreria Laravel. Funzionerà perfettamente con il Backbone quando usato con il principio RESTfull. Sotto il link a Laravel Framework e un tutorial sulla costruzione di API RESTfull:

http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/

http://laravel.com/

Di seguito è un tutorial da nettuts. Nettuts ha un sacco di tutorial di alta qualità:

http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/

Forse il suo tempo per iniziare a implementare un stream di lavoro di sviluppo completo utilizzando strumenti come il yeoman http://yeoman.io/ . Questo aiuterà a controllare tutte le dependencies, il process di creazione e se volete, test automatizzati. Il suo lavoro molto da cominciare ma una volta implementato renderà i cambiamenti futuri molto più facile.