Moduł vs Przestrzeń nazw - Import vs Wymaganie Typescriptu


102

Dostaję dużo zamieszania module/namespace/exporti import, require, referenceużytkowania. Jako że mam doświadczenie w Javie, czy ktoś może mi w skrócie wyjaśnić, kiedy używać czego i jaki jest właściwy projekt? Kiedy piszę przykładowy projekt, czuję się źle

Jak dotąd to jest moje rozumienie 1. moduledotyczy pakietów zewnętrznych 2. namespacedotyczy pakietów wewnętrznych

  • Nie rozumiem, jak je klasyfikujemy?
  • Kiedy wyeksportować klasę, przestrzeń nazw lub pakiet?
  • Jeśli eksportujemy pakiet / przestrzeń nazw, wszystkie zawarte w nich klasy są eksportowane lub muszą być jawnie eksportowane
  • W jaki sposób każdy z nich może być importowany / wymagany?

Według doc , jeśli tworzę każdy plik „ts” dla każdego menedżera / modelu, Typescript nie zaleca używania „przestrzeni nazw”? Używać bezpośrednio ścieżek referencyjnych?

Proszę wyjaśnić szczegółowo, ponieważ pochodzę z innego środowiska i nie jestem pewien co do ES6 / ES5 itp. ".

Widziałem kilka osób podnoszących / mylących się z tymi samymi pytaniami. Mam nadzieję, że ktoś może szczegółowo wyjaśnić scenariusz z prawdziwego świata


ma dużo uwagi, która odpowiada na twoje pytanie, nie jestem pewien, dlaczego została nagrodzona
— Dan Pantry

@DanPantry, przed rozpoczęciem zlecenia nie było ani jednej odpowiedzi :(
— Reddy

mój błąd - nie porównałem znaczników czasu.
— Dan Pantry

Odpowiedzi:


80

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

30

Są dwie rzeczy:

  • Moduł w maszynopisie jest standardowym ES6 pojęcie, używa import/ exportsłowa kluczowe na poziomie górnym kodu;
  • Nazw to specyficzny pogląd na maszynie do pomocy zorganizować kod w przestarzałym mody.

Przestrzenie nazw

Jest to pojęcie prawie przestarzałe. Przed modułami ES6 powszechnym sposobem oddzielania kodu JavaScript w przeglądarce było tworzenie zmiennych globalnych. Na przykład wszystkie funkcje interfejsu API, takie jak podkreślenie, znajdowały się w zmiennej globalnej o nazwie _.

Ten stary sposób postępowania przypomina pakiety Java lub przestrzenie nazw PHP. Nie jest przystosowany do sieci. Nowy standard ECMAScript rozwiązuje takie kwestie, jak: jak używać dwóch bibliotek o tej samej nazwie? Jak korzystać z dwóch różnych wersji tej samej biblioteki?

Uwaga: W wersjach języka TypeScript poprzedzających definicję „modułów” w ECMAScript (lato 2014), przestrzenie nazw były nazywane „modułami wewnętrznymi”, a moduły - „modułami zewnętrznymi”.

Uwaga 2: Słowo kluczowe exportwewnątrz a namespaceto niestandardowe użycie słowa kluczowego w języku TypeScript. Jest to sposób na zadeklarowanie rzeczy, która jest publicznie dostępna spoza przestrzeni nazw.

Moduły ES6

Moduł to plik zawierający słowa kluczowe importlub exportna najwyższym poziomie kodu.

TypeScript jest zgodny ze standardem z ECMAScript. Proponuję przeczytać dobre wprowadzenie do modułów ES6 w artykule Mozilli.

Jeśli chcesz używać modułów w aplikacji front-end (w przeglądarce), będziesz musiał użyć pakietu ( Webpack [ dokumentacja tutaj ], Browserify) lub programu ładującego ( SystemJS [ tutorial tutaj ], RequireJS) i aby skonfigurować TypeScript w tym środowisku.

Jeśli Twój kod jest wykonywany w Node.js, po prostu skonfiguruj kompilator TypeScript do generowania formatu CommonJS.

Uwaga: przestrzeń nazw można zadeklarować w module. W takim przypadku nie będzie dostępna jako zmienna globalna spoza modułu. Można go jednak wyeksportować z modułu.


Czy kompilator maszynopisu nie łączy automatycznie modułów w jeden plik .js?
— Kokodoko

@Kokodoko, nie. To może po prostu łączyć.
— Paleo

Erm ... jaka jest różnica? .... Używam modułów i tylko tsc do zbudowania jednego pliku .js. Nie muszę używać systemJS, webpacka ani przeglądarki.
— Kokodoko

1
@Kokodoko Masz rację! Mój błąd! Wygląda na to, że kompilator TypeScript może zbudować pakiet od czasu TS 1.8 , przegapiłem tę funkcję.
— Paleo

To powiedziawszy ... Moje moduły kompilują się, ale nie są wykonywane po SystemJS.import ('/ js / main.js'); Masz jakiś pomysł, jak „uruchomić” aplikację?
— Kokodoko

14
  1. moduł jest przeznaczony dla pakietów zewnętrznych. 2. Przestrzeń nazw jest dla pakietów wewnętrznych

W rzeczywistości modulesłowo kluczowe zostało zastąpione namespacesłowem kluczowym.

Lepszą instrukcją jest więc to, że moduły to to, co kiedyś nazywano modułami zewnętrznymi, przestrzeń nazw to to, co nazywano modułami wewnętrznymi.

Więcej

Mam nadzieję, że to pomoże dalej: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html


8
z przykrością informuję, pytania pozostają bez odpowiedzi. Spodziewałem się wyjaśnienia szczegółowo na przykładzie prawdziwego świata, a nie definicji podręcznika.
— Reddy

Masz na myśli, że w TypeScript nie ma teraz klawiatury modułu?
— Mohammad Kermani,

7

„ Wymagaj ” i „ Importuj ” są równoważne pod względem funkcjonalności. możemy używać ich obu zamiennie, ponieważ mamy transpilery, które tak naprawdę nie dbają o to, czy przeglądarka obsługuje je natywnie, czy nie. ale chociaż „ require ” ma swoje korzenie w starym stylu kodowania pochodzącym z CommonJS z 2009 roku, „import” wywodzi swoją składnię z powszechnie akceptowanej składni ES6 (ES2015). więc powinieneś używać „ importu ” dla nowych projektów, a nie „ wymagaj ”.


1
Dokładnie. Tak zagmatwane i tak mało informacji na ten temat, mówiąc, że jest w zasadzie to samo. Zastanawiam się, dlaczego głosowanie odrzucono?
— Shai Petel

0

Zamieszanie nazw

We wczesnych dniach Typescript przestrzenie nazw były nazywane modułami wewnętrznymi, a moduły ES6 - modułami zewnętrznymi .

Teraz do deklarowania przestrzeni nazw zespół Typescript zaleca użycie składni namespace { }zamiast module { }składni, aby uniknąć zamieszania w nazewnictwie z modułami zewnętrznymi. Ponieważ moduły zewnętrzne są teraz po prostu „modułami”, a moduły wewnętrzne są „przestrzeniami nazw”.


Przestrzenie nazw

Deklaracja

Przestrzeń nazw w Typescript można zadeklarować za pomocą słowa kluczowego namespacelub module. Oba słowa kluczowe robią to samo. Następnie możemy zdecydować, która część naszej przestrzeni nazw ma zostać upubliczniona za pomocą exportsłowa kluczowego.

// 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"

Grupowanie logiczne

Przed ES6 przestrzenie nazw były używane w Typescript do hermetyzacji interfejsów, klas, funkcji i zmiennych w celu obsługi grupy powiązanych funkcji i ukrywania szczegółów implementacji. W ten sposób mogliśmy zapobiec wyciekowi zmiennych do przestrzeni globalnej. Pomogło to w lepszej organizacji kodu i zapobieganiu kolizjom nazw. Teraz w tym celu zaleca się użycie modułów ES6.

Przestrzenie nazw są teraz używane w deklaracjach otoczenia nazw.

Wykorzystanie pojedynczego pliku

Możemy zadeklarować przestrzenie nazw w wielu plikach i można je łączyć za pomocą --outFileflagi. Następnie możemy użyć tego połączonego pliku wewnątrz <script>tagu na naszej stronie HTML. Pozwala nam to na dobrą strukturę naszego kodu w aplikacji internetowej po stronie klienta z uwzględnieniem wszystkich zależności.


Moduły

Deklaracja

Moduły są również nazywane modułami ES6. Używamy wielu plików do grupowania powiązanych funkcjonalności i po prostu używamy exportsłowa kluczowego, aby żądany obiekt był publicznie widoczny.

// 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"

Grupowanie logiczne

Logiczne grupowanie w moduły uzyskuje się za pomocą oddzielnych plików do grupowania powiązanych funkcjonalności. Z tego powodu zewnętrzne moduły nazywane są również modułami plików .

Wykorzystanie pojedynczego pliku

Nie ładujemy modułów aplikacji internetowej po stronie klienta za pomocą <script>tagu, ponieważ przeglądarki mogą działać wolno podczas pobierania wielu plików i renderowania strony w tym samym czasie. W tym celu używamy modułów ładujących, takich jak CommonJS, AMD, SystemJS, które umożliwiają nam ładowanie plików asynchronicznie lub łączenie plików modułów zewnętrznych w jeden zoptymalizowany plik.

Po stronie serwera, szczególnie w Node.js, moduły są zdecydowanie zalecane.

Otóż ​​to!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.