Muchas respuestas excelentes aquí que brindan enfoques muy diferentes a este problema. Diría que como ingenieros todavía no estamos seguros de cuál es el mejor enfoque para el desarrollo de la interfaz de usuario en el navegador (representación del lado del servidor frente a MVC del lado del cliente, widgets desacoplados con módulos de evento / estado, etc.). No estoy seguro de tener la mejor respuesta a la pregunta, pero básicamente paso el 80% de mi tiempo en el trabajo / en casa tratando de mejorar la interfaz de usuario en yelp. También adoptamos el enfoque de representación del lado del servidor y solo usamos JavaScript para aplicar el comportamiento. Estas son las cosas en las que pienso al desarrollar una interfaz de usuario basada en navegador.
Voy a construir pequeños módulos encapsulados que manejan pequeñas unidades de trabajo llamadas componentes. Al formar un árbol de estos componentes (a través de subcomponentes encapsulados) puede permitir que los componentes se comuniquen entre sí. Para ayudar a desacoplar estos componentes, cada componente tiene un elemento sobre el que actúa y solo asume la estructura dentro de su contenedor (nunca haga referencia a elementos fuera de su contenedor). Cada componente tiene un conjunto de eventos que puede publicar, que es la única forma de notificación que proporcionan. Los componentes principales pueden suscribirse a eventos de subcomponentes y notificar a los hermanos que deben cambiar algo. Por ejemplo, si tenía un componente de conjunto de radio y un componente de área de texto que se actualiza en función de la selección de radio, en lugar de que el área de texto se una al conjunto de radio, cree un componente principal (cuyo contenedor contenga el conjunto de radio + área de texto) que representa el evento. En el componente principal, verá un código que dice “cuando la radio active el evento de cambio, notifique al área de texto de lo que debería cambiar su valor”. Con este patrón, los componentes hermanos no saben realmente acerca de sus hermanos y pueden reutilizarse en otras estructuras de árbol. Mi ejemplo de radio / área de texto es increíblemente demasiado simple y la sobrecarga de esta abstracción puede no parecer útil, pero en casos más complejos con árboles muy profundos, esto puede ser muy útil.
Este patrón es muy similar a HMVC ( http://net.tutsplus.com/tutorial … ). Tiene un árbol de controladores que tienen presentación (contenedor) y modelos (pueden realizar llamadas ajax para actualizar datos). Si está asumiendo la representación del lado del servidor, haga todo lo posible para seguir representando en el servidor; sus llamadas ajax deberían devolver json que contenga HTML. Desafortunadamente, esto significa que sus “modelos” están devolviendo datos de vista, pero teniendo en cuenta que el componente (módulo MVC) está directamente asociado con una presentación (supone el contenido dentro de un contenedor), creo que esto está bien. Sugiero representar el lado del servidor específicamente porque garantiza que sus plantillas no estén duplicadas y que i18n se realice en el servidor (evite enviar cadenas / lógica de traducción al navegador). Usar algo como el bigote ( http://mustache.github.com/ ) puede eliminar mi argumento para duplicar plantillas en todos los idiomas y eso podría valer la pena explorarlo.
Cuando se trata de aplicar componentes a su DOM estático, simplemente está adjuntando un conjunto de componentes de nivel superior a un conjunto de contenedores. Estos componentes de nivel superior crean subcomponentes, que luego pueden hacer subcomponentes, etc. Si está trabajando con una aplicación en la que todos los elementos de la página necesitan comunicarse entre sí, es posible que se encuentre en un escenario en el que tiene un solo componente de nivel superior, pero Creo que en la mayoría de los casos esto es raro.
A diferencia de muchos enfoques para el desarrollo de DOM UI, el acoplamiento es muy explícito. Está claro cómo ocurren los eventos en el DOM y no hay magia global de que si agrego este nombre de clase, de repente es un menú desplegable. Encuentro que los ingenieros a menudo no consideran que el DOM sea una variable global. Están de acuerdo con aplicar el comportamiento global a esa variable global, pero creo que todos podemos estar de acuerdo en que es global y, en general, no nos gustan los globales. En la mayoría de los sistemas de software evitamos el uso de globales, en el navegador tenemos pocas opciones, una variable global es nuestra capa de presentación, haga todo lo posible para no abusar de ella.
Con mi patrón descrito de un árbol de componentes, le sugiero que haga todo lo posible para no violar la Ley de Demeter ( http://en.wikipedia.org/wiki/Law … ). Un componente en la profundidad 2 no debe interactuar con un componente en la profundidad 20 porque su árbol puede volverse difícil de refactorizar. Si un componente en el nivel 2 necesita saber sobre un evento en el nivel 20, cada componente en la rama debe publicar un evento apropiado (que probablemente no sea semánticamente el mismo evento en todos los niveles). El burbujeo de eventos es algo que trato de evitar usar en árboles muy grandes porque viola la ley.
La prueba del patrón descrito permite pruebas unitarias fáciles, cada componente se puede probar de forma independiente y las pruebas de integración se pueden realizar probando los componentes principales.
En términos de cómo hacer componentes, es realmente difícil de decir. En el pasado, he optado por una estructura basada en clases (una clase base proporciona el sistema de eventos básico), pero existen límites para la herencia única cuando se trata de construir comportamientos de IU: terminas con 100 opciones para cada componente o subclases que En realidad duplica el código. Dan Webb y Angus Croll de Twitter tienen algunos buenos comentarios sobre el uso de mixins funcionales para conectar comportamientos cuando los necesita ( https://speakerdeck.com/u/angusc … ). Por ejemplo, puede tener un área de texto y un “complemento”, un límite en el número de caracteres, el comportamiento del texto del marcador de posición “complemento”, etc. Creo que ese modelo es muy atractivo y es algo que probablemente usaré en el futuro.
Si no sigue mi patrón y baja al concepto de modelo / estado más desacoplado, le recomiendo algo que hicimos en yelp llamado hub. Es un objeto que tiene dos funciones básicas: suscribirse / publicar. Parece ser increíblemente similar a un módulo pubsub básico, pero tiene una propiedad importante de que el último valor publicado se almacena realmente en el concentrador. Si un nuevo suscriptor se suscribe al centro y hay un valor anterior, el centro publica ese valor de inmediato. Esto es similar a $ .ready: si llega “demasiado tarde” (después de cargar el dom), aún se le llama. Vea el centro aquí: https://github.com/Yelp/thehub . Utilizamos mucho el centro en Yelp, pero hemos encontrado que los centros a veces se vuelven desordenados, especialmente si son objetos globales en los que las personas tienen valores fijos. También es muy importante que reconozca que no es un sistema de eventos, específicamente almacena el estado.