Módulo vs espacio de nombres - Importar vs Requerir mecanografiado


102

Tengo mucha confusión module/namespace/exporty import, require, referenceuso. Siendo de origen Java, ¿alguien puede explicarme en pocas palabras cuándo usar qué y cuál es el diseño correcto? Siento que me equivoco cuando escribo un proyecto de muestra

Hasta ahora, esto es lo que tengo entendido 1. modulees para paquetes externos 2. namespacees para paquetes internos

  • ¿No entendí cómo los categorizamos?
  • ¿Cuándo exportar una clase, un espacio de nombres o un paquete?
  • Si exportamos el paquete / espacio de nombres, todas las clases dentro de ese se exportan o deben exportarse explícitamente
  • ¿Cómo se puede importar / requerir cada uno de ellos?

Según el documento , si estoy creando cada archivo "ts" para cada administrador / modelo, ¿Typecript no recomienda el uso de "espacios de nombres"? ¿Usar directamente rutas de referencia?

Explique en detalle ya que vengo de diferentes antecedentes y no estoy seguro acerca de ES6 / ES5, etc.

He visto a varias personas plantearse / confundirse con las mismas preguntas. Espero que alguien pueda explicarlo en detalle con un escenario del mundo real.


esto tiene mucha atención que responde a su pregunta, no estoy seguro de por qué se recompensó
— Dan Pantry

@DanPantry, antes de comenzar la recompensa, no había una respuesta única :(
— Reddy

mi error: no comparé las marcas de tiempo.
— Dan Pantry

Respuestas:


80

¿No entendí cómo los categorizamos?

Los espacios de nombres se utilizan para organizar / encapsular su código. Los módulos externos se utilizan para organizar / encapsular su código Y para localizar su código en tiempo de ejecución. En la práctica, tiene dos opciones en tiempo de ejecución: 1) combinar todo el código transpilado en un archivo, o 2) usar módulos externos y tener varios archivos y requerir algún otro mecanismo para acceder a esos archivos.

¿Cuándo exportar una clase, un espacio de nombres o un paquete?

Para que un tipo o valor sea visible fuera del archivo en el que se encuentra, debe exportarlo si está dentro de un espacio de nombres. Ya sea que lo exporte al nivel superior o dentro de un espacio de nombres, decidirá si ahora está en un módulo externo.

Si exportamos el paquete / espacio de nombres, todas las clases dentro de ese se exportan o deben exportarse explícitamente

Las clases en un espacio de nombres siempre deberán exportarse explícitamente para que la clase sea visible en tiempo de compilación fuera del archivo en el que está definida.

¿Cómo se puede importar / requerir cada uno de ellos?

Esto depende de si está utilizando módulos externos. Siempre será necesario importar un módulo externo para "usarlo". Importar un espacio de nombres que no está en un módulo externo en realidad es solo proporcionar un alias para el espacio de nombres; aún debe prefijar el tipo / lo que sea con el alias (y es por eso que generalmente no desea usar espacios de nombres con módulos externos; hacerlo significa que siempre tiene que usar un prefijo al hacer referencia a algo proporcionado por el módulo externo). Los espacios de nombres que no están en un módulo externo pueden abarcar archivos, por lo que si está en el mismo espacio de nombres, puede hacer referencia a cualquier cosa exportada por el espacio de nombres sin necesidad de ningún tipo de importación.

Para comprender realmente lo anterior, necesita algunos conocimientos previos. La clave para entender con referencias / espacios de nombres / módulos externos es lo que hacen estas construcciones en tiempo de compilación y lo que hacen en tiempo de ejecución.

Las directivas de referencia se utilizan en tiempo de compilación para localizar información de tipo. Su fuente tiene un símbolo particular. ¿Cómo localiza el compilador de TypeScript la definición de ese símbolo? La directiva de referencia ha sido subsumida en gran medida por el mecanismo tsconfig.json: usando tsconfig.json, le indica al compilador dónde están todas sus fuentes.

Los espacios de nombres pueden contener definiciones de tipo y / o implementación. Si un espacio de nombres contiene solo información de tipo, entonces no tiene ninguna manifestación de tiempo de ejecución; puede verificar esto mirando la salida JS y encontrando un archivo JS vacío. Si un espacio de nombres tiene código de implementación, entonces el código se envuelve dentro de un cierre que se asigna a una variable global con el mismo nombre que el espacio de nombres. Con los espacios de nombres anidados, habrá una variable global para el espacio de nombres raíz. Nuevamente, verifique la salida JS. Los espacios de nombres son históricamente la forma en que las bibliotecas del lado del cliente de JS han intentado evitar el problema de las colisiones de nombres. La idea es envolver toda su biblioteca en un solo cierre y luego exponer una huella global lo más pequeña posible, solo una variable global que hace referencia al cierre. Bueno, el problema sigue siendo que ha reclamado un nombre en el espacio global. ¿Y si quisiera, digamos, dos versiones de una biblioteca? Un espacio de nombres de TypeScript todavía tiene el problema de cómo ubicar la fuente para el espacio de nombres. Es decir, el código fuente que hace referencia a AB todavía tiene el problema de decirle al compilador cómo ubicar AB, ya sea usando directivas de referencia o usando tsconfig.json. O colocando el espacio de nombres en un módulo externo y luego importando el módulo externo.

Los módulos externos se originaron con JS del lado del servidor. Existe una correspondencia uno a uno entre un módulo externo y un archivo en el sistema de archivos. Puede utilizar la estructura de directorios del sistema de archivos para organizar módulos externos en una estructura anidada. La importación de un módulo externo generalmente siempre introducirá una dependencia de tiempo de ejecución en ese módulo externo (la excepción es cuando importa un módulo externo pero luego no usa ninguna de sus exportaciones en la posición de valor, es decir, solo importa el módulo externo para obtener la información de su tipo). Un módulo externo está implícito en un cierre, y esta es la clave: el usuario del módulo puede asignar el cierre a cualquier variable local que desee. TypeScript / ES6 agrega sintaxis adicional sobre la asignación de las exportaciones de los módulos externos a los nombres locales, pero esto es solo una sutileza. En el lado del servidor, localizar un módulo externo es relativamente sencillo: simplemente ubique el archivo que representa el módulo externo en el sistema de archivos local. Si desea utilizar módulos externos en el lado del cliente, en un navegador, se vuelve más complejo ya que no hay equivalente al sistema de archivos que tiene el módulo disponible para cargar. Así que ahora, en el lado del cliente, necesita una forma de agrupar todos esos archivos en un formulario que se pueda usar de forma remota en el navegador; aquí es donde los paquetes de módulos como Webpack (aunque Webpack hace mucho más que paquetes de módulos) y Entra en juego Browserify. Los paquetes de módulos permiten la resolución en tiempo de ejecución de sus módulos externos en el navegador. en un navegador, se vuelve más complejo ya que no hay equivalente al sistema de archivos que tiene el módulo disponible para cargar. Así que ahora, en el lado del cliente, necesita una forma de agrupar todos esos archivos en un formulario que se pueda usar de forma remota en el navegador; aquí es donde los paquetes de módulos como Webpack (aunque Webpack hace mucho más que paquetes de módulos) y Entra en juego Browserify. Los paquetes de módulos permiten la resolución en tiempo de ejecución de sus módulos externos en el navegador. en un navegador, se vuelve más complejo ya que no hay equivalente al sistema de archivos que tiene el módulo disponible para cargar. Así que ahora, en el lado del cliente, necesita una forma de agrupar todos esos archivos en un formulario que se pueda usar de forma remota en el navegador; aquí es donde los paquetes de módulos como Webpack (aunque Webpack hace mucho más que paquetes de módulos) y Entra en juego Browserify. Los paquetes de módulos permiten la resolución en tiempo de ejecución de sus módulos externos en el navegador.

Escenario del mundo real: AngularJS. Suponga que los módulos externos no existen, use un solo espacio de nombres para limitar la contaminación del espacio global (en el ejemplo a continuación, una sola variable MyApp es todo lo que hay en el espacio global), exporte solo interfaces y use la inyección de dependencia AngularJS para realizar implementaciones disponible para su uso. Coloque todas las clases en una raíz de directorio, agregue un tsconfig.json a la raíz, instale tipificaciones de angularjs en la misma raíz de directorio para que tsconfig.json también lo recoja, combine toda la salida en un archivo JS. Esto funcionará bien para la mayoría de los proyectos si la reutilización del código no es una gran preocupación.

MyService.ts:

namespace MyApp {

    // without an export the interface is not visible outside of MyService.ts
    export interface MyService { 
        ....
    }

    // class is not exported; AngularJS DI will wire up the implementation
    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
}

MyController.ts:

namespace MyApp {

   class MyController {
       // No import of MyService is needed as we are spanning 
       // one namespace with multiple files.
       // MyService is only used at compile time for type checking. 
       // AngularJS DI is done on the name of the variable. 
       constructor(private myService: MyService) { 
       }
   }
   angular.module("MyApp").controller("myController", MyController);
}

Usar IIFE para evitar contaminar el alcance del tiempo de ejecución global. En este ejemplo, no se crea ninguna variable global. (Se asume un tsconfig.json).

Foo.ts:

namespace Foo {
    // without an export IFoo is not visible. No JS is generated here
    // as we are only defining a type.
    export interface IFoo {
        x: string;
    }
}

interface ITopLevel {
    z: string;
}

(function(){
    // export required above to make IFoo visible as we are not in the Foo namespace
    class Foo1 implements Foo.IFoo {
        x: string = "abc";
    }
    // do something with Foo1 like register it with a DI system
})();

Bar.ts:

// alias import; no external module created
import IFoo = Foo.IFoo;

(function() {

    // Namespace Foo is always visible as it was defined at
    // top level (outside of any other namespace).
    class Bar1 implements Foo.IFoo {
        x: string;
    }

    // equivalent to above
    class Bar2 implements IFoo {
        x: string;
    }

    // IToplevel is visible here for the same reason namespace Foo is visible
    class MyToplevel implements ITopLevel {
        z: string;
    }

})();

Con IIFE puede eliminar la introducción de MyApp como una variable global en el primer ejemplo.

MyService.ts:

interface MyService { 
    ....
}

(function() {

    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
})();

MyController.ts:

(function() { 

   class MyController { 
       constructor(private myService: MyService) { 
       }
   }

   angular.module("MyApp").controller("myController", MyController);
})();

30

Hay dos cosas:

  • Un módulo en TypeScript es una noción estándar de ES6, usa palabras clave import/ en el nivel superior del código;export
  • Un espacio de nombres es una noción específica de TypeScript para ayudar a organizar el código de una manera obsoleta.

Espacios de nombres

Es una noción casi obsoleta. Antes de los módulos ES6, la forma común de separar el código JavaScript en un navegador era crear variables globales. Por ejemplo, todas las funciones de una API como el guión bajo se ubicaron en una variable global llamada _.

Esta antigua forma de proceder es como los paquetes Java o los espacios de nombres PHP. No está adaptado a la Web. El nuevo estándar ECMAScript resuelve problemas como: ¿cómo usar dos bibliotecas que tienen el mismo nombre? ¿Cómo usar dos versiones distintas de la misma biblioteca?

Aviso: En las versiones de TypeScript anteriores a la definición de ECMAScript de "módulos" (verano de 2014), los espacios de nombres se denominaban "módulos internos" y los módulos se denominaban "módulos externos".

Aviso 2: La palabra clave exportdentro de a namespacees un uso no estándar de TypeScript de la palabra clave. Es el medio para declarar algo que es de acceso público desde fuera del espacio de nombres.

Módulos ES6

Un módulo es un archivo que contiene palabras clave importo exporten el nivel superior del código.

TypeScript sigue el estándar de ECMAScript. Sugiero leer una buena introducción a los módulos de ES6 en un artículo de Mozilla.

Si desea utilizar módulos en una aplicación front-end (en un navegador), tendrá que utilizar un paquete ( Webpack [ la documentación aquí ], Browserify) o un cargador ( SystemJS [ un tutorial aquí ], RequireJS) y para configurar TypeScript con este entorno.

Si su código se ejecuta en Node.js, simplemente configure el compilador TypeScript para generar el formato CommonJS.

Aviso: un espacio de nombres se puede declarar en un módulo. En ese caso, no será accesible como una variable global desde fuera del módulo. Sin embargo, se puede exportar desde el módulo.


¿El compilador de mecanografiado no agrupa automáticamente los módulos en un archivo .js?
— Kokodoko

@Kokodoko, No. Puede simplemente concatenar.
— Paleo

Erm ... ¿cuál es la diferencia? .... Estoy usando módulos y solo tsc para construir un solo archivo .js. No tengo que usar systemJS, webpack o browserify.
— Kokodoko

1
@Kokodoko ¡Tienes razón! ¡Culpa mía! Parece que el compilador de TypeScript puede crear un paquete desde TS 1.8 , me había perdido la función.
— Paleo

Dicho esto ... Mis módulos se compilan pero no se ejecutan después de SystemJS.import ('/ js / main.js'); ¿Alguna idea de cómo poner en marcha la aplicación?
— Kokodoko

13
  1. el módulo es para paquetes externos 2. el espacio de nombres es para paquetes internos

En realidad, la modulepalabra clave ha sido reemplazada por la namespacepalabra clave.

Una mejor declaración es que los módulos son lo que solían llamarse módulos externos, el espacio de nombres es lo que solían llamarse módulos internos.

Más

Espero que esto ayude más: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html


8
Lamento informar, las preguntas están sin respuesta. Esperaba explicar en detalle con un ejemplo del mundo real en lugar de una definición de libro de texto.
— Reddy

¿Quiere decir que ahora no hay un teclado de módulo en TypeScript?
— Mohammad Kermani

7

" require " e " import " son equivalentes en funcionalidad. podemos usarlos indistintamente porque tenemos transpilers a los que realmente no les importa si el navegador los admite de forma nativa o no. pero, mientras que " require " tiene sus raíces en el antiguo estilo de codificación proveniente de CommonJS desde 2009, "import" deriva su sintaxis de la sintaxis ES6 (ES2015) ampliamente aceptada. por lo que debe usar " importar " para nuevos proyectos y no " requerir ".


1
Exactamente. Tan confuso, y con tan poca información al respecto, que dice que es básicamente lo mismo. Me pregunto por qué esto fue rechazado.
— Shai Petel

0

Confusión de nombres

En los primeros días de TypeScript, los espacios de nombres se llamaban módulos internos y los módulos ES6 se llamaban módulos externos .

Ahora, para declarar los espacios de nombres, el equipo de TypeScript recomienda usar el en namespace { }lugar de la module { }sintaxis para evitar la confusión de nombres con los módulos externos. Porque los módulos externos ahora son simplemente 'módulos' y los módulos internos son 'espacios de nombres'.


Espacios de nombres

Declaración

Un espacio de nombres en TypeScript se puede declarar usando namespacela modulepalabra clave o . Ambas palabras clave hacen lo mismo. Luego, podemos decidir qué parte de nuestro espacio de nombres hacer pública usando la exportpalabra clave.

// LivingThings.ts
export namespace Animals {
    export class Dog { }
    export class Cat { }
}
export namespace Plants {
    export class Orchid { }
    export class Bamboo { }
}

// LivingThingsUser.ts
import { Animals, Plants } from "./LivingThings"

Agrupación lógica

Antes de ES6, los espacios de nombres se usaban en Typescript para encapsular las interfaces, clases, funciones y variables para soportar un grupo de funcionalidades relacionadas y ocultar detalles de implementación. De esta manera podríamos evitar que las variables se filtren al espacio global. Esto ayudó a una mejor organización del código y a evitar colisiones de nombres. Ahora se recomienda utilizar módulos ES6 para lograr esto.

Los espacios de nombres ahora se utilizan para declaraciones de espacios de nombres ambientales.

Uso de un solo archivo

Podemos declarar espacios de nombres en varios archivos y se pueden concatenar usando --outFileflag. Luego podemos usar ese archivo concatenado dentro de la <script>etiqueta en nuestra página HTML. Esto nos permite estructurar nuestro código de una buena manera en una aplicación web del lado del cliente con todas las dependencias incluidas.


Módulos

Declaración

Los módulos también se denominan módulos ES6. Usamos varios archivos para agrupar las funcionalidades relacionadas y solo usamos la exportpalabra clave para hacer que el objeto deseado sea visible públicamente.

// Animals.ts
export class Dog { }
export class Cat { }

// Plants.ts
export class Orchid { }
export class Bamboo { }

// LivingThingsUser.ts
import { Dog, Cat } from "./Animals"
import { Orchid, Bamboo } from "./Plants"

Agrupación lógica

La agrupación lógica en módulos se logra utilizando archivos separados para agrupar las funcionalidades relacionadas. Por esta razón, los módulos externos también se denominan módulos de archivo .

Uso de un solo archivo

No cargamos los módulos de la aplicación web del lado del cliente usando la <script>etiqueta, porque los navegadores pueden volverse lentos al descargar tantos archivos y renderizar la página al mismo tiempo. Para ello, utilizamos los cargadores de módulos como CommonJS, AMD, SystemJS que nos permiten cargar archivos de forma asincrónica o concatenar los archivos de módulos externos en un único archivo optimizado.

Para el lado del servidor, especialmente en Node.js, se recomiendan encarecidamente los módulos.

¡Eso es!

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.