web-dev-qa-db-fra.com

Meilleur moyen d'organiser le code jQuery / JavaScript (2013)

Le problème

Cette réponse a déjà reçu une réponse, mais elle est ancienne et n'est pas à jour. J'ai plus de 2 000 lignes de code dans un seul fichier et, comme nous le savons tous, il s'agit d'une mauvaise pratique, en particulier lorsque je regarde dans le code ou que j'ajoute de nouvelles fonctionnalités. Je veux mieux organiser mon code, pour le présent et pour l'avenir.

Je dois mentionner que je construis un outil (pas un simple site Web) avec beaucoup de boutons, d’éléments d’interface utilisateur, de glisser-déposer, d’écouteurs d’action/gestionnaires et de fonctions dans la portée globale où plusieurs auditeurs peuvent utiliser la même fonction.

Exemple de code

$('#button1').on('click', function(e){
    // Determined action.
    update_html();
});

... // Around 75 more of this

function update_html(){ .... }

...

Plus de code d'exemple

Conclusion

J'ai vraiment besoin d'organiser ce code pour une meilleure utilisation et de ne pas me répéter et pouvoir ajouter de nouvelles fonctionnalités et mettre à jour d'anciennes. Je vais y travailler par moi-même. Certains sélecteurs peuvent avoir 100 lignes de code, d'autres 1. J'ai jeté un coup d'œil à require.js Et je l'ai trouvé assez répétitif, écrivant en fait plus de code que nécessaire. Je suis ouvert à toute solution possible qui répond à ces critères et un lien vers des ressources/exemples est toujours un avantage.

Merci.

101
Kivylius

Je vais passer en revue des choses simples qui peuvent ou non vous aider. Certains pourraient être évidents, certains pourraient être extrêmement arcaniques.

Étape 1: compartimentez votre code

Séparer votre code en plusieurs unités modulaires est une très bonne première étape. Rassemblez ce qui fonctionne "ensemble" et mettez-les dans leur propre petite unité encapsulée. ne vous inquiétez pas du format pour le moment, gardez-le en ligne. La structure est un point postérieur.

Alors, supposons que vous ayez une page comme celle-ci:

enter image description here

Il serait judicieux de compartimenter de manière à ce que tous les gestionnaires d'événements/classeurs liés à l'en-tête soient présents, afin de faciliter la maintenance (sans avoir à parcourir 1 000 lignes).

Vous pouvez ensuite utiliser un outil tel que Grunt pour reconstruire votre JS en une seule unité.

Étape 1a: gestion des dépendances

Utilisez une bibliothèque telle que RequireJS ou CommonJS pour implémenter quelque chose appelé AMD . Le chargement de module asynchrone vous permet d’énoncer explicitement la nature de votre code, ce qui vous permet ensuite de décharger l’appel de bibliothèque vers le code. Vous pouvez simplement dire littéralement "Cela nécessite jQuery" et AMD le chargera et exécutera votre code lorsque jQuery est disponible.

Cela a aussi un joyau caché: le chargement de la bibliothèque sera fait à la seconde où le DOM est prêt, pas avant. Cela ne stoppe plus le chargement de votre page!

Étape 2: Modulariser

Voir le fil de fer? J'ai deux blocs d'annonces. Ils auront probablement des auditeurs d'événements partagés.

Dans cette étape, votre tâche consiste à identifier les points de répétition dans votre code et à essayer de synthétiser tout cela en modules. Les modules, maintenant, engloberont tout. Nous partagerons les choses au fur et à mesure.

L'idée de cette étape est de passer de l'étape 1 à la suppression de tous les copieurs-pâtes, pour les remplacer par des unités faiblement couplées. Donc, au lieu d'avoir:

ad_unit1.js

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

ad_unit2.js

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

J'aurai:

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

Ce qui vous permet de compartimenter votre événements et votre balises en plus de vous débarrasser de la répétition. C'est une étape assez décente et nous allons l'étendre plus tard.

Étape 3: Choisissez un cadre!

Si vous souhaitez modulariser et réduire davantage les répétitions, il existe de nombreux frameworks géniaux autour de la mise en œuvre d'approches MVC (Model-View-Controller). Mon préféré est Backbone/Spine, cependant, il y a aussi Angular, Yii, ... La liste est longue.

Un Modèle représente vos données.

A View représente votre marge et tous les événements qui lui sont associés

A Controller représente votre logique métier. En d'autres termes, le contrôleur indique à la page les vues à charger et les modèles à utiliser.

Ce sera une étape d'apprentissage importante, mais le prix en vaut la peine: il favorise un code propre et modulaire par rapport aux spaghettis.

Il y a beaucoup d'autres choses que vous pouvez faire, ce ne sont que des lignes directrices et des idées.

Changements spécifiques au code

Voici quelques améliorations spécifiques à votre code:

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

            }

        }

    });
 });

Ceci est mieux écrit comme:

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

Plus tôt dans votre code:

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

Soudain, vous avez le moyen de créer un calque standard à partir de n’importe où dans votre code sans copier-coller. Vous faites cela dans cinq endroits différents. Je viens de vous enregistrer cinq copies.

Un de plus:

// wrapper d'ensemble de règles pour les actions

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

C'est un moyen très puissant d'enregistrer des règles si vous avez des événements non standard ou des événements de création. Ceci est également un sérieux coup de pied lorsqu'il est combiné à un système de notification pub/sub et lorsqu'il est lié à un événement que vous déclenchez chaque fois que vous créez des éléments. Reliure d'événement modulaire Fire'n'forget!

93

Voici un moyen simple de scinder votre base de code actuelle en plusieurs fichiers, en utilisant require.js. Je vais vous montrer comment diviser votre code en deux fichiers. Ajouter plus de fichiers sera simple après cela.

Étape 1) En haut de votre code, créez un objet App (ou le nom de votre choix, comme MyGame):

var App = {}

Étape 2) Convertissez toutes vos variables et fonctions de niveau supérieur pour qu'elles appartiennent à l'objet App.

Au lieu de:

var selected_layer = "";

Tu veux:

App.selected_layer = "";

Au lieu de:

function getModified(){
...
}

Tu veux:

App.getModified = function() {

}

Notez qu'à ce stade, votre code ne fonctionnera que lorsque vous aurez terminé l'étape suivante.

Étape 3) Convertissez toutes les références de variable globale et de fonction pour qu'elles passent par App.

Changer des choses comme:

selected_layer = "."+classes[1];

à:

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

et:

getModified()

à:

App.GetModified()

Étape 4) Testez votre code à ce stade - tout devrait fonctionner. Vous aurez probablement quelques erreurs au début parce que vous avez oublié quelque chose, corrigez-les avant de continuer.

Étape 5) Configurez les requirejs. Je suppose que vous avez une page Web, servie à partir d'un serveur Web, dont le code est dans:

www/page.html

et jquery dans

www/js/jquery.js

Si ces chemins ne sont pas exactement comme ceux-ci, la procédure ci-dessous ne fonctionnera pas et vous devrez les modifier.

Téléchargez requirejs et mettez require.js dans votre www/js répertoire.

dans votre page.html, supprimez toutes les balises de script et insérez une balise de script du type:

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

créer www/js/main.js avec contenu:

require.config({
 "shim": {
   'jquery': { exports: '$' }
 }
})

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

puis mettez tout le code que vous venez de corriger aux étapes 1 à 3 (dont la seule variable globale devrait être App) dans:

www/js/app.js

Tout en haut de ce fichier, mettez:

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

Au bas mis:

})

Puis chargez page.html dans votre navigateur. Votre application devrait fonctionner!

Étape 6) Créez un autre fichier.

Voici où votre travail porte ses fruits, vous pouvez le faire encore et encore.

Extrayez du code de www/js/app.js qui fait référence à $ et à App.

par exemple.

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

Mettre dans www/js/foo.js

Tout en haut de ce fichier, mettez:

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

Au bas mis:

})

Puis changez la dernière ligne de www/js/main.js en:

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

C'est ça! Faites cela chaque fois que vous voulez mettre du code dans son propre fichier!

13
Lyn Headley

Pour votre question et vos commentaires, je suppose que vous n'êtes pas disposé à transférer votre code dans un framework tel que Backbone, ou à utiliser une bibliothèque de chargement telle que Require. Vous voulez juste un meilleur moyen d'organiser le code que vous avez déjà, de la manière la plus simple possible.

Je comprends qu'il est ennuyeux de faire défiler plus de 2000 lignes de code pour trouver la section sur laquelle vous souhaitez travailler. La solution consiste à diviser votre code en différents fichiers, un pour chaque fonctionnalité. Par exemple sidebar.js, canvas.js etc. Ensuite, vous pouvez les joindre pour la production avec Grunt. Avec Usemin, vous pouvez obtenir quelque chose comme ceci:

Dans votre html:

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

Dans votre Gruntfile:

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

Si vous voulez utiliser Yeoman, il vous donnera un code passe-partout pour tout cela.

Ensuite, pour chaque fichier, vous devez vous assurer que vous suivez les meilleures pratiques et que tout le code et les variables sont tous dans ce fichier et ne dépendent pas d'autres fichiers. Cela ne signifie pas que vous ne pouvez pas appeler les fonctions d'un fichier à partir d'un autre, il s'agit simplement d'encapsuler des variables et des fonctions. Quelque chose de semblable à l'espacement de noms. Je suppose que vous ne voulez pas que tout votre code soit orienté objet, mais si cela ne vous dérange pas de refactoriser un peu, je vous conseillerais d'ajouter quelque chose d'équivalent avec ce qu'on appelle un modèle de module. Cela ressemble à quelque chose comme ça:

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

Ensuite, vous pouvez charger ce morceau de code comme ceci:

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

Cela vous permettra d'avoir un code beaucoup plus facile à gérer sans avoir à trop réécrire votre code.

10
Jesús Carrera

Utilisez javascript MVC Framework pour organiser le code javascript de manière standard.

Les meilleurs frameworks JavaScript MVC disponibles sont:

La sélection d'un framework JavaScript MVC a nécessité de nombreux facteurs à prendre en compte. Lisez l'article de comparaison ci-après qui vous aidera à sélectionner le meilleur framework en fonction des facteurs importants pour votre projet: http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-) peut-ember /

Vous pouvez également utiliser RequireJS avec le framework pour prendre en charge le chargement de fichiers et de modules js asynchrone.
Regardez ci-dessous pour commencer le chargement du module JS:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/

6
Rohit Tailor

Classez votre code. Cette méthode m'aide beaucoup et fonctionne avec n'importe quel 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. e.g:
    var crr = null;
    $('::section.panel a').addEvent('click', function(E){
        if ( crr) {
            crr.hide();
        }
        crr = this.show();
    });
})();

Dans votre éditeur préféré (le meilleur est Komodo Edit), vous pouvez vous replier en réduisant toutes les entrées et vous ne verrez que les titres:

(function(){//HEADER: menu_____________________________________
(function(){//HEADER: location bar_____________________________
(function(){//FOOTER___________________________________________
(function(){//PANEL: interactive links. e.g:___________________
4
user2377782

Je voudrais suggerer:

  1. modèle éditeur/abonné pour la gestion des événements.
  2. orientation d'objet
  3. nom de domaine

Dans votre cas, Jessica, divisez l'interface en pages ou en écrans. Les pages ou les écrans peuvent être des objets et être étendus à partir de certaines classes parentes. Gérez les interactions entre les pages avec une classe PageManager.

3
Fazle Rabbi

Je vous suggère d'utiliser quelque chose comme Backbone. Backbone est une bibliothèque javascript prise en charge par RESTFUL. Ik rend votre code plus propre et plus lisible et est puissant lorsqu'il est utilisé avec requirejs.

http://backbonejs.org/

http://requirejs.org/

Backbone n'est pas une vraie bibliothèque. Il est destiné à structurer votre code javascript. Il est capable d'inclure d'autres bibliothèques comme jquery, jquery-ui, google-maps, etc. Backbone est, à mon avis, l'approche javascript la plus proche des structures de contrôleurs orientés objet et vue modèle.

Aussi en ce qui concerne votre flux de travail .. Si vous construisez vos applications dans PHP utilisez la bibliothèque Laravel. Cela fonctionnera parfaitement avec Backbone s’il est utilisé avec le principe RESTfull. Ci-dessous le lien vers Laravel Framework et un tutoriel sur la construction d'API RESTfull:

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

http://laravel.com/

Ci-dessous, un tutoriel de nettuts. Nettuts a beaucoup de tutoriels de haute qualité:

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

2
Chris Visser

Il est peut-être temps que vous commenciez à mettre en œuvre tout un flux de travail de développement en utilisant des outils tels que yeoman http://yeoman.io/ . Cela vous aidera à contrôler toutes vos dépendances, votre processus de construction et, si vous le souhaitez, des tests automatisés. Cela demande beaucoup de travail, mais une fois mis en œuvre, les modifications futures seront beaucoup plus faciles.

0
drobson