Prácticas recomendadas comúnmente aceptadas en torno a la organización del código en JavaScript



Answers

Intento evitar incluir cualquier javascript con el HTML. Todo el código está encapsulado en clases y cada clase está en su propio archivo. Para el desarrollo, tengo etiquetas <script> separadas para incluir cada archivo js, ​​pero se fusionan en un solo paquete más grande para producción para reducir la sobrecarga de las solicitudes HTTP.

Normalmente, tendré un único archivo 'principal' js para cada aplicación. Entonces, si estuviera escribiendo una aplicación de "encuesta", tendría un archivo js llamado "survey.js". Esto contendría el punto de entrada en el código jQuery. Creo referencias de jQuery durante la creación de instancias y luego las paso a mis objetos como parámetros. Esto significa que las clases de JavaScript son 'puras' y no contienen ninguna referencia a identificadores de CSS o nombres de clases.

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

También encuentro que nombrar las convenciones es importante para la legibilidad. Por ejemplo: antepongo 'j' a todas las instancias de jQuery.

En el ejemplo anterior, hay una clase llamada DimScreen. (Supongamos que atenúa la pantalla y aparece un cuadro de alerta.) Necesita un elemento div que se pueda agrandar para cubrir la pantalla, y luego agregue un cuadro de alerta, así que paso un objeto jQuery. jQuery tiene un concepto de plug-in, pero parecía limitado (por ejemplo, las instancias no son persistentes y no se puede acceder) sin una ventaja real. Entonces, la clase DimScreen sería una clase javascript estándar que simplemente usa jQuery.

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

Construí algunas aplicaciones bastante complejas usando este enfoque.

Question

Como los frameworks de JavaScript como jQuery hacen que las aplicaciones web del lado del cliente sean más ricas y más funcionales, comencé a notar un problema ...

¿Cómo diablos mantienes esto organizado?

  • Pon todos tus controladores en un solo lugar y escribe funciones para todos los eventos.
  • Crear funciones / clases para envolver toda su funcionalidad?
  • Escribir como loco y solo espero que funcione para mejor?
  • ¿Renunciar y obtener una nueva carrera?

Menciono jQuery, pero en realidad es cualquier código JavaScript en general. Me doy cuenta de que a medida que las líneas y líneas comienzan a acumularse, es más difícil administrar los archivos de script o encontrar lo que está buscando. Posiblemente los problemas más grandes que he encontrado es que hay tantas maneras de hacer lo mismo, es difícil saber cuál es la mejor práctica comúnmente aceptada en la actualidad.

¿Hay alguna recomendación general sobre la mejor manera de mantener sus archivos .js tan agradables y ordenados como el resto de su aplicación? ¿O es solo una cuestión de IDE? ¿Hay una mejor opción por ahí?

EDITAR

Esta pregunta tenía como objetivo ser más acerca de la organización del código y no de la organización del archivo. Ha habido algunos buenos ejemplos de fusión de archivos o división de contenido.

Mi pregunta es: ¿cuál es la forma de mejores prácticas comúnmente aceptada actualmente para organizar su código real? ¿Cuál es tu manera, o incluso una forma recomendada de interactuar con elementos de página y crear código reutilizable que no entre en conflicto?

Algunas personas han enumerado espacios de nombres, lo cual es una buena idea. ¿Cuáles son algunas otras formas, más específicamente tratar con elementos en la página y mantener el código organizado y ordenado?




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. Ejemplo:

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.




Un buen director de OO + MVC definitivamente sería muy útil para administrar una aplicación compleja de JavaScript.

Básicamente, estoy organizando mi aplicación y javascript para el siguiente diseño familiar (que existe desde los días de programación de mi escritorio a la Web 2.0)

Descripción para los valores numéricos en la imagen:

  1. Widgets que representan las vistas de mi aplicación. Esto debe ser extensible y separado, lo que resulta en una buena separación que MVC intenta lograr en lugar de convertir mi widget en un código de espagueti (equivalente en la aplicación web de poner un gran bloque de Javascript directamente en HTML). Cada widget se comunica a través de otros escuchando el evento generado por otros widgets, reduciendo así el fuerte acoplamiento entre widgets que podría llevar a un código inmanejable (¿recuerdas el día de agregar onclick en todas partes apuntando a funciones globales en la etiqueta de script? Urgh ...)
  2. Los modelos de objetos que representan los datos que quiero rellenar en los widgets y que pasan de ida y vuelta al servidor. Al encapsular los datos en su modelo, la aplicación se convierte en agnóstico de formato de datos. Por ejemplo: mientras Naturalmente en Javascript, estos modelos de objetos están mayormente serializados y deserializados en JSON, si de alguna manera el servidor usa XML para la comunicación, todo lo que necesito cambiar es cambiar la capa de serialización / deserialización y no necesariamente cambiar todas las clases de widgets .
  3. Clases de controlador que administran la lógica de negocios y la comunicación con el servidor + ocasionalmente capa de caché. Esta capa controla el protocolo de comunicación del servidor y pone los datos necesarios en los modelos de objetos
  4. Las clases se envuelven prolijamente en sus espacios de nombres correspondientes. Estoy seguro de que todos sabemos lo desagradable que puede ser el espacio de nombre global en Javascript.

En el pasado, separaba los archivos en su propio js y uso la práctica común para crear principios de OO en Javascript. El problema es que pronto descubrí que hay varias formas de escribir JS OO y que no necesariamente todos los miembros del equipo tienen el mismo enfoque. A medida que el equipo se hizo más grande (en mi caso, más de 15 personas), esto se complica ya que no existe un enfoque estándar para Javascript orientado a objetos. Al mismo tiempo, no quiero escribir mi propio marco y repetir algunos de los trabajos que estoy seguro que son personas más inteligentes de las que he resuelto.

jQuery es increíblemente bueno como Javascript Framework y me encanta, sin embargo, a medida que el proyecto se hace más grande, claramente necesito una estructura adicional para mi aplicación web, especialmente para facilitar la práctica estandarizada de OO. Para mí, después de varios experimentos, encuentro que la infraestructura de YUI3 Base y Widget ( http://yuilibrary.com/yui/docs/widget/ y http://yuilibrary.com/yui/docs/base/index.html ) proporciona exactamente lo que necesito Pocas razones por las que los uso.

  1. Proporciona soporte de Namespace. Una necesidad real de OO y una organización ordenada de tu código
  2. Apoya la noción de clases y objetos
  3. Proporciona un medio estandarizado para agregar variables de instancia a su clase
  4. Es compatible con la extensión de clase ordenadamente
  5. Proporciona constructor y destructor
  6. Proporciona enlace de renderizado y evento
  7. Tiene un marco de widget base
  8. Cada artilugio ahora puede comunicarse entre sí utilizando un modelo estándar basado en eventos
  9. Lo más importante es que brinda a todos los ingenieros un estándar OO para el desarrollo de Javascript

A diferencia de muchas vistas, no necesariamente tengo que elegir entre jQuery y YUI3. Estos dos pueden coexistir pacíficamente. Si bien YUI3 proporciona la plantilla de OO necesaria para mi aplicación web compleja, jQuery aún proporciona a mi equipo una abstracción JS fácil de usar que todos conocemos y amamos.

Utilizando YUI3, he logrado crear un patrón MVC separando las clases que extienden la Base como el Modelo, las clases que amplían el Widget como una Vista y, por supuesto, tiene clases de Controladores que hacen las llamadas lógicas y de servidor necesarias.

Los widgets se pueden comunicar entre ellos usando un modelo basado en eventos y escuchando el evento y realizando la tarea necesaria en base a una interfaz predefinida. En pocas palabras, poner la estructura OO + MVC a JS es una alegría para mí.

Solo un descargo de responsabilidad, no trabajo para Yahoo! y simplemente un arquitecto que intenta lidiar con el mismo problema planteado por la pregunta original. Creo que si alguien encuentra un marco OO equivalente, esto también funcionaría. Principalmente, esta pregunta también se aplica a otras tecnologías. Gracias a Dios por todas las personas que diseñaron OO Principles + MVC para que nuestros días de programación sean más manejables.




Dojo tenía el sistema de módulos desde el primer día. De hecho, se considera que es una piedra angular de Dojo, el pegamento que lo mantiene todo junto:

Usando módulos, Dojo logra los siguientes objetivos:

  • Espacios de nombres para código Dojo y código personalizado ( dojo.declare() ): no contamine el espacio global, coexista con otras bibliotecas y el código no compatible con Dojo del usuario.
  • Cargando módulos de forma síncrona o asíncrona por nombre ( dojo.require() ).
  • Desarrollos personalizados mediante el análisis de dependencias de módulos para crear un único archivo o un grupo de archivos interdependientes (las denominadas capas) para incluir solo lo que su aplicación web necesita. Las versiones personalizadas también pueden incluir módulos Dojo y módulos proporcionados por el cliente.
  • Acceso transparente basado en CDN a Dojo y el código del usuario. Tanto AOL como Google llevan Dojo de esta manera, pero algunos clientes también lo hacen por sus aplicaciones web personalizadas.



Mi jefe todavía habla de los tiempos cuando escribieron un código modular (lenguaje C) y se queja de cuán malo es el código hoy en día. Se dice que los programadores pueden escribir ensamblajes en cualquier marco. Siempre hay una estrategia para superar la organización del código. El problema básico es con chicos que tratan el guión java como un juguete y nunca intentan aprenderlo.

En mi caso, escribo archivos js en un tema UI o en una pantalla de aplicación, con una init_screen () adecuada. Utilizando la convención de nomenclatura de identificación adecuada, me aseguro de que no haya conflictos de espacio de nombres en el nivel del elemento raíz. En el discreto window.load (), relaciono las cosas según el ID de nivel superior.

Uso estrictamente los cierres y patrones de script java para ocultar todos los métodos privados. Después de hacer esto, nunca se enfrentó a un problema de propiedades conflictivas / definiciones de funciones / definiciones de variables. Sin embargo, cuando se trabaja con un equipo, a menudo es difícil imponer el mismo rigor.




Lazy Carga el código que necesitas bajo demanda. Google hace algo como esto con su google.loader




Inspirado por publicaciones anteriores, hice una copia de los directorios de Rakefile y del proveedor distribuidos con WysiHat (un RTE mencionado por changelog) e hice algunas modificaciones para incluir la verificación de código con JSLint y la JSLint con YUI Compressor .

La idea es usar Sprockets (de WysiHat) para combinar múltiples JavaScripts en un solo archivo, verificar la sintaxis del archivo fusionado con JSLint y minificarlo con YUI Compressor antes de la distribución.

Requisitos previos

  • Java Runtime
  • gema de rubí y rastrillo
  • Debes saber cómo poner un JAR en Classpath

Ahora haz

  1. Descarga Rhino y coloca el JAR ("js.jar") en tu classpath
  2. Descarga YUI Compressor y coloca el JAR (build / yuicompressor-xyz.jar) en tu classpath
  3. Descargue WysiHat y copie el directorio "proveedor" a la raíz de su proyecto JavaScript
  4. Descargue JSLint for Rhino y póngalo dentro del directorio "proveedor"

Ahora crea un archivo llamado "Rakefile" en el directorio raíz del proyecto JavaScript y agregale el siguiente contenido:

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

Si hiciste todo correctamente, deberías poder usar los siguientes comandos en tu consola:

  • rake merge - para fusionar diferentes archivos de JavaScript en uno
  • rake check - para verificar la sintaxis de su código (esta es la tarea predeterminada , por lo que puede simplemente escribir rake )
  • rake minify : para preparar una versión reducida de su código JS

En la fusión de fuentes

Utilizando Sprockets, el preprocesador JavaScript puede incluir (o require ) otros archivos JavaScript. Use la siguiente sintaxis para incluir otras secuencias de comandos desde el archivo inicial (llamado "main.js", pero puede cambiar eso en el 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
})();

Y entonces...

Eche un vistazo a Rakefile provisto con WysiHat para configurar las pruebas automatizadas de la unidad. Lindas cosas :)

Y ahora la respuesta

Esto no responde muy bien a la pregunta original. Lo sé y lo siento, pero lo publiqué aquí porque espero que pueda ser útil para otra persona organizar su desorden.

Mi enfoque del problema es hacer todo el modelado orientado a objetos que pueda y separar implementaciones en diferentes archivos. Entonces los manejadores deben ser lo más cortos posible. El ejemplo con List singleton también es bueno.

Y espacios de nombres ... bueno, pueden ser imitados por una estructura de objetos más profunda.

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

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

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

No soy muy fanático de las imitaciones, pero esto puede ser útil si tiene muchos objetos que le gustaría mover fuera del alcance global.




Para la organización de JavaScript ha estado usando lo siguiente

  1. Carpeta para todos tus javascript
  2. El javascript de nivel de página obtiene su propio archivo con el mismo nombre de la página. ProductDetail.aspx sería ProductDetail.js
  3. Dentro de la carpeta javascript para archivos de biblioteca tengo una carpeta lib
  4. Coloque funciones de biblioteca relacionadas en una carpeta de lib que desee usar en su aplicación.
  5. Ajax es el único javascript que muevo fuera de la carpeta javascript y obtiene su propia carpeta. Luego agrego dos subcarpetas cliente y servidor
  6. La carpeta del cliente obtiene todos los archivos .js, mientras que la carpeta del servidor obtiene todos los archivos del lado del servidor.



En mi último proyecto -Viajeros.com- he usado una combinación de varias técnicas. No sabría cómo organizar una aplicación web: Viajeros es un sitio de redes sociales para viajeros con secciones bien definidas, por lo que es fácil separar el código para cada área.

Utilizo la simulación del espacio de nombres y la carga diferida de los módulos de acuerdo con la sección del sitio. En cada carga de página declaro un objeto "vjr", y siempre cargo un conjunto de funciones comunes (vjr.base.js). Luego, cada página HTML decide qué módulos necesitan con un simple:

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

Vjr.base.js obtiene cada uno comprimido del servidor y los ejecuta.

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

Cada "módulo" tiene esta estructura:

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

Dado mi conocimiento limitado de Javascript, sé que debe haber mejores formas de administrar esto, pero hasta ahora nos está funcionando muy bien.




Hace unos días, los chicos de 37Signals WysiHat , con un giro. Hicieron una biblioteca que agrupa los archivos javascript utilizando una especie de comandos de pre-procesador.

Lo he usado desde entonces para separar mis archivos JS y luego, al final, fusionarlos como uno solo. De esa forma puedo separar preocupaciones y, al final, tener solo un archivo que pase por el conducto (gzip, nada menos).

En sus plantillas, verifique si se encuentra en modo de desarrollo, e incluya los archivos por separado, y si está en producción, incluya el final (que tendrá que "crear").




Seguir los principios de diseño de OO y los patrones de diseño es muy útil para hacer que su código sea fácil de mantener y comprender. Pero una de las mejores cosas que he descubierto recientemente son las señales y las ranuras, también conocido como publicar / suscribir. Eche un vistazo a http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html para una implementación simple de jQuery.

La idea se usa bien en otros idiomas para el desarrollo de GUI. Cuando sucede algo significativo en algún lugar de su código, publica un evento sintético global al que otros métodos en otros objetos pueden suscribirse. Esto proporciona una excelente separación de objetos.

Creo que Dojo (¿y Prototype?) Tienen una versión integrada de esta técnica.

ver también ¿Qué son señales y ranuras?




"¿Escribir como loco y esperar que funcione para mejor?", He visto un proyecto como este que fue desarrollado y mantenido por solo 2 desarrolladores, una gran aplicación con muchos códigos JavaScript. Además de eso, había diferentes atajos para cada función de jquery posible que se pueda imaginar. Sugerí que organizaran el código como complementos, ya que ese es el equivalente de jquery de clase, módulo, espacio de nombres ... y todo el universo. Pero las cosas empeoraron, ahora comenzaron a escribir complementos reemplazando cada combinación de 3 líneas de código utilizadas en el proyecto. Personalmente, creo que jQuery es el demonio y no debería usarse en proyectos con muchos javascript porque te anima a ser flojo y no se piensa en organizar el código de ninguna manera. Prefiero leer 100 líneas de javascript que una línea con 40 funciones jQuery encadenadas (no estoy bromeando). Contrariamente a la creencia popular, es muy fácil organizar código JavaScript en equivalentes a espacios de nombres y clases. Eso es lo que hacen YUI y Dojo. Puede enrollar fácilmente el suyo si quiere. Encuentro el enfoque de YUI mucho mejor y más eficiente. Pero normalmente necesita un buen editor con soporte para fragmentos para compensar las convenciones de nombres de YUI si quiere escribir algo útil.




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.







Related