Mejores prácticas comúnmente aceptadas sobre la organización de código en JavaScript




14 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 la producción para reducir la sobrecarga de las solicitudes HTTP.

Normalmente, tendré un solo archivo js 'principal' 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 los identificadores de CSS o los nombres de clase.

// 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 me parece que la convención de nombres es importante para la legibilidad. Por ejemplo: antepongo 'j' a todas las instancias de jQuery.

En el ejemplo anterior, hay una clase llamada DimScreen. (Suponga que esto oscurece la pantalla y aparece un cuadro de alerta). Necesita un elemento div que pueda ampliar para cubrir la pantalla, y luego agrega un cuadro de alerta, por lo que paso un objeto jQuery. jQuery tiene un concepto de plug-in, pero parecía limitante (por ejemplo, las instancias no son persistentes y no se puede acceder a ellas) sin una ventaja real. Así que la clase DimScreen sería una clase estándar de javascript 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>');
  //...
};

He construido algunas aplicaciones bastante complejas utilizando este enfoque.

javascript jquery design-patterns architecture formatting

A medida que los marcos 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 en el mundo mantienes esto organizado?

  • ¿Poner todos los manejadores en un solo lugar y escribir funciones para todos los eventos?
  • ¿Crear funciones / clases para envolver todas tus funcionalidades?
  • Escribe como un loco y solo espero que funcione para lo mejor?
  • ¿Te rindes y consigues una nueva carrera?

Menciono jQuery, pero en realidad es cualquier código JavaScript en general. Estoy encontrando que a medida que las líneas sobre líneas comienzan a acumularse, se vuelve 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 muchas maneras de hacer lo mismo, es difícil saber cuál es la mejor práctica aceptada actualmente.

¿Hay recomendaciones generales 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 pretendía ser más sobre la organización del código y no sobre la organización de archivos. Ha habido algunos ejemplos realmente buenos de fusión de archivos o división de contenido.

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

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




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

La idea es usar Sprockets (de WysiHat) para combinar varios JavaScripts en un archivo, verificar la sintaxis del archivo combinado con JSLint y minimizarlo con YUI Compressor antes de la distribución.

Prerrequisitos

  • 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" en la raíz de su proyecto JavaScript
  4. Descargue JSLint for Rhino y colóquelo en el directorio "proveedor"

Ahora cree un archivo llamado "Rakefile" en el directorio raíz del proyecto JavaScript y agregue 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 hizo todo correctamente, debería poder usar los siguientes comandos en su consola:

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

En la fusión de la fuente

Usando Sprockets, el preprocesador de JavaScript puede incluir (o require ) otros archivos JavaScript. Use la siguiente sintaxis para incluir otros scripts del 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 proporcionado con WysiHat para configurar la prueba de la unidad automatizada. Lindas cosas :)

Y ahora la respuesta.

Esto no responde muy bien a la pregunta original. Lo sé y lo lamento, pero lo he publicado aquí porque espero que pueda ser útil para alguien más organizar su desorden.

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

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

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

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

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

No soy un gran fanático de las imitaciones, pero esto puede ser útil si tiene muchos objetos que le gustaría salir del ámbito global.




Seguir los buenos principios de diseño de OO y los patrones de diseño ayuda a que su código sea fácil de mantener y entender. Pero una de las mejores cosas que he descubierto recientemente son las señales y las máquinas tragamonedas, también conocidas como publicación / suscripción. Eche un vistazo a http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html para obtener una implementación jQuery simple.

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

Creo que Dojo (y prototipo?) Tienen una versión incorporada de esta técnica.

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




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

Usando módulos Dojo logra los siguientes objetivos:

  • Los espacios de nombres para el código Dojo y el código personalizado ( dojo.declare() ): no contaminan el espacio global, coexisten con otras bibliotecas y con el código que no dojo.declare() Dojo del usuario.
  • Cargando módulos de forma síncrona o asíncrona por nombre ( dojo.require() ).
  • Compilaciones personalizadas al analizar las dependencias de los módulos para crear un solo archivo o un grupo de archivos interdependientes (las llamadas capas) para incluir solo lo que su aplicación web necesita. Las compilaciones personalizadas pueden incluir módulos Dojo y también módulos proporcionados por el cliente.
  • Acceso transparente basado en CDN a Dojo y al código del usuario. Tanto AOL como Google llevan a Dojo de esta manera, pero algunos clientes también lo hacen para sus aplicaciones web personalizadas.



¡Mi jefe aún habla de los tiempos en que escribieron el 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 framework. Siempre hay una estrategia para superar la organización del código. El problema básico es con los chicos que tratan el java script como un juguete y nunca intentan aprenderlo.

En mi caso, escribo archivos js sobre una base de pantalla de aplicación o tema de UI, con un init_screen () adecuado. Al utilizar la convención de nomenclatura de ID adecuada, me aseguro de que no haya conflictos de espacio de nombres en el nivel del elemento raíz. En el window.load () no intrusivo, até las cosas en función del ID de nivel superior.

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




"¿Escribe como loco y solo espero que funcione de la mejor manera?", He visto un proyecto como este que fue desarrollado y mantenido por solo 2 desarrolladores, una gran aplicación con un montón de código javascript. Además de eso, había diferentes accesos directos para cada posible función de jQuery que puedas imaginar. Sugerí que organizaran el código como complementos, ya que 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 que reemplazaban 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 perezoso ya no pensar en organizar 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 el código javascript en equivalentes a los espacios de nombres y las clases. Eso es lo que hacen YUI y Dojo. Usted puede rodar fácilmente su propio si lo desea. Encuentro el enfoque de YUI mucho mejor y eficiente. Pero normalmente necesitas un buen editor con soporte para fragmentos para compensar las convenciones de nomenclatura YUI si quieres escribir algo útil.




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 de cada área.

Utilizo la simulación de espacio de nombres y la carga lenta de módulos de acuerdo con la sección del sitio. En cada carga de página declaro un objeto "vjr" y siempre le cargué 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 de los archivos comprimidos desde el 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 gestionar esto, pero hasta ahora está funcionando muy bien para nosotros.




Un buen director de OO + MVC definitivamente ayudaría a administrar una aplicación javascript compleja.

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 hasta la Web 2.0)

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

  1. Widgets que representan las vistas de mi aplicación. Esto debería ser extensible y separado de manera clara, lo que resultaría en una buena separación que MVC intenta lograr en lugar de convertir mi widget en un código spaghetti (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 al escuchar el evento generado por otros widgets, lo que reduce el fuerte acoplamiento entre los widgets que podrían generar un código inmanejable (¿recuerda el día de agregar onclick en todas partes que apunta a funciones globales en la etiqueta de script? Urgh ...)
  2. Modelos de objetos que representan los datos que quiero llenar en los widgets y que van y vienen al servidor. Al encapsular los datos a su modelo, la aplicación se convierte en agnósticos del formato de datos. Por ejemplo: si bien, naturalmente, en Javascript, estos modelos de objetos están en su mayoría serializados y deserializados en JSON, si de alguna manera el servidor utiliza XML para la comunicación, todo lo que debo cambiar es cambiar la capa de serialización / deserialización y no necesariamente tiene que 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, la capa de caché Esta capa controla el protocolo de comunicación al servidor y coloca los datos necesarios en los modelos de objetos.
  4. Las clases están envueltas cuidadosamente en sus espacios de nombres correspondientes. Estoy seguro de que todos sabemos lo desagradable que puede ser el espacio de nombres global en Javascript.

En el pasado, separaba los archivos en sus propios js y usaba la práctica común para crear principios OO en Javascript. El problema que pronto descubrí es que hay varias formas de escribir JS OO y no es necesariamente que todos los miembros del equipo tengan 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 hay 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 que las que he resuelto.

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

  1. Proporciona soporte de espacio de nombres. Una necesidad real de OO y una organización ordenada de su código.
  2. Apoya la noción de clases y objetos.
  3. Proporciona un medio de estandarización para agregar variables de instancia a su clase.
  4. Es compatible con la extensión de clase perfectamente
  5. Proporciona constructor y destructor.
  6. Proporciona render y enlace de eventos.
  7. Tiene un framework base de widgets.
  8. Cada widget ahora puede comunicarse entre sí mediante un modelo estándar basado en eventos
  9. Lo más importante es que le da a todos los ingenieros un estándar OO para el desarrollo de Javascript

Contrariamente a muchas vistas, no necesariamente tengo que elegir entre jQuery y YUI3. Estos dos pueden coexistir pacíficamente. Si bien YUI3 proporciona la plantilla OO necesaria para mi aplicación web compleja, jQuery aún le brinda a mi equipo una abstracción JS fácil de usar con la que todos nos enamoramos y estamos familiarizados.

Usando YUI3, he logrado crear un patrón MVC al separar las clases que extienden la Base como el Modelo, las clases que extienden el Widget como una Vista y, por supuesto, tiene clases de Controladores que están haciendo las llamadas necesarias de la lógica y del servidor.

Widget puede comunicarse entre sí utilizando un modelo basado en eventos y escuchando el evento y haciendo la tarea necesaria basada en una interfaz predefinida. En pocas palabras, poner la estructura de OO + MVC en JS es una alegría para mí.

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




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

Lo he estado usando desde entonces para separar mis archivos JS y luego, al final, fusionarlos como uno solo. De esa manera puedo separar las preocupaciones y, al final, tener solo un archivo que pasa por la tubería (gzipped, no menos).

En sus plantillas, verifique si está en modo de desarrollo e incluya los archivos separados, y si está en producción, incluya el último (que tendrá que "construir" usted mismo).







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

  1. Carpeta para todo tu javascript
  2. El nivel de página javascript 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 las funciones de la biblioteca relacionadas en una carpeta lib que desee usar en toda la aplicación.
  5. Ajax es el único javascript que muevo fuera de la carpeta de 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.



Puede usar jquery mx (usado en javascriptMVC), que es un conjunto de scripts que le permite usar modelos, vistas y controladores. Lo usé en un proyecto y me ayudó a crear javascript estructurado, con tamaños de script mínimos debido a la compresión. Este es un ejemplo de controlador:

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

También puede usar solo el lado del controlador de jquerymx si no está interesado en la vista y las partes del modelo.




Utilizo un script personalizado inspirado en el comportamiento de Ben Nolan (lamentablemente, no puedo encontrar un enlace actual a esto) para almacenar la mayoría de los controladores de eventos. Estos controladores de eventos son activados por los elementos className o Id, por ejemplo. Ejemplo:

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

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

});

Me gusta incluir la mayoría de mis bibliotecas de Javascript sobre la marcha, excepto las que contienen un comportamiento global. Utilizo el marcador de posición headScript () de Zend Framework para esto, pero también puede usar javascript para cargar otros scripts sobre la marcha con Ajile por ejemplo.




Lazy Cargar el código que necesita bajo demanda. Google hace algo así con su google.loader






Related