Nie rozumiem, jak je klasyfikujemy?
Przestrzenie nazw służą do organizowania / hermetyzacji kodu. Moduły zewnętrzne służą do organizowania / hermetyzacji kodu ORAZ do lokalizowania kodu w czasie wykonywania. W praktyce masz dwie możliwości w czasie wykonywania: 1) połączyć cały transpiled kod w jeden plik lub 2) użyć zewnętrznych modułów i mieć wiele plików i wymagać innego mechanizmu, aby uzyskać dostęp do tych plików.
Kiedy wyeksportować klasę, przestrzeń nazw lub pakiet?
Aby typ lub wartość były widoczne poza plikiem, w którym się znajdują, musisz je wyeksportować, jeśli znajduje się w przestrzeni nazw. To, czy wyeksportujesz go na najwyższym poziomie, czy w przestrzeni nazw, zadecyduje, czy znajduje się teraz w module zewnętrznym.
Jeśli eksportujemy pakiet / przestrzeń nazw, wszystkie zawarte w nich klasy są eksportowane lub muszą być jawnie eksportowane
Klasy w przestrzeni nazw zawsze będą musiały być jawnie eksportowane, aby klasa była widoczna w czasie kompilacji poza plikiem, w którym została zdefiniowana.
W jaki sposób każdy z nich może być importowany / wymagany?
Zależy to od tego, czy używasz modułów zewnętrznych. Aby go „używać”, zawsze trzeba będzie zaimportować moduł zewnętrzny. Importowanie przestrzeni nazw, która nie znajduje się w zewnętrznym module, to tak naprawdę po prostu zapewnienie aliasu dla przestrzeni nazw - nadal musisz poprzedzić typ / cokolwiek aliasem (i dlatego generalnie nie chcesz używać przestrzeni nazw z zewnętrznymi modułami; oznacza to, że zawsze musisz używać przedrostka przy odwoływaniu się do czegokolwiek dostarczonego przez moduł zewnętrzny). Przestrzenie nazw, które nie są w module zewnętrznym, mogą obejmować pliki, więc jeśli jesteś w tej samej przestrzeni nazw, możesz odwołać się do wszystkiego, co wyeksportował przestrzeni nazw bez potrzeby jakiegokolwiek importu.
Aby naprawdę zrozumieć powyższe, potrzebujesz podstawowej wiedzy. Kluczową rzeczą do zrozumienia w przypadku odwołań / przestrzeni nazw / modułów zewnętrznych jest to, co te konstrukcje robią w czasie kompilacji i co robią w czasie wykonywania.
Dyrektywy referencyjne są używane w czasie kompilacji do lokalizowania informacji o typie. Twoje źródło ma w sobie określony symbol. W jaki sposób kompilator TypeScript lokalizuje definicję tego symbolu? Dyrektywa reference została w dużej mierze podciągnięta przez mechanizm tsconfig.json - używając tsconfig.json, informujesz kompilator, gdzie znajdują się wszystkie źródła.
Przestrzenie nazw mogą zawierać definicje typów i / lub implementacje. Jeśli przestrzeń nazw zawiera tylko informacje o typie, nie ma w ogóle manifestacji środowiska wykonawczego - możesz to sprawdzić, patrząc na dane wyjściowe JS i znajdując pusty plik JS. Jeśli przestrzeń nazw ma kod implementacji, to kod jest zawijany wewnątrz zamknięcia, które jest przypisane do zmiennej globalnej o takiej samej nazwie jak przestrzeń nazw. W przypadku zagnieżdżonych przestrzeni nazw będzie istniała zmienna globalna dla głównej przestrzeni nazw. Ponownie sprawdź wyjście JS. Przestrzenie nazw to historycznie sposób, w jaki biblioteki JS po stronie klienta próbowały uniknąć problemu z kolizjami nazw. Chodzi o to, aby zawinąć całą bibliotekę w jedno zamknięcie, a następnie ujawnić jak najmniejszy globalny ślad - tylko jedną zmienną globalną odwołującą się do zamknięcia. Cóż, problem w dalszym ciągu polega na tym, że wybrałeś nazwę w globalnej przestrzeni. A gdybyś chciał, powiedzmy, dwóch wersji biblioteki? Przestrzeń nazw TypeScript nadal ma problem z lokalizacją źródła przestrzeni nazw. Oznacza to, że kod źródłowy, który odwołuje się do AB, nadal ma problem z informacją kompilatora, jak zlokalizować AB - albo przy użyciu dyrektyw referencyjnych, albo za pomocą tsconfig.json. Lub umieszczając przestrzeń nazw w module zewnętrznym, a następnie importując moduł zewnętrzny.
Zewnętrzne moduły pochodzą z JS po stronie serwera. Istnieje korespondencja „jeden do jednego” między modułem zewnętrznym a plikiem w systemie plików. Struktury katalogów systemu plików można używać do organizowania modułów zewnętrznych w strukturę zagnieżdżoną. Importowanie modułu zewnętrznego generalnie powoduje zależność środowiska wykonawczego od tego modułu zewnętrznego (wyjątkiem jest sytuacja, gdy importujesz moduł zewnętrzny, ale nie używasz żadnego z jego eksportu w pozycji wartości - to znaczy importujesz tylko moduł zewnętrzny aby uzyskać informacje o typie). Moduł zewnętrzny znajduje się niejawnie w zamknięciu i to jest klucz: użytkownik modułu może przypisać zamknięcie do dowolnej zmiennej lokalnej. TypeScript / ES6 dodaje dodatkową składnię dotyczącą mapowania eksportu modułów zewnętrznych na nazwy lokalne, ale to tylko drobiazg. Po stronie serwera zlokalizowanie modułu zewnętrznego jest stosunkowo proste: po prostu zlokalizuj plik reprezentujący moduł zewnętrzny w lokalnym systemie plików. Jeśli chcesz używać zewnętrznych modułów po stronie klienta, w przeglądarce staje się to bardziej złożone, ponieważ nie ma odpowiednika systemu plików, który ma moduł dostępny do załadowania. Więc teraz po stronie klienta potrzebujesz sposobu na spakowanie wszystkich tych plików w formularz, który może być używany zdalnie w przeglądarce - to jest miejsce, w którym pakiety modułów, takie jak Webpack (chociaż Webpack robi o wiele więcej niż pakiety modułów) i Browserify wkracza do gry. Pakiety modułów umożliwiają rozpoznawanie w czasie wykonywania zewnętrznych modułów w przeglądarce. w przeglądarce staje się to bardziej złożone, ponieważ nie ma odpowiednika systemu plików, w którym moduł jest dostępny do załadowania. Więc teraz po stronie klienta potrzebujesz sposobu na spakowanie wszystkich tych plików w formularz, który może być używany zdalnie w przeglądarce - to jest miejsce, w którym pakiety modułów, takie jak Webpack (chociaż Webpack robi o wiele więcej niż pakiety modułów) i Browserify wkracza do gry. Pakiety modułów umożliwiają rozpoznawanie w czasie wykonywania zewnętrznych modułów w przeglądarce. w przeglądarce staje się to bardziej złożone, ponieważ nie ma odpowiednika systemu plików, w którym moduł jest dostępny do załadowania. Więc teraz po stronie klienta potrzebujesz sposobu na spakowanie wszystkich tych plików w formularz, który może być używany zdalnie w przeglądarce - to jest miejsce, w którym pakiety modułów, takie jak Webpack (chociaż Webpack robi o wiele więcej niż pakiety modułów) i Browserify wkracza do gry. Pakiety modułów umożliwiają rozpoznawanie w czasie wykonywania zewnętrznych modułów w przeglądarce.
Scenariusz ze świata rzeczywistego: AngularJS. Udawaj, że moduły zewnętrzne nie istnieją, użyj jednej przestrzeni nazw, aby ograniczyć zanieczyszczenie globalnej przestrzeni (w poniższym przykładzie pojedyncza zmienna MyApp to wszystko, co znajduje się w przestrzeni globalnej), eksportuj tylko interfejsy i użyj wstrzykiwania zależności AngularJS do implementacji dostępne do użytku. Umieść wszystkie klasy w katalogu głównym, dodaj tsconfig.json do katalogu głównego, zainstaluj typy angularjs w tym samym katalogu głównym, aby tsconfig.json również je odebrał, połącz wszystkie dane wyjściowe w jednym pliku JS. Będzie to działać dobrze w przypadku większości projektów, jeśli ponowne użycie kodu nie stanowi większego problemu.
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);
}
Korzystanie z IIFE w celu uniknięcia zanieczyszczania globalnego zakresu czasu wykonywania. W tym przykładzie w ogóle nie są tworzone żadne zmienne globalne. (Zakładany jest plik 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;
}
})();
Korzystając z IIFE, możesz wyeliminować wprowadzanie MyApp jako zmiennej globalnej w pierwszym przykładzie.
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);
})();