Išsamus TypeScript modulių atskyrimo vadovas, apimantis klasikines ir node modulių atskyrimo strategijas, baseUrl, paths ir geriausios praktikos valdant importo kelius sudėtinguose projektuose.
TypeScript Module Resolution: Demystifying Import Path Strategies
TypeScript'o modulių atskyrimo sistema yra kritinis didelių ir lengvai prižiūrimų programų kūrimo aspektas. Supratimas, kaip TypeScript lokalizuoja modulius, remiantis importo keliais, yra būtinas organizuojant jūsų kodo bazę ir išvengiant dažnų klaidų. Šis išsamus vadovas gilinsis į TypeScript modulių atskyrimo subtilybes, apimdamas klasikines ir node modulių atskyrimo strategijas, baseUrl
ir paths
vaidmenį tsconfig.json
, bei geriausias praktikas efektyviai valdant importo kelius.
What is Module Resolution?
Modulių atskyrimas yra procesas, kurio metu TypeScript kompiliatorius nustato modulio vietą pagal importo teiginį jūsų kode. Kai rašote import { SomeComponent } from './components/SomeComponent';
, TypeScript turi išsiaiškinti, kur SomeComponent
modulis iš tikrųjų yra jūsų failų sistemoje. Šį procesą valdo taisyklių ir konfigūracijų rinkinys, kuris apibrėžia, kaip TypeScript ieško modulių.
Neteisingas modulių atskyrimas gali sukelti kompiliavimo klaidas, vykdymo laiko klaidas ir sunkumus suprantant projekto struktūrą. Todėl tvirtas modulių atskyrimo supratimas yra labai svarbus bet kuriam TypeScript kūrėjui.
Module Resolution Strategies
TypeScript siūlo dvi pagrindines modulių atskyrimo strategijas, konfigūruojamas per moduleResolution
kompiliatoriaus parinktį tsconfig.json
:
- Classic: Originali modulių atskyrimo strategija, naudojama TypeScript.
- Node: Atkuria Node.js modulių atskyrimo algoritmą, todėl idealiai tinka projektams, skirtiems Node.js arba naudojantiems npm paketus.
Classic Module Resolution
classic
modulių atskyrimo strategija yra paprastesnė iš dviejų. Ji ieško modulių tiesioginiu būdu, pereinant aukštyn katalogų medį nuo importuojančio failo.
How it works:
- Pradedant nuo katalogo, kuriame yra importuojantis failas.
- TypeScript ieško failo nurodytu pavadinimu ir plėtiniais (
.ts
,.tsx
,.d.ts
). - Jei nerastas, jis pereina į aukštesnį katalogą ir pakartoja paiešką.
- Šis procesas tęsiasi tol, kol modulis bus rastas arba pasiekiamas failų sistemos šaknis.
Example:
Apsvarstykite šią projekto struktūrą:
project/
├── src/
│ ├── components/
│ │ ├── SomeComponent.ts
│ │ └── index.ts
│ └── app.ts
├── tsconfig.json
Jei app.ts
yra importo teiginys import { SomeComponent } from './components/SomeComponent';
, classic
modulių atskyrimo strategija veiks taip:
- Ieškos
./components/SomeComponent.ts
,./components/SomeComponent.tsx
arba./components/SomeComponent.d.ts
src
kataloge. - Jei nerastas, jis pereis į aukštesnį katalogą (projekto šaknį) ir pakartos paiešką, kuri mažai tikėtina, kad pavyks šiuo atveju, nes komponentas yra
src
aplanke.
Limitations:
- Ribotas lankstumas tvarkant sudėtingas projekto struktūras.
- Nepalaiko paieškos
node_modules
viduje, todėl netinka projektams, priklausantiems nuo npm paketų. - Gali sukelti daugžodžius ir pasikartojančius santykinius importo kelius.
When to Use:
classic
modulių atskyrimo strategija paprastai tinka tik labai mažiems projektams su paprasta katalogų struktūra ir be išorinių priklausomybių. Šiuolaikiniuose TypeScript projektuose beveik visada turėtų būti naudojama node
modulių atskyrimo strategija.
Node Module Resolution
node
modulių atskyrimo strategija atkuria modulių atskyrimo algoritmą, naudojamą Node.js. Dėl to tai yra pageidaujamas pasirinkimas projektams, skirtiems Node.js arba naudojantiems npm paketus, nes jis užtikrina nuoseklų ir nuspėjamą modulių atskyrimo veikimą.
How it works:
node
modulių atskyrimo strategija vadovaujasi sudėtingesniu taisyklių rinkiniu, pirmenybę teikiant paieškai node_modules
viduje ir tvarkant skirtingus failų plėtinius:
- Non-relative imports: Jei importo kelias neprasideda
./
,../
arba/
, TypeScript mano, kad jis nurodo modulį, esantįnode_modules
. Jis ieškos modulio šiose vietose: node_modules
esamame kataloge.node_modules
aukštesniame kataloge.- ...ir taip toliau, iki failų sistemos šaknies.
- Relative imports: Jei importo kelias prasideda
./
,../
arba/
, TypeScript traktuoja jį kaip santykinį kelią ir ieško modulio nurodytoje vietoje, atsižvelgiant į šiuos dalykus: - Pirmiausia ieško failo nurodytu pavadinimu ir plėtiniais (
.ts
,.tsx
,.d.ts
). - Jei nerastas, jis ieško katalogo nurodytu pavadinimu ir failo, pavadinto
index.ts
,index.tsx
arbaindex.d.ts
tame kataloge (pvz.,./components/index.ts
, jei importas yra./components
).
Example:
Apsvarstykite šią projekto struktūrą su priklausomybe nuo lodash
bibliotekos:
project/
├── src/
│ ├── utils/
│ │ └── helpers.ts
│ └── app.ts
├── node_modules/
│ └── lodash/
│ └── lodash.js
├── tsconfig.json
Jei app.ts
yra importo teiginys import * as _ from 'lodash';
, node
modulių atskyrimo strategija veiks taip:
- Atpažins, kad
lodash
yra nesantykis importas. - Ieškos
lodash
node_modules
kataloge projekto šaknyje. - Ras
lodash
modulįnode_modules/lodash/lodash.js
.
Jei helpers.ts
yra importo teiginys import { SomeHelper } from './SomeHelper';
, node
modulių atskyrimo strategija veiks taip:
- Atpažins, kad
./SomeHelper
yra santykinis importas. - Ieškos
./SomeHelper.ts
,./SomeHelper.tsx
arba./SomeHelper.d.ts
src/utils
kataloge. - Jei nė vienas iš šių failų neegzistuoja, jis ieškos katalogo pavadinimu
SomeHelper
ir tada ieškosindex.ts
,index.tsx
arbaindex.d.ts
tame kataloge.
Advantages:
- Palaiko
node_modules
ir npm paketus. - Užtikrina nuoseklų modulių atskyrimo veikimą su Node.js.
- Supaprastina importo kelius, leidžiant nesantykius importus moduliams
node_modules
.
When to Use:
node
modulių atskyrimo strategija yra rekomenduojamas pasirinkimas daugumai TypeScript projektų, ypač tiems, kurie skirti Node.js arba naudoja npm paketus. Jis užtikrina lankstesnę ir patikimesnę modulių atskyrimo sistemą, palyginti su classic
strategija.
Configuring Module Resolution in tsconfig.json
tsconfig.json
failas yra pagrindinis jūsų TypeScript projekto konfigūracijos failas. Jis leidžia nurodyti kompiliatoriaus parinktis, įskaitant modulių atskyrimo strategiją, ir tinkinti, kaip TypeScript tvarko jūsų kodą.
Štai pagrindinis tsconfig.json
failas su node
modulių atskyrimo strategija:
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"outDir": "dist",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
Key compilerOptions
related to module resolution:
moduleResolution
: Nurodo modulių atskyrimo strategiją (classic
arbanode
).baseUrl
: Nurodo pagrindinį katalogą, skirtą nesantykiniams modulių pavadinimams atskirti.paths
: Leidžia konfigūruoti pasirinktinius kelių atvaizdavimus moduliams.
baseUrl
and paths
: Controlling Import Paths
baseUrl
ir paths
kompiliatoriaus parinktys suteikia galingus mechanizmus, skirtus valdyti, kaip TypeScript atskiria importo kelius. Jie gali žymiai pagerinti jūsų kodo skaitomumą ir prižiūrimumą, leisdami naudoti absoliučius importus ir kurti pasirinktinius kelių atvaizdavimus.
baseUrl
baseUrl
parinktis nurodo pagrindinį katalogą, skirtą nesantykiniams modulių pavadinimams atskirti. Kai baseUrl
yra nustatytas, TypeScript atskirs nesantykius importo kelius, palyginti su nurodytu pagrindiniu katalogu, o ne su esamu darbiniu katalogu.
Example:
Apsvarstykite šią projekto struktūrą:
project/
├── src/
│ ├── components/
│ │ ├── SomeComponent.ts
│ │ └── index.ts
│ └── app.ts
├── tsconfig.json
Jei tsconfig.json
yra tai:
{
"compilerOptions": {
"moduleResolution": "node",
"baseUrl": "./src"
}
}
Tada app.ts
galite naudoti šį importo teiginį:
import { SomeComponent } from 'components/SomeComponent';
Vietoj:
import { SomeComponent } from './components/SomeComponent';
TypeScript atskirs components/SomeComponent
, palyginti su ./src
katalogu, nurodytu baseUrl
.
Benefits of using baseUrl
:
- Supaprastina importo kelius, ypač giliai įdėtuose kataloguose.
- Padaro kodą skaitesnį ir lengviau suprantamą.
- Sumažina klaidų riziką, kurią sukelia neteisingi santykiniai importo keliai.
- Palengvina kodo refaktorizavimą, atskiriant importo kelius nuo fizinės failų struktūros.
paths
paths
parinktis leidžia konfigūruoti pasirinktinius kelių atvaizdavimus moduliams. Tai suteikia lankstesnį ir galingesnį būdą valdyti, kaip TypeScript atskiria importo kelius, leidžiantį kurti modulių slapyvardžius ir peradresuoti importus į skirtingas vietas.
paths
parinktis yra objektas, kuriame kiekvienas raktas atspindi kelio šabloną, o kiekviena reikšmė yra kelių pakeitimų masyvas. TypeScript bandys suderinti importo kelią su kelių šablonais ir, jei atitiktis bus rasta, pakeis importo kelią nurodytais pakeitimo keliais.
Example:
Apsvarstykite šią projekto struktūrą:
project/
├── src/
│ ├── components/
│ │ ├── SomeComponent.ts
│ │ └── index.ts
│ └── app.ts
├── libs/
│ └── my-library.ts
├── tsconfig.json
Jei tsconfig.json
yra tai:
{
"compilerOptions": {
"moduleResolution": "node",
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@mylib": ["../libs/my-library.ts"]
}
}
}
Tada app.ts
galite naudoti šiuos importo teiginius:
import { SomeComponent } from '@components/SomeComponent';
import { MyLibraryFunction } from '@mylib';
TypeScript atskirs @components/SomeComponent
į components/SomeComponent
, remiantis @components/*
kelių atvaizdavimu, o @mylib
į ../libs/my-library.ts
, remiantis @mylib
kelių atvaizdavimu.
Benefits of using paths
:
- Kurkite modulių slapyvardžius, supaprastinkite importo kelius ir pagerinkite skaitomumą.
- Peradresuoja importus į skirtingas vietas, palengvindamas kodo refaktorizavimą ir priklausomybių valdymą.
- Leidžia jums atskirti fizinę failų struktūrą nuo importo kelių, todėl jūsų kodas tampa atsparesnis pokyčiams.
- Palaiko pakaitos simbolius (
*
), skirtus lanksčiam kelių atitikimui.
Common Use Cases for paths
:
- Creating aliases for frequently used modules: For example, you can create an alias for a utility library or a set of shared components.
- Mapping to different implementations based on the environment: For example, you can map an interface to a mock implementation for testing purposes.
- Simplifying imports from monorepos: In a monorepo, you can use
paths
to map to modules within different packages.
Best Practices for Managing Import Paths
Efektyvus importo kelių valdymas yra labai svarbus kuriant dideles ir lengvai prižiūrimas TypeScript programas. Štai keletas geriausių praktikų, kurių reikia laikytis:
- Use the
node
module resolution strategy:node
modulių atskyrimo strategija yra rekomenduojamas pasirinkimas daugumai TypeScript projektų, nes jis užtikrina nuoseklų ir nuspėjamą modulių atskyrimo veikimą. - Configure
baseUrl
: NustatykitebaseUrl
parinktį į šaltinio kodo šakninį katalogą, kad supaprastintumėte importo kelius ir pagerintumėte skaitomumą. - Use
paths
for custom path mappings: Naudokitepaths
parinktį, kad sukurtumėte modulių slapyvardžius ir peradresuotumėte importus į skirtingas vietas, atskirdami fizinę failų struktūrą nuo importo kelių. - Avoid deeply nested relative import paths: Giliai įdėti santykiniai importo keliai (pvz.,
../../../../utils/helpers
) gali būti sunkiai skaitomi ir prižiūrimi. NaudokitebaseUrl
irpaths
, kad supaprastintumėte šiuos kelius. - Be consistent with your import style: Pasirinkite nuoseklų importo stilių (pvz., naudodami absoliučius importus arba santykinius importus) ir laikykitės jo visame savo projekte.
- Organize your code into well-defined modules: Organizuojant kodą į gerai apibrėžtus modulius, jį lengviau suprasti ir prižiūrėti, o importo kelių valdymo procesas supaprastėja.
- Use a code formatter and linter: Kodo formatuotojas ir linteris gali padėti jums užtikrinti nuoseklius kodavimo standartus ir nustatyti galimas importo kelių problemas.
Troubleshooting Module Resolution Issues
Modulių atskyrimo problemas gali būti varginantis derinti. Štai keletas dažnų problemų ir sprendimų:
- "Cannot find module" error:
- Problem: TypeScript cannot find the specified module.
- Solution:
- Verify that the module is installed (if it's an npm package).
- Check the import path for typos.
- Ensure that the
moduleResolution
,baseUrl
, andpaths
options are configured correctly intsconfig.json
. - Confirm that the module file exists at the expected location.
- Incorrect module version:
- Problem: You're importing a module with an incompatible version.
- Solution:
- Check your
package.json
file to see which version of the module is installed. - Update the module to a compatible version.
- Check your
- Circular dependencies:
- Problem: Two or more modules depend on each other, creating a circular dependency.
- Solution:
- Refactor your code to break the circular dependency.
- Use dependency injection to decouple modules.
Real-World Examples Across Different Frameworks
TypeScript modulių atskyrimo principai taikomi įvairiuose JavaScript framework'uose. Štai kaip jie dažniausiai naudojami:
- React:
- React projektai labai priklauso nuo komponentais pagrįstos architektūros, todėl tinkamas modulių atskyrimas yra labai svarbus.
- Naudojant
baseUrl
, norint nurodytisrc
katalogą, leidžiami švarūs importai, tokie kaipimport MyComponent from 'components/MyComponent';
. - Bibliotekos, tokios kaip
styled-components
arbamaterial-ui
, paprastai importuojamos tiesiogiai išnode_modules
, naudojantnode
atskyrimo strategiją.
- Angular:
- Angular CLI automatiškai konfigūruoja
tsconfig.json
su pagrįstomis numatytosiomis reikšmėmis, įskaitantbaseUrl
irpaths
. - Angular moduliai ir komponentai dažnai organizuojami į funkcijų modulius, naudojant kelių slapyvardžius supaprastintiems importams modulių viduje ir tarp jų. Pavyzdžiui,
@app/shared
gali būti atvaizduotas į bendrai naudojamą modulių katalogą.
- Angular CLI automatiškai konfigūruoja
- Vue.js:
- Panašiai kaip React, Vue.js projektams naudinga naudoti
baseUrl
, kad būtų supaprastintas komponentų importas. - Vuex parduotuvės moduliai gali būti lengvai susieti naudojant
paths
, pagerinant kodo bazės organizavimą ir skaitomumą.
- Panašiai kaip React, Vue.js projektams naudinga naudoti
- Node.js (Express, NestJS):
- NestJS, pavyzdžiui, skatina plačiai naudoti kelių slapyvardžius modulių importui valdyti struktūruotoje programoje.
node
modulių atskyrimo strategija yra numatytoji ir būtina norint dirbti sunode_modules
.
Conclusion
TypeScript'o modulių atskyrimo sistema yra galingas įrankis, skirtas organizuoti kodo bazę ir efektyviai valdyti priklausomybes. Suprantant skirtingas modulių atskyrimo strategijas, baseUrl
ir paths
vaidmenį ir geriausias importo kelių valdymo praktikas, galite kurti dideles, lengvai prižiūrimas ir skaitomas TypeScript programas. Tinkamai sukonfigūravus modulių atskyrimą tsconfig.json
, galite žymiai pagerinti kūrimo darbo eigą ir sumažinti klaidų riziką. Eksperimentuokite su skirtingomis konfigūracijomis ir suraskite požiūrį, kuris geriausiai atitinka jūsų projekto poreikius.