Modul vs Namespace - Import vs Require Typescript


101

Ich bekomme viel Verwirrung mit module/namespace/exportund import, require, referenceGebrauch. Kann mir jemand mit Java-Hintergrund auf den Punkt bringen, wann und was das richtige Design ist? Ich habe das Gefühl, dass ich es vermassle, wenn ich ein Beispielprojekt schreibe

Bisher ist dies mein Verständnis 1. moduleist für externe Pakete 2. namespaceist für interne Pakete

  • Ich habe nicht verstanden, wie wir sie kategorisieren?
  • Wann soll eine Klasse, ein Namespace oder ein Paket exportiert werden?
  • Wenn wir Paket / Namespace exportieren, werden alle darin enthaltenen Klassen exportiert oder müssen explizit exportiert werden
  • Wie kann jeder von ihnen importiert / benötigt werden?

Laut doc empfiehlt Typescript nicht, "Namespaces" zu verwenden , wenn ich jede "ts" -Datei für jeden Manager / jedes Modell erstelle. Referenzpfade direkt verwenden?

Bitte erläutern Sie dies ausführlich, da ich aus einem anderen Umfeld komme und mir bei ES6 / ES5 usw. nicht sicher bin. "

Ich habe mehrere Leute gesehen, die mit denselben Fragen aufgeworfen / verwirrt wurden. Ich hoffe, jemand kann dies anhand eines realen Szenarios detailliert erklären


Dies hat viel Aufmerksamkeit, die Ihre Frage beantwortet, nicht sicher, warum es Kopfgeld bekommen wurde
— Dan Pantry

@ DanPantry, bevor Bounty gestartet wurde, gab es keine einzige Antwort :(
— Reddy

Mein Fehler - ich habe keine Zeitstempel verglichen.
— Dan Pantry

Antworten:


80

Ich habe nicht verstanden, wie wir sie kategorisieren?

Namespaces werden verwendet, um Ihren Code zu organisieren / zu kapseln. Externe Module werden verwendet, um Ihren Code zu organisieren / zu kapseln UND Ihren Code zur Laufzeit zu lokalisieren. In der Praxis haben Sie zur Laufzeit zwei Möglichkeiten: 1) Kombinieren Sie den gesamten transpilierten Code in einer Datei oder 2) Verwenden Sie externe Module und verfügen Sie über mehrere Dateien. Sie benötigen einen anderen Mechanismus, um an diese Dateien zu gelangen.

Wann soll eine Klasse, ein Namespace oder ein Paket exportiert werden?

Um einen Typ oder Wert außerhalb der Datei sichtbar zu machen, in der er sich befindet, müssen Sie ihn exportieren, wenn er sich innerhalb eines Namespace befindet. Ob Sie es auf der obersten Ebene oder innerhalb eines Namespace exportieren, entscheidet, ob es sich jetzt in einem externen Modul befindet.

Wenn wir Paket / Namespace exportieren, werden alle darin enthaltenen Klassen exportiert oder müssen explizit exportiert werden

Klassen in einem Namespace müssen immer explizit exportiert werden, damit die Klasse zur Kompilierungszeit außerhalb der Datei angezeigt wird, in der sie definiert ist.

Wie kann jeder von ihnen importiert / benötigt werden?

Dies hängt davon ab, ob Sie externe Module verwenden. Ein externes Modul muss immer importiert werden, um es zu "verwenden". Das Importieren eines Namespace, der sich nicht in einem externen Modul befindet, stellt lediglich einen Alias ​​für den Namespace bereit. Sie müssen dem Alias ​​immer noch den Typ / was auch immer voranstellen (und aus diesem Grund möchten Sie Namespaces im Allgemeinen nicht mit externen Modulen verwenden. Dies bedeutet, dass Sie immer ein Präfix verwenden müssen, wenn Sie auf etwas verweisen, das vom externen Modul bereitgestellt wird.) Namespaces, die sich nicht in einem externen Modul befinden, können sich über Dateien erstrecken. Wenn Sie sich also im selben Namespace befinden, können Sie auf alles verweisen, was vom exportiert wird Namespace ohne Import.

Um das oben Genannte wirklich zu verstehen, benötigen Sie Hintergrundwissen. Bei Verweisen / Namespaces / externen Modulen ist es wichtig zu verstehen, was diese Konstrukte zur Kompilierungszeit und zur Laufzeit tun.

Referenzanweisungen werden zur Kompilierungszeit verwendet, um Typinformationen zu lokalisieren. Ihre Quelle enthält ein bestimmtes Symbol. Wie findet der TypeScript-Compiler die Definition für dieses Symbol? Die Referenzanweisung wurde größtenteils vom tsconfig.json-Mechanismus subsumiert. Mit tsconfig.json teilen Sie dem Compiler mit, wo sich alle Ihre Quellen befinden.

Namespaces können Typdefinitionen und / oder Implementierungen enthalten. Wenn ein Namespace nur Typinformationen enthält, hat er überhaupt keine Laufzeitmanifestation. Sie können dies überprüfen, indem Sie sich die JS-Ausgabe ansehen und eine leere JS-Datei suchen. Wenn ein Namespace Implementierungscode enthält, wird der Code in einen Abschluss eingeschlossen, der einer globalen Variablen mit demselben Namen wie der Namespace zugewiesen ist. Bei verschachtelten Namespaces gibt es eine globale Variable für den Stammnamenraum. Überprüfen Sie erneut die JS-Ausgabe. Namespaces sind historisch gesehen die Art und Weise, wie JS-clientseitige Bibliotheken versucht haben, das Problem mit Namenskollisionen zu vermeiden. Die Idee ist, Ihre gesamte Bibliothek in einen Abschluss zu packen und dann einen möglichst kleinen globalen Footprint bereitzustellen - nur eine globale Variable, die auf den Abschluss verweist. Nun, das Problem ist immer noch, dass Sie einen Namen im globalen Raum beansprucht haben. Was wäre, wenn Sie beispielsweise zwei Versionen einer Bibliothek wollten? Ein TypeScript-Namespace hat immer noch das Problem, wie die Quelle für den Namespace gefunden werden kann. Das heißt, Quellcode, der auf AB verweist, hat immer noch das Problem, dem Compiler mitzuteilen, wie AB zu finden ist - entweder mithilfe von Referenzanweisungen oder mithilfe von tsconfig.json. Oder indem Sie den Namespace in ein externes Modul einfügen und dann das externe Modul importieren.

Externe Module stammen von serverseitigem JS. Es besteht eine Eins-zu-Eins-Entsprechung zwischen einem externen Modul und einer Datei im Dateisystem. Sie können die Dateisystemverzeichnisstruktur verwenden, um externe Module in einer verschachtelten Struktur zu organisieren. Das Importieren eines externen Moduls führt im Allgemeinen immer zu einer Laufzeitabhängigkeit von diesem externen Modul (die Ausnahme ist, wenn Sie ein externes Modul importieren, aber dann keinen seiner Exporte an der Wertposition verwenden - das heißt, Sie importieren nur das externe Modul um an seine Typinformationen zu gelangen). Ein externes Modul befindet sich implizit in einem Abschluss, und dies ist der Schlüssel: Der Benutzer des Moduls kann den Abschluss jeder gewünschten lokalen Variablen zuweisen. TypeScript / ES6 fügt zusätzliche Syntax hinzu, um die Exporte der externen Module lokalen Namen zuzuordnen. Dies ist jedoch nur eine nette Sache. Auf der Serverseite Das Auffinden eines externen Moduls ist relativ einfach: Suchen Sie einfach die Datei, die das externe Modul darstellt, im lokalen Dateisystem. Wenn Sie externe Module auf der Clientseite in einem Browser verwenden möchten, wird dies komplexer, da es kein Äquivalent zu dem Dateisystem gibt, in dem das Modul zum Laden verfügbar ist. Auf der Clientseite müssen Sie nun eine Möglichkeit haben, alle diese Dateien in einem Formular zu bündeln, das im Browser remote verwendet werden kann. Hier können Modulbündler wie Webpack (Webpack bietet jedoch weit mehr als nur Module bündeln) und Browserify kommt ins Spiel. Modulbündler ermöglichen die Laufzeitauflösung Ihrer externen Module im Browser. In einem Browser wird es komplexer, da es kein Äquivalent zum Dateisystem gibt, in dem das Modul zum Laden verfügbar ist. Auf der Clientseite müssen Sie nun eine Möglichkeit haben, alle diese Dateien in einem Formular zu bündeln, das im Browser remote verwendet werden kann. Hier können Modulbündler wie Webpack (Webpack bietet jedoch weit mehr als nur Module bündeln) und Browserify kommt ins Spiel. Modulbündler ermöglichen die Laufzeitauflösung Ihrer externen Module im Browser. In einem Browser wird es komplexer, da es kein Äquivalent zum Dateisystem gibt, in dem das Modul zum Laden verfügbar ist. Auf der Clientseite müssen Sie nun eine Möglichkeit haben, alle diese Dateien in einem Formular zu bündeln, das im Browser remote verwendet werden kann. Hier können Modulbündler wie Webpack (Webpack bietet jedoch weit mehr als nur Module bündeln) und Browserify kommt ins Spiel. Modulbündler ermöglichen die Laufzeitauflösung Ihrer externen Module im Browser.

Reales Szenario: AngularJS. Stellen Sie sich vor, externe Module existieren nicht, verwenden Sie einen einzelnen Namespace, um die Verschmutzung des globalen Raums zu begrenzen (im folgenden Beispiel befindet sich nur eine einzige Variable im globalen Raum), exportieren Sie nur Schnittstellen und verwenden Sie AngularJS-Abhängigkeitsinjektion, um Implementierungen vorzunehmen zur Verwendung verfügbar. Fügen Sie alle Klassen in ein Verzeichnisstammverzeichnis ein, fügen Sie dem Stammverzeichnis eine tsconfig.json hinzu, installieren Sie anglejs-Typisierungen unter demselben Verzeichnisstammverzeichnis, damit tsconfig.json sie ebenfalls aufnimmt, und kombinieren Sie alle Ausgaben in einer JS-Datei. Dies funktioniert für die meisten Projekte einwandfrei, wenn die Wiederverwendung von Code kein großes Problem darstellt.

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

Verwenden von IIFE, um eine Verschmutzung des globalen Laufzeitbereichs zu vermeiden. In diesem Beispiel werden überhaupt keine globalen Variablen erstellt. (Eine tsconfig.json wird angenommen.)

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

})();

Mit IIFE können Sie die Einführung von MyApp als globale Variable im ersten Beispiel eliminieren.

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

Es gibt zwei Dinge:

  • Ein Modul in TypeScript ist ein Standard-ES6-Begriff. Es verwendet import/ exportSchlüsselwörter auf der obersten Ebene des Codes.
  • Ein Namespace ist ein spezifischer Begriff für TypeScript, mit dessen Hilfe der Code veraltet organisiert werden kann.

Namespaces

Es ist fast eine veraltete Vorstellung. Vor ES6-Modulen bestand die übliche Methode zum Trennen von JavaScript-Code in einem Browser darin, globale Variablen zu erstellen. Beispielsweise befanden sich alle Funktionen einer API wie der Unterstrich in einer globalen Variablen mit dem Namen _.

Diese alte Vorgehensweise ähnelt Java-Paketen oder PHP-Namespaces. Es ist nicht an das Web angepasst. Der neue ECMAScript-Standard löst Probleme wie: Wie werden zwei Bibliotheken mit demselben Namen verwendet? Wie verwende ich zwei verschiedene Versionen derselben Bibliothek?

Hinweis: In TypeScript-Versionen vor der ECMAScript-Definition von „Modulen“ (Sommer 2014) wurden Namespaces als „interne Module“ und Module als „externe Module“ bezeichnet.

Hinweis 2: Das Schlüsselwort exportin a namespaceist eine nicht standardmäßige TypeScript-Verwendung des Schlüsselworts. Es ist das Mittel, um etwas zu deklarieren, das von außerhalb des Namespace öffentlich zugänglich ist.

ES6-Module

Ein Modul ist eine Datei, die Schlüsselwörter enthält importoder sich exportauf der obersten Ebene des Codes befindet.

TypeScript folgt dem Standard von ECMAScript. Ich schlage vor, eine gute Einführung in ES6-Module in einem Artikel von Mozilla zu lesen .

Wenn Sie Module in einer Front-End-Anwendung (in einem Browser) verwenden möchten, müssen Sie einen Bundler ( Webpack [ die Dokumentation hier ], Browserify) oder einen Loader ( SystemJS [ ein Tutorial hier ], RequireJS) und verwenden um TypeScript mit dieser Umgebung zu konfigurieren.

Wenn Ihr Code in Node.js ausgeführt wird, konfigurieren Sie einfach den TypeScript-Compiler, um das CommonJS-Format zu generieren.

Hinweis: In einem Modul kann ein Namespace deklariert werden. In diesem Fall kann von außerhalb des Moduls nicht als globale Variable darauf zugegriffen werden. Es kann jedoch aus dem Modul exportiert werden.


Bündelt der Typoskript-Compiler Module nicht automatisch in einer .js-Datei?
— Kokodoko

@ Kokodoko, Nein. Es kann nur verketten.
— Paleo

Ähm ... was ist der Unterschied? ... Ich verwende Module und nur tsc, um eine einzelne .js-Datei zu erstellen. Ich muss nicht systemJS, webpack oder browserify verwenden.
— Kokodoko

1
@ Kokodoko Du hast recht! Mein Fehler! Es scheint, dass der TypeScript-Compiler seit TS 1.8 ein Bundle erstellen kann. Ich hatte die Funktion verpasst.
— Paleo

Davon abgesehen ... Meine Module werden kompiliert, aber nach SystemJS.import ('/ js / main.js') nicht ausgeführt. Irgendeine Idee, wie man die App "startet"?
— Kokodoko

13
  1. Modul ist für externe Pakete 2. Namespace ist für interne Pakete

Tatsächlich wurde das moduleSchlüsselwort durch das namespaceSchlüsselwort ersetzt.

Eine bessere Aussage ist daher, dass Module früher als externe Module bezeichnet wurden, Namespace als interne Module.

Mehr

Hoffe, das hilft weiter: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html


8
Entschuldigung, die Fragen sind unbeantwortet. Ich hatte erwartet, dies anhand eines Beispiels aus der Praxis und nicht anhand einer Lehrbuchdefinition ausführlich zu erklären.
— Reddy

Sie meinen, es gibt jetzt keine Modultastatur in TypeScript?
— Mohammad Kermani

7

" erfordern " und " importieren " sind in der Funktionalität gleichwertig. Wir können beide austauschbar verwenden, da wir Transpiler haben, denen es egal ist, ob der Browser sie nativ unterstützt oder nicht. Während " require " seine Wurzeln im alten Codierungsstil hat, der von CommonJS bis 2009 stammt, leitet "import" seine Syntax von der allgemein akzeptierten ES6 (ES2015) -Syntax ab. Sie sollten also " importieren " für neue Projekte verwenden und nicht " erfordern ".


1
Genau. So verwirrend und so wenig Informationen darüber, dass es im Grunde dasselbe ist. Frage mich, warum dies abgelehnt wurde?
— Shai Petel
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.