patron inyeccion injection dependencias javascript dependency-injection module ecmascript-6

javascript - inyeccion - Dado ES2015, la inyección de dependencia y la abstracción de la biblioteca, ¿cómo debería ser mi módulo ideal en 2016?



patron de inyeccion (3)

Para mí, este parece ser uno de los mayores problemas no resueltos de la comunidad JS. No existen mejores prácticas en torno a la administración de dependencias y la inyección de dependencia (al menos que yo sepa).

Hay una larga discusión sobre este hilo: ¿Necesito inyección de dependencia en NodeJS, o cómo tratar con ...? , pero la mayoría de las soluciones parecen funcionar solo para casos específicos, y requieren cambiar la forma en que se escriben los módulos de alguna manera. Y lo más sorprendente es que muchas respuestas argumentan que ni siquiera necesitas DI.

Mi propia solución para el problema es este marco DI mínimo , que te permite definir módulos una vez, y los conectará por ti con las dependencias adecuadas.

Si no, por un lado, estaría a bordo para escribir todos mis módulos como

import A from ''./a.js''; var B = function(){ //use A }; export default B;

y luego usar un compilador para compilarlo en algún navegador o formato de servidor.

Sin embargo, mi único problema con lo anterior es la especificación explícita de ./a.js en la import .

Entiendo por qué la especificación fue así 1 , para estar a favor del análisis estático . Pero hay dos razones muy prácticas por las cuales hornear el nombre del archivo y su ruta son problemas.

  1. Como ya se mencionó aquí , cuando recicla módulos con frecuencia de un proyecto a otro, es muy probable que no pueda mantener un camino consistente hacia ese recurso en su árbol de proyecto. Cocinar una llamada de importación como import myModule from ''./../../vendor/lib/dist/mod.js'' en el código de un módulo no me parece exactamente a prueba de futuro.
  2. Además del camino en sí mismo, especificar el nombre del archivo también te amarra. Algo como esto parece bastante inocente:

    import $ from ''vendor/jquery.js''

    Pero, ¿qué tal el día en que quiero usar Zepto en lugar de jQuery ? He descubierto que la abstracción, especialmente en torno a las bibliotecas de proveedores, es inmensamente útil cuando se trata de grandes bases de código, desarrolladores obstinados y un ecosistema de JavaScript siempre cambiante. Es posible que desee importar React como mi biblioteca componente hoy, pero ¿qué pasa mañana? Además, ¿qué sucede si voy a utilizar el mismo módulo tanto en el cliente como en el servidor, pero necesito diferentes versiones de una biblioteca dependiente?

Insisto en la abstracción robusta (pero clara y consistente) en mis equipos. Muchas veces, la abstracción ha tomado la forma de algún tipo de espacio de nombres. Fantaseo un poco sobre esto:

//BAD: Bakes React into my component modules import ComponentLib from ''./React.js''; //GOOD: Leaves me free to use any React-like library import ComponentLib from ''vendor.lib.component'';

Donde vendor.lib.component , de forma similar a Java, se ha registrado en algún lugar previamente.

Tenga en cuenta que, a diferencia de esta pregunta , mi objetivo no es tener un control dinámico sobre mis importaciones. No quiero flexibilidad en tiempo de ejecución, me gustaría tener flexibilidad de tiempo de compilación . Debería poder subcontratar un marco dependiente para otro, o para un simulacro, o para algo que funcione en un entorno particular, sin tener que preocuparme por las dependencias a las que llaman mis módulos, o intentar duplicar algún directorio loco. árbol para cada producto de construcción que estoy buscando.

Preguntas similares han llevado a la sugerencia de una biblioteca que aprovecha la especificación del sistema , como SystemJS . Luego puede usar algo como jspm para introducir un mapa de módulo para obtener abstracción. Pero en el momento en que hago eso, escribo todos mis módulos de manera diferente:

System.import(''a'', function(A){ //use ''A'' });

¿De repente ese es el futuro? Si es así, ¿por qué no sigo usando AMD? ¿Por qué molestarse con los módulos ES2015 y los transpilers en ejecución si voy a volver a utilizar una API de cargador de aspecto asíncrono?

Más sorprendente, no veo mucho ni ninguna mención de abordar un estándar API de cargador de módulos en la especificación ES2017 .

( EDIT: Pregunta revisada para cumplir con los estándares de una respuesta no basada en opiniones )

Dado todo lo anterior, le pregunto a la comunidad: ¿cómo escribo un módulo de JavaScript que (i) cumple con el estándar ES2015, (ii) no hace referencia a un módulo dependiente por su nombre de archivo o ruta, y (iii) no se basa en herramientas / configuraciones intermedias extensas que harían prohibitivo compartir el módulo con múltiples equipos.

-

Nota 1 Como @zeroflagL anotó en los comentarios, la especificación no establece explícitamente que un módulo debe especificarse como una ruta, solo una cadena (ver ModuleSpecifier - http://www.ecma-international.org/ecma-262/ 6.0 / # table-41 ). Sin embargo, también hay una instrucción clara para tener en cuenta las referencias circulares, lo que implica algún tipo de análisis estático ( http://www.ecma-international.org/ecma-262/6.0/#sec-imports ), con rutas de archivos aparentemente el contexto de referencia de elección hasta este punto. Entonces no podemos culpar a las especificaciones por ser rígidas aquí, sino todo lo contrario. La responsabilidad recae sobre el resto de nosotros para desarrollar implementaciones más robustas de import / ModuleSpecifier que conducen a un estándar secundario.


Sé que está solicitando una solución que pueda usar específicamente en el caso de un compilador JS, pero como está solicitando una mejor práctica, creo que debemos tener en cuenta el campo de juego completo en el que se lleva a cabo la administración de la dependencia de JavaScript. , incluyendo diferentes entornos de host posibles, como el navegador y Node.js, sistemas de empaque frontales como Webpack, Browserify y jspm, y en cierta medida, tecnologías vecinas como HTML y HTTP / HTTP2.

Historia de los módulos ES

Existe una razón por la cual no encontrará una API de cargador en ninguna especificación ECMA: la resolución de dependencia no finalizó cuando llegó la fecha límite para la especificación ECMA2015, y se decidió que la especificación ECMA solo describiría la sintaxis del módulo y que el host El entorno (por ejemplo, browser, node.js, JS compiler / transpiler) sería responsable de resolver los módulos a través de sus especificadores, a través de un HostResolveImportedModule llamado HostResolveImportedModule , que podrá encontrar en las especificaciones ECMA2015 y ECMA2017 .

La especificación de la semántica detrás del patrón del módulo ES2015 ahora está en manos del WHATWG. Dos desarrollos notables han sido el fruto de sus esfuerzos:

  1. Han determinado que los módulos en HTML pueden ser denotados por <script type="module"> .
  2. Existe un borrador para una especificación de carga , que tendrá en cuenta los diferentes entornos de host y los sistemas de empaquetado frontal. Esto suena como la especificación que finalmente ayudará a responder su pregunta, pero lamentablemente está lejos de haber terminado, y no se ha actualizado durante bastante tiempo.

Implementaciones actuales

Navegador

Con la adición de la etiqueta <script type="module"> , todo parece funcionar para una implementación simplificada de los módulos ES6 en el navegador. La forma en que funciona ahora es sin tener en cuenta el rendimiento (ver esto y este comentario). Además, no es compatible con ningún sistema de empaquetado frontal. Está claro que el concepto de módulos debe expandirse para que pueda ser utilizado en todos los sitios web de producción, y por lo tanto los proveedores de navegadores no han tenido prisa en implementarlo en sus navegadores.

Node.js

Node.js es una historia completamente diferente, ya que ha implementado los módulos de estilo CommonJS desde el principio. Actualmente, no hay soporte para módulos ES2015. Se han realizado dos sugerencias para incorporar módulos ES6 en Node.js junto con los módulos CommonJS. Esta publicación de blog analiza estas sugerencias con gran detalle.

compiladores y sistemas de embalaje

Conclusión

Entonces, cuando pregunta por la mejor manera de escribir módulos, puede ver que esto es muy difícil. Proponer una solución que sea compatible con todos los entornos host posibles es preferible, ya que:

  • Es posible que desee compartir su código entre diferentes entornos.
  • Existe el riesgo de que una cierta clase de desarrolladores (por ejemplo, los desarrolladores de aplicaciones para el usuario) abracen los módulos ES2015, mientras que otros (por ejemplo, los desarrolladores de Node.js) se adhieren a otra solución (por ejemplo, los módulos CommonJS). Esto depreciaría aún más la posibilidad de código de entorno cruzado.

Pero verás de lo anterior que existen diferentes entornos que tienen diferentes demandas. En este sentido, la mejor respuesta a su pregunta podría ser: actualmente no existe la mejor manera de escribir módulos que cubran sus inquietudes sobre la abstracción.

Solución

Sin embargo, si ahora tuviera la tarea de escribir módulos ES2015 que compilan JS, me mantendría alejado de rutas relativas y siempre usaría rutas absolutas desde la raíz del proyecto como un identificador para los módulos, lo que no veo como problemático. . Java en realidad refleja espacios de nombres y su estructura de directorios de la misma manera. Usaría Webpack o Babel para compilar mi código fuente que se puede ejecutar en los entornos actuales de JS.

En cuanto a su otro problema, si quisiera poder sustituir bibliotecas de proveedores, probablemente crearía un módulo que alía las bibliotecas de proveedores con nombres que usaré internamente. Algo así como:

// /lib/libs.js import jQuery from ''vendor/jquery.js'' export const $ = jQuery;

Todos los demás módulos importarían $ de lib / libs.js y usted podría cambiar las bibliotecas de proveedores cambiando la referencia en un solo lugar.


Si desea seguir las mejores prácticas, siga la guía de estilo de AirBnb JavaScript. En mi opinión, la mejor y más completa guía de estilo de JavaScript que existe

https://github.com/airbnb/javascript#classes--constructors

Importar

Esto se ve mal para reutilizar módulos: import myModule from ''./../../vendor/lib/dist/mod.js''

Publique su módulo en NPM (que también puede ser un NPM privado o autohospedado) e importe así import myModule from ''my-module'';

Finalmente configure su NODE_PATH como una carpeta raíz y consulte los módulos relativamente desde la raíz.

En package.json

''start'': ''NODE_PATH=. node index.js'' // in Windows ''start'': ''NODE_PATH=. && node index.js''

Ahora importa así:

import myModule from ''vendor/lib/dist/mod.js''

Variables

var no es parte de ES6. Utilizar:

  • constant - cuando el valor de la variable no cambiará, también los objetos y las importaciones. Incluso si los parámetros del objeto cambian, sigue siendo un const.

  • let - cuando el valor de la variable cambia, es decir, for(let = i; i < 100; i++)

  • Desde mi propia experiencia, siempre establezca const como valor por defecto y solo cambie para let si el ESLint queja (por cierto, use ESLint http://eslint.org/ )

Clases

Ahora hay una forma adecuada de definir clases en JavaScript

class B { constructor() { } doSomething() { } }

Su ejemplo actualizado:

import A from ''./a''; Class B { constructor() { } doSomething() { } }; export default B;

Si quieres extender A:

import A from ''./a''; Class B extends A{ constructor(argumentA, argumentB) { super(argumentA, argumentB); this.paramA = argumentA; } }; export default B;

Consejos

  • Use Webpack con NPM como herramienta de compilación. No use Gulp o Grunt
  • Usa Babel para transpilar tu código (el cargador JSX puede no ser suficiente)
  • Aprenda a no utilizar jQuery en absoluto, sino que elija los polyfills y herramientas correctos para los trabajos que necesita hacer desde NPM
  • Hay toneladas de repositorios repetitivos bien escritos en github para robar lo mejor. Estos son algunos de los que estoy usando.

Mi respuesta en esencia es:

El patrón / biblioteca que solicitó es la guía de estilo de AirBnb JavaScript y se olvida de jQuery