Modul vs Prostor imena - Uvoz vs Zahtjev Typescript
Dobivam puno zabune s korištenjem module/namespace/export i import, require, reference. Budući da dolazim iz Java okruženja, može li mi netko ukratko objasniti kada što koristiti i koji je pravi dizajn? Imam osjećaj da pogriješim kad pišem primjer projekta
Do sada je ovo moje razumijevanje 1. module je za vanjske pakete 2. namespace je za unutarnje pakete
- Nisam shvatio kako ih kategoriziramo?
- Kada izvesti klasu, namespace ili paket?
- Ako izvezemo paket/prostor imena, sve klase unutar njega su izvezene ili ih je potrebno izričito izvesti
- Kako se svaki od njih može uvesti/zahtijevati?
Prema dokumentaciji, ako stvaram svaku "ts" datoteku za svakog menadžera/model, Typescript ne preporučuje korištenje "namespace-a"? Trebam li izravno koristiti reference paths?
Molim objasnite detaljno jer dolazim iz drugog područja i nisam siguran u vezi ES6/ES5 itd.
Vidio sam nekoliko ljudi kako se zbunjuju ili postavljaju ista pitanja. Nadam se da netko može detaljno objasniti s primjerom iz stvarnog svijeta
Odgovor #1
Nisam shvatio kako ih kategoriziramo?
Prostori imena koriste se za organiziranje/enkapsulaciju vašeg koda. Vanjski moduli koriste se za organiziranje/enkapsulaciju vašeg koda I za pronalaženje vašeg koda u vrijeme izvođenja. U praksi imate dva izbora u vrijeme izvođenja: 1) spojiti sav transpilirani kod u jednu datoteku, ili 2) koristiti vanjske module i imati više datoteka te zahtijevati neki drugi mehanizam za pristup tim datotekama.
Kada izvesti klasu, prostor imena ili paket?
Da biste tip ili vrijednost učinili vidljivima izvan datoteke u kojoj se nalaze, morate ih izvesti ako se nalaze unutar namespacea. Hoćete li ih izvesti na vrh razine ili unutar namespacea odlučit će hoće li sada biti u vanjskom modulu.
Ako izvezemo paket/namespacu, sve klase unutar njega su izvezene ili ih je potrebno izričito izvesti
Klase u prostoru imena uvijek će morati biti izričito izvezene da bi klasa bila vidljiva tijekom vrijeme kompajliranja izvan datoteke u kojoj je definirana.
Kako se svaki od njih može uvesti/zahtijevati?
Ovo ovisi o tome koristite li vanjske module. Vanjski modul će uvijek trebati biti uvezen da biste ga "koristili". Uvoz imenskog prostora koji nije u vanjskom modulu uistinu je samo pružanje aliasa za taj imenski prostor -- i dalje morate prefiksirati tip/što god s tim aliasom (i zbog toga uglavnom ne želite koristiti imenske prostore s vanjskim modulima; to znači da uvijek morate koristiti prefiks prilikom referenciranja bilo čega što pruža vanjski modul.) Imenski prostori koji nisu u vanjskom modulu mogu se protezati preko datoteka, pa ako ste u istom imenskom prostoru možete se referirati na bilo što što je izvezeno iz imenskog prostora bez potrebe za bilo kakvim uvozom.
Da biste zaista razumjeli gore navedeno, trebate neko osnovno znanje. Ključna stvar koju treba razumjeti kod referenci/prostora imena/vanjskih modula je što ti konstruktori rade tijekom kompilacije i što rade tijekom izvođenja programa.
Referentne direktive koriste se u vrijeme kompajliranja za pronalaženje informacija o tipu. Vaš izvorni kod ima u sebi određeni simbol. Kako TypeScript kompajler pronalazi definiciju za taj simbol? Referentna direktiva u velikoj je mjeri zamijenjena mehanizmom tsconfig.json -- koristeći tsconfig.json, rekli ste kompajleru gdje se nalaze svi vaši izvori.
Prostori imena mogu sadržavati definicije tipova i/ili implementaciju. Ako prostor imena sadrži samo informacije o tipu, tada nema nikakvu izvedbu u runtime-u -- to možete provjeriti gledajući JS izlaz i pronalazeći prazan JS datoteku. Ako prostor imena ima implementacijski kod, tada je kod omotan unutar closure-a koji je dodijeljen globalnoj varijabli istog imena kao prostor imena. Kod ugniježđenih prostora imena, postojat će globalna varijabla za root prostor imena. Opet, provjerite JS izlaz. Prostori imena su povijesno način na koji su JS biblioteke na klijentskoj strani pokušale izbjeći problem kolizije imena. Ideja je omotati cijelu vašu biblioteku u jedan closure i zatim izložiti što manji globalni otisak -- samo jednu globalnu varijablu koja referencira closure. Pa, problem je i dalje u tome što ste rezervirali ime u globalnom prostoru. Što ako biste, recimo, željeli dvije verzije biblioteke? TypeScript namespace i dalje ima problem kako pronaći izvor za taj namespace. To jest, izvorni kod koji referencira A.B i dalje ima problem kako reći kompilatoru kako pronaći A.B -- bilo korištenjem referentnih direktiva ili korištenjem tsconfig.json. Ili stavljanjem namespacea u eksterni modul i zatim uvozom eksternog modula.
Vanjski moduli potječu od server-side JS-a. Postoji jedno-na-jedno odgovaranje između vanjskog modula i datoteke na datotečnom sustavu. Možete koristiti strukturu direktorija datotečnog sustava za organiziranje vanjskih modula u ugniježđenu strukturu. Uvoz vanjskog modula općenito će uvijek uvesti runtime ovisnost o tom vanjskom modulu (iznimka je kada uvezete vanjski modul, ali tada ne koristite nijednu od njegovih izvezenih vrijednosti u vrijednosnom položaju - to jest, uvozite vanjski modul samo da biste dobili njegove informacije o tipovima). Vanjski modul je implicitno u zatvoru, i ovo je ključno: korisnik modula može dodijeliti zatvor onoj lokalnoj varijabli koju želi. TypeScript/ES6 dodaje dodatnu sintaksu oko mapiranja izvoza vanjskih modula na lokalne nazive, ali to je samo pogodnost. Na strani poslužitelja, pronalaženje vanjskog modula je relativno jednostavno: samo locirajte datoteku koja predstavlja vanjski modul na lokalnom datotečnom sustavu. Ako želite koristiti vanjske module na strani klijenta, u pregledniku, postaje složenije jer ne postoji ekvivalent datotečnog sustava koji bi imao modul dostupan za učitavanje. Tako da sada na strani klijenta trebate način za spajanje svih tih datoteka u oblik koji se može koristiti na daljinu u pregledniku -- ovdje nastupaju alat za spajanje modula poput Webpacka (iako Webpack radi puno više od samog spajanja modula) i Browserify. Alati za spajanje modula omogućuju vrijeme izvođenja rezolucije vaših vanjskih modula u pregledniku.
Stvarni scenarij: AngularJS. Pretvarajte se da vanjski moduli ne postoje, koristite jedan namespace kako biste ograničili nered u globalnom prostoru (u primjeru ispod jedina varijabla MyApp je sve što postoji u globalnom prostoru), izvezite samo sučelja i koristite AngularJS dependency-injection kako biste implementacije učinili dostupnima za korištenje. Stavite sve klase u direktorij root, dodajte tsconfig.json u root, instalirajte angularjs typings u isti root direktorij kako bi ih tsconfig.json također prepoznao, kombinirajte sav izlaz u jednoj JS datoteci. Ovo će dobro funkcionirati za većinu projekata ako ponovno korištenje koda nije previše važno.
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);
}
Korištenje IIFE za izbjegavanje zagađivanja globalnog runtime okruženja. U ovom primjeru nije stvorena nijedna globalna varijabla. (Pretpostavlja se 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;
}
})();
Korištenjem IIFE-a možete izbjeći uvođenje MyApp kao globalne varijable u prvom primjeru.
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);
})();
Odgovor #2
Postoje dvije stvari:
- Modul u TypeScriptu je standardni ES6 pojam, koristi ključne riječi
import/exportna vrhunskoj razini koda; - Namespace je pojam specifičan za TypeScript koji pomaže organizirati kod na zastario način.
Prostori imena
To je gotovo zastarjela ideja. Prije ES6 modula, uobičajeni način za razdvajanje JavaScript koda u pregledniku bio je stvaranje globalnih varijabli. Na primjer, sve funkcije API-ja poput underscore nalazile su se u globalnoj varijabli nazvanoj _.
Ovaj stari način postupanja je poput Java paketa ili PHP prostora imena. Nije prilagođen mreži. Novi standard ECMAScript rješava probleme kao što su: kako koristiti dvije biblioteke koje imaju isto ime? Kako koristiti dvije različite verzije iste biblioteke?
Obavijest: U verzijama TypeScripta prije ECMAScript definicije „modula” (ljeto 2014.), prostorima imena se govorilo „interni moduli”, a modulima „eksterni moduli”.
Obavijest 2: Ključna riječ export unutar namespace je nestandardna upotreba ključne riječi u TypeScriptu. To je način da se deklarira nešto što je javno dostupno izvan namespace-a.
ES6 moduli
Modul je datoteka koja sadrži ključne riječi import ili export na gornjoj razini koda.
TypeScript slijedi standard iz ECMAScripta. Predlažem da pročitate dobar uvod u ES6 module u članku na Mozilla stranici.
Ako želite koristiti module u front-end aplikaciji (u pregledniku), tada ćete morati koristiti bundler (Webpack [dokumentacija ovdje], Browserify) ili loader (SystemJS [tutorijal ovdje], RequireJS) i konfigurirati TypeScript s ovim okruženjem.
Ako se vaš kod izvršava u Node.js-u, jednostavno konfigurirajte TypeScript kompajler da generira CommonJS format.
Napomena: Ime prostor može biti deklarirano u modulu. U tom slučaju, neće biti dostupno kao globalna varijabla izvan modula. Međutim, može biti izvezeno iz modula.
Odgovor #3
- modul je za vanjske pakete, a prostor imena je za unutarnje pakete
Zapravo je ključna riječ module zamijenjena ključnom riječi namespace.
Bolja izjava je stoga Moduli su ono što se prije zvalo vanjski moduli, prostor imena je ono što se prije zvalo unutarnji moduli.
Više
Nadam se da ovo dalje pomaže: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html
