Mejores prácticas comúnmente aceptadas en torno a la organización del código en JavaScript [cerrado]

561

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

¿Cómo demonios mantienes esto organizado?

  • ¿Poner todos sus controladores en un solo lugar y escribir funciones para todos los eventos?
  • ¿Crear funciones / clases para envolver toda su funcionalidad?
  • ¿Escribir como loco y solo esperar que funcione mejor?
  • ¿Renunciar y obtener una nueva carrera?

Menciono jQuery, pero en realidad es cualquier código JavaScript en general. Estoy descubriendo que a medida que las líneas comienzan a acumularse, se hace más difícil administrar los archivos de script o encontrar lo que está buscando. Posiblemente, el mayor problema que he encontrado es que hay tantas maneras de hacer lo mismo, que es difícil saber cuál es la mejor práctica actual comúnmente aceptada.

¿Hay alguna recomendación general sobre la mejor manera de mantener sus archivos .js tan bonitos 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 forma de práctica recomendada 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 código reutilizable que no entre en conflicto?

Algunas personas han incluido espacios de nombres, lo cual es una buena idea. ¿De qué otras maneras se trata más específicamente con elementos en la página y mantener el código organizado y ordenado?

Hugoware
fuente
alguien que realmente se tomó el tiempo para hablar sobre la organización del código en sí, no "solo" qué herramienta usa para concatenar y comprimir sus archivos JS: stackoverflow.com/questions/16736483/…
Adrien Be

Respuestas:

183

Sería mucho mejor si JavaScript tuviera espacios de nombres integrados, pero creo que organizar cosas como las que describe Dustin Díaz aquí me ayuda mucho.

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

Puse diferentes "espacios de nombres" y, a veces, clases individuales en archivos separados. Por lo general, comienzo con un archivo y cuando una clase o espacio de nombres se vuelve lo suficientemente grande como para garantizarlo, lo separo en su propio archivo. Usar una herramienta para combinar todos sus archivos para la producción también es una excelente idea.

polarbear
fuente
24
Normalmente me refiero a eso como "la forma de Crockford". +1 de mi parte
Matt Briggs
44
Incluso puedes ir un poco más lejos. Ver este enlace: wait-till-i.com/2007/08/22/…
MKroehnert
44
@MattBriggs de lo contrario se llama module patterny se basa en el IIFE pattern.
Adrien Be
¿No necesitas exportar clases de alguna manera? ¿Cómo se crea un objeto desde el exterior de dicho módulo? ¿O debería haber un método createNewSomething()en el objeto de retorno, por lo que la creación de objetos ocurre únicamente dentro del módulo? Hm ... esperaría que las clases (constructores) sean visibles desde el exterior.
robsch
@robsch Su ejemplo no toma ningún parámetro, pero la mayoría lo haría. Vea mi ejemplo aquí para ver cómo se hace esto normalmente (TypeScript, pero 99% igual): repl.it/@fatso83/Module-Pattern-in-TypeScript
oligofren
88

Intento evitar incluir 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 estaba 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 javascript son 'puras' y no contienen referencias a identificadores 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 la convención de nomenclatura 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 atenúa la pantalla y abre un cuadro de alerta). Necesita un elemento div que se pueda ampliar para cubrir la pantalla, y luego agregar un cuadro de alerta, por lo que paso un objeto jQuery. jQuery tiene un concepto de complemento, pero parecía limitante (por ejemplo, las instancias no son persistentes y no se puede acceder a ellas) sin una ventaja real. Entonces, 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 creado algunas aplicaciones bastante complejas utilizando este enfoque.

Jason Moore
fuente
15
Encuentro que usar $como prefijo de nombre de variable es una práctica más común, pero podría estar equivocado. Entonces, en $s = $('...')lugar de jS = $('...'), solo es una cuestión de preferencia, supongo. Sin embargo, es interesante, ya que se cree que la notación húngara es un olor a código. Es extraño cuán diferentes son algunas de mis convenciones / preferencias de código JavaScript para mis convenciones de codificación C # / Java.
jamiebarrow
99
@jamie No es un olor a código en este caso, es precisamente uno de los pocos casos en que el húngaro es bueno . Es posible que desee leer esto .
Dan Abramov
3
@DanAbramov gracias por el enlace. Realmente debo leer todos los blogs de Joel, él explica muy bien las cosas. Definitivamente merece la fama / reputación que tiene. Me referiré a Systems Hungariancomo un olor de código y Apps Hungariancomo una práctica a partir de ahora :)
jamiebarrow
Supongo que en el mundo de C #, también podría ser un gran artículo para promover el uso var, ahora que lo pienso. La mayoría de los argumentos en contra del uso vares donde no estará seguro del 'tipo' de lo que se devuelve, pero supongo que el argumento debería estar en contra de no conocer la 'clase' de lo que se devuelve. Si usa Apps Hungarian, entonces no debería tener esa preocupación ... interesante.
jamiebarrow
3
@Marnen: veo su punto, pero no es inútil como guía para el programador. El prefijo $ me recuerda qué es cuando leo mi código más tarde y, por lo tanto, ayuda a una comprensión más rápida.
Sean
39

Puede dividir sus scripts en archivos separados para el desarrollo, luego crear una versión de "lanzamiento" donde los reúna todos y ejecute YUI Compressor o algo similar en él.

Greg
fuente
A veces hay scripts de JavaScript innecesarios. Es un desperdicio enviarlos al cliente. Creo que es mejor enviar solo lo que se necesita. Por supuesto, para una aplicación web que se usa todo el día, como una aplicación de intranet, podría ser mejor enviar todo el lote de una vez, en la primera carga de la página.
DOK
2
La compilación @DOK debe incluir la escisión de cosas no utilizadas.
aehlke
También existe un concepto de carga diferida para intentar reducir las necesidades de ancho de banda, donde carga la página inicial y luego realiza una carga asincrónica de los archivos de script necesarios (como se menciona en otras respuestas a esta pregunta). Sin embargo, eso puede requerir más solicitudes y en realidad podría ser menos utilizable. @DOK, si el JS está en caché, una solicitud mediana puede ser mejor que algunas pequeñas.
jamiebarrow
27

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

La idea es usar Sprockets (de WysiHat) para fusionar múltiples JavaScripts en un solo 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
  • Debe saber cómo poner un JAR en Classpath

Ahora haz

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

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 , por lo que simplemente puede escribir rake)
  • rake minify - para preparar una versión reducida de su código JS

En fusión de fuente

Usando Sprockets, el preprocesador de JavaScript puede incluir (u 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 unidad automatizada de pruebas. Lindas cosas :)

Y ahora por la respuesta

Esto no responde muy bien la pregunta original. Lo sé y lo siento, pero lo he publicado aquí porque espero que pueda ser útil para que otra persona organice 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 Listsingleton también es agradable.

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 un gran admirador de las imitaciones, pero esto puede ser útil si tiene muchos objetos que le gustaría mover fuera del alcance global.

Damir Zekić
fuente
18

La organización del código requiere la adopción de convenciones y estándares de documentación:
1. Código de espacio de nombres para un archivo físico;

Exc = {};


2. Clases de grupo en estos espacios de nombres javascript;
3. Establecer prototipos o funciones o clases relacionadas para representar objetos del mundo real;

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


4. Establecer convenciones para mejorar el código. Por ejemplo, agrupe todas sus funciones o métodos internos en su atributo de clase de un tipo de objeto.

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


5. Realizar documentación de espacios de nombres, clases, métodos y variables. Cuando sea necesario, también discuta parte del código (algunas FI y Fors, generalmente implementan una lógica importante del código).

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


Estos son solo algunos consejos, pero eso ha ayudado enormemente a organizar el código. ¡Recuerde que debe tener disciplina para tener éxito!

Nery Jr
fuente
13

Seguir buenos principios de diseño OO y patrones de diseño ayuda mucho a 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 conocidas como publicación / suscripción. 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 pueden suscribirse otros métodos en otros objetos. Esto proporciona una excelente separación de los objetos.

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

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

revs meouw
fuente
He hecho esto en jQuery. JS tiene un modelo de evento incorporado, por lo que realmente no necesita mucho soporte de marco.
Marnen Laibow-Koser
12

Pude aplicar con éxito el Patrón del Módulo Javascript a una aplicación Ext JS en mi trabajo anterior. Proporcionó una manera simple de crear código bien encapsulado.

Alan
fuente
11

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

Utilizando módulos, Dojo logra los siguientes objetivos:

  • Espacios de nombres para el código Dojo y el código personalizado ( dojo.declare()): no contamine el espacio global, coexista con otras bibliotecas y el código del usuario que no sea Dojo.
  • Carga de módulos de forma sincrónica o asincrónica por nombre ( dojo.require()).
  • Compilaciones personalizadas mediante el análisis de dependencias de 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 también pueden incluir módulos Dojo y módulos suministrados por el cliente.
  • Acceso transparente basado en CDN a Dojo y código de usuario. Tanto AOL como Google llevan Dojo de esta manera, pero algunos clientes también lo hacen para sus aplicaciones web personalizadas.
revs Eugene Lazutkin
fuente
9

Echa un vistazo a JavasciptMVC .

Usted puede :

  • divide tu código en capas de modelo, vista y controlador.

  • comprimir todo el código en un solo archivo de producción

  • autogenerar código

  • crear y ejecutar pruebas unitarias

  • y mucho más ...

Lo mejor de todo es que utiliza jQuery, por lo que también puede aprovechar otros complementos de jQuery.

andyuk
fuente
Sí, he usado jmvc y es bastante bueno, aunque los documentos podrían ser mejores
Meouw
9

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

En mi caso, escribo archivos js en un tema de UI o en una pantalla de aplicación, con un init_screen adecuado (). 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 la discreta ventana.load (), ato las cosas según la identificación de nivel superior.

Uso estrictamente los patrones y cierres de script java para ocultar todos los métodos privados. Después de hacer esto, nunca se enfrentó a 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.

questzen
fuente
9

Me sorprende que nadie haya mencionado los marcos MVC. He estado usando Backbone.js para modularizar y desacoplar mi código, y ha sido invaluable.

Existen bastantes de estos tipos de frameworks, y la mayoría de ellos también son bastante pequeños. Mi opinión personal es que si vas a escribir más que solo un par de líneas de jQuery para cosas llamativas de la interfaz de usuario, o si quieres una aplicación rica de Ajax, un marco MVC te hará la vida mucho más fácil.

Chetan
fuente
8

"¿Escribir 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 mucho código javascript. Además de eso, había diferentes accesos directos para cada posible función jquery que se te ocurra. Sugerí que organizaran el código como complementos, ya que es el equivalente jquery de clase, módulo, espacio de nombres ... y todo el universo. Pero las cosas se pusieron mucho peor, 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 alienta a ser flojo y no pensar en organizar el código de ninguna manera. Prefiero leer 100 líneas de JavaScript que una línea con 40 funciones jQuery encadenadas (I ' No estoy bromeando). Contrariamente a la creencia popular, es muy fácil organizar el código javascript en equivalentes a espacios de nombres y clases. Eso es lo que hacen YUI y Dojo. Puede rodar fácilmente el suyo si lo desea. Encuentro el enfoque de YUI mucho mejor y eficiente. Pero generalmente necesita un buen editor con soporte para fragmentos para compensar las convenciones de nomenclatura de YUI si desea escribir algo útil.

Vasil
fuente
3
Estoy de acuerdo con usted acerca de comandos encadenados realmente largos, pero una de las mejores partes de jQuery es que mantiene todo Javascript fuera de HTML. Puede configurar controladores de eventos para todos sus elementos sin "necesidad" de agregar ID o eventos <whatever> en sus elementos. Como siempre, el uso excesivo de cualquier herramienta es malo ...
Hugoware
He trabajado en proyectos enormes y bien organizados en jQuery. No sé por qué crees que se interpone en el camino de la organización.
Marnen Laibow-Koser
7

Creo singletons para cada cosa que realmente no necesito instanciar varias veces en la pantalla, una clase para todo lo demás. Y todos ellos se colocan en el mismo espacio de nombres en el mismo archivo. Todo está comentado y diseñado con UML, diagramas de estado. El código javascript no contiene html, por lo que no hay javascript en línea y tiendo a usar jquery para minimizar los problemas entre navegadores.

Nikola Stjelja
fuente
3
Un buen comentario es CLAVE. Me alegra que lo hayas dicho, así que no tuve que hacerlo. Agregaría convenciones de nomenclatura consistentes, algún tipo de estrategia de organización fácilmente comprensible para variables & amp; funciones, y como usted mencionó, el uso juicioso de clases versus singletons.
Matt Lohkamp
No. Si necesita comentarios, su código generalmente no es lo suficientemente legible. Esfuércese por escribir código que no necesite comentarios.
Marnen Laibow-Koser
Además, si necesita UML y diagramas de estado, eso probablemente significa que su arquitectura no es lo suficientemente clara del código. Votación a favor.
Marnen Laibow-Koser
1
@Marnen Los proyectos bien escritos incluyen comentarios para describir POR QUÉ, no necesariamente QUÉ. El código ya describe el QUÉ, pero a menudo necesita algo para describir el POR QUÉ. Votación a favor.
Cypher
@Cypher Los proyectos bien escritos tienen un código lo suficientemente claro que generalmente se puede decir el "por qué", no solo el "qué". No confiaría en un comentario para decirme el "por qué", porque no tengo garantía de que esté sincronizado con el código. Deje que el código se documente.
Marnen Laibow-Koser
6

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 del espacio de nombres y la carga diferida de módulos de acuerdo con la sección del sitio. En cada carga de página, declaro un objeto "vjr", y siempre le cargo un conjunto de funciones comunes (vjr.base.js). Luego, cada página HTML decide qué módulos necesita 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 limitado conocimiento de Javascript, sé que debe haber mejores formas de administrar esto, pero hasta ahora está funcionando muy bien para nosotros.

Danita
fuente
6

Organizar su código de una manera NameSpace centrada en Jquery puede verse de la siguiente manera ... y no chocará con otras API de Javascript como Prototype, Ext tampoco.

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

Espero que esto ayude.

Darryl
fuente
Esto me parece una pequeña cultura de carga. jQuery.fnes un puntero a jQuery.prototype, porque en $()realidad devuelve una nueva instancia de la función constructora jQuery. Agregar un 'complemento' a jQuery significa simplemente extender su prototipo. Pero lo que está haciendo no es eso, y hay formas más limpias de lograr lo mismo.
Adam Lassek
Creo que simplemente está creando funciones estáticas. Recuerdo haber visto en los documentos de jQuery que esta forma de declarar funciones estáticas es aceptable
Alex Heyd
6

Un buen principio de OO + MVC definitivamente ayudaría mucho 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 mis días de programación de escritorio hasta la Web 2.0)

JS OO y MVC

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 cuidadosamente, 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 conducir a un código inmanejable (¿recuerda el día de agregar onclick en todas partes apuntando a funciones globales en la etiqueta del script? Urgh ...)
  2. Modelos de objetos que representan los datos que quiero llenar en los widgets y que pasan de un lado a otro al servidor. Al encapsular los datos a su modelo, la aplicación se convierte en agnóstico de formato de datos. Por ejemplo: mientras que naturalmente en Javascript estos modelos de objetos se serializan y deserializan en su mayoría 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 necesita cambiar todas las clases de widgets .
  3. Clases de controlador que gestionan la lógica empresarial y la comunicación con el servidor + capa de almacenamiento en caché ocasionalmente Esta capa controla el protocolo de comunicación con el servidor y coloca los datos necesarios en los modelos de objetos.
  4. Las clases se envuelven perfectamente en sus espacios de nombres correspondientes. Estoy seguro de que todos sabemos cuán desagradable podría 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 múltiples 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 porque no hay un enfoque estándar para Javascript orientado a objetos. Al mismo tiempo, no quiero escribir mi propio marco y repetir parte del trabajo que estoy seguro de que son personas más inteligentes de las que he resuelto.

jQuery es increíblemente agradable 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 práctica estandarizada de OO. Para mí, después de varios experimentos, encuentro que la infraestructura YUI3 Base and 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 espacio de nombres. Una necesidad real de OO y una organización ordenada de su código
  2. Es compatible con 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 perfectamente
  5. Proporciona constructor y destructor.
  6. Proporciona render y enlace de eventos
  7. Tiene marco base de widgets
  8. Cada widget ahora puede comunicarse entre sí utilizando un modelo estándar basado en eventos
  9. Lo más importante es que ofrece 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 de OO necesaria para mi aplicación web compleja, jQuery todavía proporciona a mi equipo una abstracción JS fácil de usar que todos amamos y conocemos.

Usando YUI3, he logrado crear un patrón MVC separando las clases que extienden la Base como el Modelo, las clases que extienden el Widget como una Vista y, por supuesto, tiene clases de Controlador que están haciendo la lógica necesaria y las llamadas del lado 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 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 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 también se aplica a otras tecnologías. Gracias a Dios por todas las personas que idearon los Principios OO + MVC para hacer que nuestros días de programación sean más manejables.

momo
fuente
5

Utilizo la administración de paquetes de Dojo ( dojo.requirey dojo.provide) y el sistema de clases ( dojo.declareque también permite una herencia múltiple simple) para modularizar todas mis clases / widgets en archivos separados. No solo dosificar esto mantiene su código organizado, sino que también le permite cargar pereza / justo a tiempo las clases / widgets.

Justin Johnson
fuente
3

Hace unos días, los chicos de 37Signals lanzaron un control RTE , con un giro. Crearon una biblioteca que agrupa 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. De esa manera, puedo separar las preocupaciones y, al final, solo tengo un archivo que pasa por la tubería (comprimido, nada 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).

registro de cambios
fuente
1
getsprockets.org es el enlace directo
Matt Gardner
3

Cree clases falsas y asegúrese de que todo lo que se pueda incluir en una función separada que tenga sentido se haga así. También asegúrate de comentar mucho, y no escribir código spagghetti, sino mantenerlo todo en secciones. Por ejemplo, un código sin sentido que representa mis ideales. Obviamente en la vida real también escribo muchas bibliotecas que básicamente abarcan su funcionalidad.

$(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.
    }
};
Dmitri Farkov
fuente
2

Creo que esto se relaciona, quizás, con DDD (diseño impulsado por dominio). La aplicación en la que estoy trabajando, aunque carece de una API formal, da indicios de ello a través del código del lado del servidor (nombres de clase / archivo, etc.). Armado con eso, creé un objeto de nivel superior como contenedor para todo el dominio del problema; luego, agregué espacios de nombres en donde sea necesario:

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');
conocer
fuente
2

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

  1. Carpeta para todos tus 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 biblioteca relacionadas en una carpeta lib que desee usar en toda 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.
Jason Too Cool Webs
fuente
Agradable para la organización de archivos. Lo hago con código. Pero al final compilo mi código en un ... digamos dll. También lo necesita con javascript o terminará solicitando 15 archivos js por página.
graffic
No hay nada de malo en solicitar 15 archivos JS por página. Su navegador los almacenará en caché para solicitudes posteriores de todos modos.
Marnen Laibow-Koser
@ MarnenLaibow-Koser El único problema al solicitar 15 archivos JS en una página es cuántas solicitudes HTTP puede manejar el navegador a la vez. Por lo tanto, agruparlos en un archivo permite al navegador solicitar otros archivos necesarios al mismo tiempo.
Fui robado el
Eso es cierto, pero después de los primeros golpes, estarán en la memoria caché del navegador, por lo que no requerirán conexiones HTTP.
Marnen Laibow-Koser
2

Estoy usando esta pequeña cosa. Le da la directiva 'incluir' para las plantillas JS y HTML. Elimina el desorden por completo.

https://github.com/gaperton/include.js/

$.include({
    html: "my_template.html" // include template from file...
})
.define( function( _ ){ // define module...
    _.exports = function widget( $this, a_data, a_events ){ // exporting function...
        _.html.renderTo( $this, a_data ); // which expands template inside of $this.

        $this.find( "#ok").click( a_events.on_click ); // throw event up to the caller...
        $this.find( "#refresh").click( function(){
            widget( $this, a_data, a_events ); // ...and update ourself. Yep, in that easy way.
        });
    }
});
Gaperton
fuente
2

Puede usar jquery mx (usado en javascriptMVC), que es un conjunto de scripts que le permite usar modelos, vistas y controladores. Lo he usado en un proyecto y me ayudó a crear JavaScript estructurado, con tamaños mínimos de script 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.

Rolnn
fuente
1

Su pregunta es una que me atormentó a fines del año pasado. La diferencia: entregar el código a los nuevos desarrolladores que nunca habían oído hablar de métodos privados y públicos. Tuve que construir algo simple.

El resultado final fue un marco pequeño (alrededor de 1 KB) que traduce literales de objetos en jQuery. La sintaxis es visualmente más fácil de escanear, y si su js crece demasiado, puede escribir consultas reutilizables para encontrar elementos como selectores utilizados, archivos cargados, funciones dependientes, etc.

Publicar un pequeño marco aquí no es práctico, así que escribí una publicación en el blog con ejemplos (Mi primero. ¡Fue una aventura!). Eres bienvenido a echar un vistazo.

Para cualquier otro aquí con unos minutos para verlo, agradecería mucho sus comentarios.

Se recomienda FireFox ya que admite toSource () para el ejemplo de consulta de objeto.

¡Salud!

Adán

Adam
fuente
0

Utilizo un script personalizado inspirado en el comportamiento de Ben Nolan (por desgracia, ya no puedo encontrar un enlace actual para esto) para almacenar la mayoría de mis 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 Javascript sobre la marcha, excepto las que contienen un comportamiento global. Utilizo el asistente de 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.

Aron Rotteveel
fuente
Esto es lo que estabas buscando? koders.com/javascript/…
DOK
Sí, ese es el! :) Sin embargo, parece que el código detrás del enlace es bastante más nuevo que la versión en la que me inspiré. ¡Gracias por tu esfuerzo!
Aron Rotteveel
0

No mencionas cuál es el idioma del lado del servidor. O, más pertinente, qué marco está utilizando, si lo hay, en el lado del servidor.

IME, organizo las cosas en el lado del servidor y dejo que todo se agite en la página web. El marco tiene la tarea de organizar no solo JS que cada página debe cargar, sino también fragmentos JS que funcionan con el marcado generado. Tales fragmentos que generalmente no desea emitir más de una vez, es por eso que se abstraen en el marco para que ese código se ocupe de ese problema. :-)

Para las páginas finales que tienen que emitir su propio JS, generalmente encuentro que hay una estructura lógica en el marcado generado. Tal JS localizado a menudo se puede ensamblar al comienzo y / o al final de dicha estructura.

¡Tenga en cuenta que nada de esto lo absuelve de escribir JavaScript eficiente! :-)

staticsan
fuente
0

Lazy Carga el código que necesitas a pedido. Google hace algo así con su google.loader

Bergantín Lamoreaux
fuente