Candy Cup Analogie
Version 1: Eine Tasse für jede Süßigkeit
Angenommen, Sie haben einen Code wie diesen geschrieben:
Mod1.ts
export namespace A {
export class Twix { ... }
}
Mod2.ts
export namespace A {
export class PeanutButterCup { ... }
}
Mod3.ts
export namespace A {
export class KitKat { ... }
}
Sie haben dieses Setup erstellt:
Jedes Modul (Blatt Papier) erhält eine eigene Tasse mit dem Namen A
. Dies ist nutzlos - Sie organisieren Ihre Süßigkeiten hier nicht wirklich , sondern fügen lediglich einen zusätzlichen Schritt (Herausnehmen aus der Tasse) zwischen Ihnen und den Leckereien hinzu.
Version 2: Eine Tasse im globalen Bereich
Wenn Sie keine Module verwenden, können Sie Code wie diesen schreiben (beachten Sie das Fehlen von export
Deklarationen):
global1.ts
namespace A {
export class Twix { ... }
}
global2.ts
namespace A {
export class PeanutButterCup { ... }
}
global3.ts
namespace A {
export class KitKat { ... }
}
Dieser Code erstellt einen zusammengeführten Namespace A
im globalen Bereich:
Dieses Setup ist nützlich, gilt jedoch nicht für Module (da Module den globalen Bereich nicht verschmutzen).
Version 3: Cupless gehen
Gehen wir zurück zum ursprünglichen Beispiel, die Becher A
, A
und A
tun Sie keinen Gefallen. Stattdessen können Sie den Code wie folgt schreiben:
Mod1.ts
export class Twix { ... }
Mod2.ts
export class PeanutButterCup { ... }
Mod3.ts
export class KitKat { ... }
um ein Bild zu erstellen, das so aussieht:
Viel besser!
Wenn Sie immer noch darüber nachdenken, wie oft Sie den Namespace wirklich mit Ihren Modulen verwenden möchten, lesen Sie weiter ...
Dies sind nicht die Konzepte, nach denen Sie suchen
Wir müssen zu den Ursprüngen zurückkehren, warum Namespaces überhaupt existieren, und untersuchen, ob diese Gründe für externe Module sinnvoll sind.
Organisation : Namespaces sind praktisch, um logisch verwandte Objekte und Typen zu gruppieren. In C # finden Sie beispielsweise alle Sammlungstypen in System.Collections
. Durch die Organisation unserer Typen in hierarchischen Namespaces bieten wir Benutzern dieser Typen eine gute "Discovery" -Erfahrung.
Namenskonflikte : Namensräume sind wichtig , um zu vermeiden Namenskollisionen. Beispielsweise könnten Sie My.Application.Customer.AddForm
und My.Application.Order.AddForm
- zwei Typen mit demselben Namen, aber einem anderen Namespace haben. In einer Sprache, in der alle Bezeichner im selben Stammbereich vorhanden sind und alle Assemblys alle Typen laden, ist es wichtig, dass sich alles in einem Namespace befindet.
Sind diese Gründe in externen Modulen sinnvoll?
Organisation : Externe Module sind notwendigerweise bereits in einem Dateisystem vorhanden. Wir müssen sie nach Pfad und Dateiname auflösen, damit wir ein logisches Organisationsschema verwenden können. Wir können einen /collections/generic/
Ordner mit einem list
Modul darin haben.
Namenskonflikte : Dies gilt überhaupt nicht für externe Module. Innerhalb eines Moduls gibt es keinen plausiblen Grund, zwei Objekte mit demselben Namen zu haben. Auf der Verbraucherseite kann der Verbraucher eines bestimmten Moduls den Namen auswählen, mit dem er auf das Modul verweist, sodass versehentliche Namenskonflikte nicht möglich sind.
Selbst wenn Sie nicht glauben, dass diese Gründe durch die Funktionsweise von Modulen angemessen berücksichtigt werden, funktioniert die "Lösung" des Versuchs, Namespaces in externen Modulen zu verwenden, nicht einmal.
Boxen in Boxen in Boxen
Eine Geschichte:
Dein Freund Bob ruft dich an. "Ich habe ein großartiges neues Organisationsschema in meinem Haus", sagt er, "komm und schau es dir an!". Ordentlich, lass uns sehen, was Bob sich ausgedacht hat.
Sie beginnen in der Küche und öffnen die Speisekammer. Es gibt 60 verschiedene Boxen mit der Aufschrift "Pantry". Sie wählen eine Box nach dem Zufallsprinzip aus und öffnen sie. Im Inneren befindet sich eine einzelne Box mit der Aufschrift "Grains". Sie öffnen das Feld "Getreide" und finden ein einzelnes Feld mit der Bezeichnung "Pasta". Sie öffnen die Box "Pasta" und finden eine einzelne Box mit der Bezeichnung "Penne". Sie öffnen diese Schachtel und finden erwartungsgemäß eine Tüte Penne-Nudeln.
Etwas verwirrt nehmen Sie eine benachbarte Kiste mit der Aufschrift "Pantry". Im Inneren befindet sich eine einzelne Schachtel mit der Aufschrift "Grains". Sie öffnen das Feld "Getreide" und finden erneut ein einzelnes Feld mit der Bezeichnung "Pasta". Sie öffnen die Box "Pasta" und finden eine einzelne Box mit der Bezeichnung "Rigatoni". Sie öffnen diese Schachtel und finden ... eine Tüte Rigatoni-Nudeln.
"Es ist toll!" sagt Bob. "Alles ist in einem Namespace!".
"Aber Bob ..." antwortest du. „Ihr Organisationsschema ist nutzlos. Sie haben ein paar Kisten zu öffnen, um etwas zu bekommen, und es ist eigentlich nicht mehr praktisch alles als zu finden , wenn Sie hatte gerade alles setzen in einer Box statt drei . In der Tat, da Ihr Die Speisekammer ist bereits Regal für Regal sortiert, Sie brauchen die Kisten überhaupt nicht. Warum stellen Sie die Nudeln nicht einfach auf das Regal und holen sie ab, wenn Sie sie brauchen? "
"Du verstehst nicht - ich muss sicherstellen, dass niemand anderes etwas in den 'Pantry'-Namespace einfügt. Und ich habe alle meine Nudeln sicher im Pantry.Grains.Pasta
Namespace organisiert, damit ich es leicht finden kann."
Bob ist ein sehr verwirrter Mann.
Module sind ihre eigene Box
Wahrscheinlich ist im wirklichen Leben etwas Ähnliches passiert: Sie bestellen ein paar Dinge bei Amazon, und jeder Artikel wird in einer eigenen Schachtel mit einer kleineren Schachtel angezeigt, wobei Ihr Artikel in einer eigenen Verpackung verpackt ist. Selbst wenn die Innenboxen ähnlich sind, werden die Sendungen nicht sinnvoll "kombiniert".
In Übereinstimmung mit der Box-Analogie ist die wichtigste Beobachtung, dass externe Module ihre eigene Box sind . Es mag ein sehr komplexes Element mit vielen Funktionen sein, aber jedes externe Modul ist eine eigene Box.
Anleitung für externe Module
Nachdem wir herausgefunden haben, dass wir keine 'Namespaces' verwenden müssen, wie sollen wir unsere Module organisieren? Es folgen einige Leitprinzipien und Beispiele.
Exportieren Sie so nah wie möglich an die oberste Ebene
- Wenn Sie nur eine einzelne Klasse oder Funktion exportieren, verwenden Sie
export default
:
MyClass.ts
export default class SomeType {
constructor() { ... }
}
MyFunc.ts
function getThing() { return 'thing'; }
export default getThing;
Verbrauch
import t from './MyClass';
import f from './MyFunc';
var x = new t();
console.log(f());
Dies ist optimal für die Verbraucher. Sie können Ihren Typ benennen, was immer sie wollen ( t
in diesem Fall) und müssen keine zusätzlichen Punkte machen, um Ihre Objekte zu finden.
- Wenn Sie mehrere Objekte exportieren, platzieren Sie sie alle auf der obersten Ebene:
MyThings.ts
export class SomeType { ... }
export function someFunc() { ... }
Verbrauch
import * as m from './MyThings';
var x = new m.SomeType();
var y = m.someFunc();
- Wenn Sie eine große Anzahl von Dingen exportieren, sollten Sie nur dann das Schlüsselwort
module
/ verwenden namespace
:
MyLargeModule.ts
export namespace Animals {
export class Dog { ... }
export class Cat { ... }
}
export namespace Plants {
export class Tree { ... }
}
Verbrauch
import { Animals, Plants} from './MyLargeModule';
var x = new Animals.Dog();
Rote Flaggen
Alle folgenden sind rote Fahnen für die Modulstrukturierung. Stellen Sie sicher, dass Sie nicht versuchen, Ihre externen Module zu benennen, wenn eines dieser Elemente für Ihre Dateien gilt:
- Eine Datei, deren einzige Deklaration der obersten Ebene lautet
export module Foo { ... }
( Foo
alles entfernen und auf einer Ebene nach oben verschieben)
- Eine Datei, die eine Single hat
export class
oder export function
nichtexport default
- Mehrere Dateien, die
export module Foo {
auf oberster Ebene dasselbe haben (denken Sie nicht, dass diese zu einer kombiniert werden Foo
!)
An Ryans Antwort ist nichts auszusetzen, aber für Leute, die hierher gekommen sind, um eine Struktur für eine Klasse pro Datei beizubehalten , während sie ES6-Namespaces weiterhin korrekt verwenden, lesen Sie bitte diese hilfreiche Ressource von Microsoft.
Eine Sache, die mir nach dem Lesen des Dokuments unklar ist, ist: wie man das gesamte (zusammengeführte) Modul mit einem einzigen importiert
import
.Bearbeiten Sie das Zurückkreisen, um diese Antwort zu aktualisieren. In TS gibt es einige Ansätze für den Namespace.
Alle Modulklassen in einer Datei.
Importieren Sie Dateien in den Namespace und weisen Sie sie neu zu
Fässer
Eine letzte Überlegung. Sie können jede Datei mit einem Namespace versehen
Da man jedoch zwei Klassen aus demselben Namespace importiert, beschwert sich TS, dass es eine doppelte Kennung gibt. Die einzige Lösung besteht diesmal darin, den Namespace als Alias zu verwenden.
Dieses Aliasing ist absolut abscheulich, also tu es nicht. Mit dem obigen Ansatz sind Sie besser dran. Persönlich bevorzuge ich das "Fass".
quelle
const fs = require('fs')
,fs
Ist der Namespace.import * as moment from 'moment'
,moment
Ist der Namespace. Dies ist Ontologie, nicht die Spezifikation.require
Beispiel gilt aus einer Reihe von Gründen nicht für sie, einschließlich der Tatsache, dass ES6-Namespaces möglicherweise nicht aufgerufen werden, währendrequire
ein einfaches Objekt zurückgegeben wird, das möglicherweise aufgerufen werden kann.Versuchen Sie, nach Ordner zu organisieren:
baseTypes.ts
dog.ts
tree.ts
LivingThings.ts
main.ts
Die Idee ist, dass Ihr Modul selbst sich nicht darum kümmern sollte / weiß, dass es an einem Namespace teilnimmt, aber dies stellt Ihre API dem Verbraucher auf kompakte, vernünftige Weise zur Verfügung, die unabhängig von der Art des Modulsystems ist, das Sie für das Projekt verwenden.
quelle
tree.ts
wenn es überhaupt kein exportiertes Mitglied hat?import
undrequire
zusammen in einer Aussage.Kleine Verbesserung von Albinofrenchy Antwort:
base.ts
dog.ts
Dinge.ts
main.ts
quelle
OP Ich bin bei dir, Mann. Auch an dieser Antwort mit mehr als 300 Stimmen ist nichts auszusetzen, aber meine Meinung ist:
Was ist falsch daran, Klassen einzeln in ihre gemütlichen, warmen eigenen Dateien zu legen? Ich meine, das wird die Dinge viel besser aussehen lassen, oder? (oder jemand wie eine 1000-Zeilen-Datei für alle Modelle)
Wenn also die erste erreicht wird, müssen wir importieren importieren importieren ... nur in jede der Modelldateien importieren, wie man, srsly, eine Modelldatei, eine .d.ts-Datei, warum gibt es so viele * ist da drin? es sollte einfach, ordentlich und das war's. Warum brauche ich dort Importe? Warum? C # hat aus einem bestimmten Grund Namespaces.
Und bis dahin verwenden Sie buchstäblich "filenames.ts" als Bezeichner. Als Identifikatoren ... Kommst du jetzt auf 2017 und machen wir das immer noch? Ich gehe zurück zum Mars und schlafe noch 1000 Jahre.
Meine Antwort lautet leider: Nein, Sie können das "Namespace" -Ding nicht funktionsfähig machen, wenn Sie nicht alle diese Importe oder Dateinamen als Bezeichner verwenden (was ich wirklich dumm finde). Eine weitere Option ist: Legen Sie alle diese Abhängigkeiten in ein Feld namens filenameasidentifier.ts und verwenden Sie
Wickeln Sie sie so ein, dass sie nicht versuchen, auf andere Klassen mit demselben Namen zuzugreifen, wenn sie nur versuchen, eine Referenz von der Klasse zu erhalten, die direkt über ihnen sitzt.
quelle
Einige der Fragen / Kommentare, die ich zu diesem Thema gesehen habe, klingen für mich so, als würde die Person dort verwenden,
Namespace
wo sie "Modulalias" bedeutet. Wie Ryan Cavanaugh in einem seiner Kommentare erwähnt hat, können Sie mit einem 'Wrapper'-Modul mehrere Module erneut exportieren.Wenn Sie wirklich alles aus demselben Modulnamen / Alias importieren möchten, kombinieren Sie ein Wrapper-Modul mit einer Pfadzuordnung in Ihrem
tsconfig.json
.Beispiel:
./path/to/CompanyName.Products/Foo.ts
./path/to/CompanyName.Products/Bar.ts
./path/to/CompanyName.Products/index.ts
tsconfig.json
main.ts
Hinweis : Die Modulauflösung in den Ausgabe-.js-Dateien muss irgendwie gehandhabt werden, z. B. mit diesem https://github.com/tleunen/babel-plugin-module-resolver
Beispiel
.babelrc
für die Behandlung der Aliasauflösung:quelle
Probieren Sie dieses Namespaces-Modul aus
NamespaceModuleFile.ts
bookTreeCombine.ts
--- Zusammenstellungsteil ---
quelle
dog.ts
tree.ts
quelle
Die richtige Art, Ihren Code zu organisieren, besteht darin, anstelle von Namespaces separate Verzeichnisse zu verwenden. Jede Klasse befindet sich in einer eigenen Datei im entsprechenden Namespace-Ordner. index.ts exportiert nur jede Datei erneut. In der Datei index.ts sollte sich kein tatsächlicher Code befinden. Wenn Sie Ihren Code so organisieren, wird die Navigation erheblich vereinfacht und anhand der Verzeichnisstruktur selbstdokumentiert.
Sie würden es dann als solches verwenden:
quelle