Kattava opas JavaScript Import Maps -ominaisuuksiin, keskittyen tehokkaaseen 'scopes'-ominaisuuteen, scope inheritanceen ja moduulien määrittelyhierarkiaan modernissa web-kehityksessä.
Uuden Web-kehityksen Aikakauden Avaaminen: Syväsukellus JavaScript Import Maps Scope Inheritanceen
JavaScript-moduulien matka on ollut pitkä ja mutkainen. Varhaisen webin globaalin nimiavaruuden kaaoksesta Node.js:n CommonJS:n ja selainten AMD:n kaltaisiin kehittyneisiin malleihin kehittäjät ovat jatkuvasti etsineet parempia tapoja organisoida ja jakaa koodia. Natiivien ES-moduulien (ESM) saapuminen merkitsi monumentaalista muutosta standardoiden moduulijärjestelmän suoraan JavaScript-kieleen ja selaimiin.
Tämän uuden standardin mukana tuli kuitenkin merkittävä este selainpohjaiselle kehitykselle. Yksinkertaiset ja elegantit import-lauseet, joihin olimme tottuneet Node.js:ssä, kuten import _ from 'lodash';
, aiheuttaisivat virheen selaimessa. Tämä johtuu siitä, että selaimilla, toisin kuin Node.js:llä sen `node_modules`-algoritmin kanssa, ei ole natiivia mekanismia näiden "bare module specifiers" -määrittelijöiden muuntamiseksi kelvolliseksi URL-osoitteeksi.
Vuosien ajan ratkaisu oli pakollinen build-vaihe. Webpackin, Rollupin ja Parcelin kaltaiset työkalut niputtaisivat koodimme muuntaen nämä bare specifiers -määrittelijät poluiksi, jotka selain ymmärtäisi. Vaikka nämä työkalut olivat tehokkaita, ne lisäsivät monimutkaisuutta, konfiguraation yläpuolta ja hitaampia palautesilmukoita kehitysprosessiin. Entä jos olisi olemassa natiivi, build-työkaluvapaa tapa ratkaista tämä? Astu sisään JavaScript Import Maps.
Import maps ovat W3C-standardi, joka tarjoaa natiivin mekanismin JavaScript-importtien käyttäytymisen hallintaan. Ne toimivat hakutaulukkona, joka kertoo selaimelle tarkalleen, miten moduulin määrittelijät muunnetaan konkreettisiksi URL-osoitteiksi. Niiden teho ulottuu kuitenkin paljon yksinkertaista aliasta pidemmälle. Todellinen pelin muuttaja on vähemmän tunnettu, mutta uskomattoman tehokas ominaisuus: `scopes`. Scopes mahdollistaa kontekstuaalisen moduulien määrittelyn, jolloin sovelluksesi eri osat voivat importata saman määrittelijän, mutta määrittää sen eri moduuleihin. Tämä avaa uusia arkkitehtonisia mahdollisuuksia mikro-frontendeille, A/B-testaukselle ja monimutkaiselle riippuvuuksien hallinnalle ilman yhtäkään riviä bundler-konfiguraatiota.
Tämä kattava opas vie sinut syvälle import maps -maailmaan keskittyen erityisesti `scopes`-ominaisuuden määrittelyhierarkian selvittämiseen. Tutkimme, miten scope inheritance (tai tarkemmin sanottuna fallback-mekanismi) toimii, analysoimme määrittelyalgoritmin ja paljastamme käytännön malleja, jotka mullistavat modernin web-kehitystyösi.
Mitä JavaScript Import Maps Ovat? Perusteellinen Yleiskatsaus
Ytimeltään import map on JSON-objekti, joka tarjoaa kartoituksen kehittäjän importoiman moduulin nimen ja vastaavan moduulitiedoston URL-osoitteen välillä. Sen avulla voit käyttää puhtaita, bare module specifiers -määrittelijöitä koodissasi aivan kuten Node.js-ympäristössä ja antaa selaimen hoitaa määrittelyn.
Perussyntaksi
Ilmoitat import mapin käyttämällä <script>
-tagia, jonka attribuutti on type="importmap"
. Tämä tagi on sijoitettava HTML-dokumenttiin ennen kaikkia <script type="module">
-tageja, jotka käyttävät kartoitetuja importteja.
Tässä on yksinkertainen esimerkki:
<!DOCTYPE html>
<html>
<head>
<!-- The Import Map -->
<script type="importmap">
{
"imports": {
"moment": "https://cdn.skypack.dev/moment",
"lodash": "/js/vendor/lodash-4.17.21.min.js",
"app/": "/js/app/"
}
}
</script>
<!-- Your Application Code -->
<script type="module" src="/js/main.js"></script>
</head>
<body>
<h1>Welcome to Import Maps!</h1>
</body>
</html>
/js/main.js
-tiedostomme sisällä voimme nyt kirjoittaa tällaista koodia:
// This works because "moment" is mapped in the import map.
import moment from 'moment';
// This works because "lodash" is mapped.
import { debounce } from 'lodash';
// This is a package-like import for your own code.
// It resolves to /js/app/utils.js because of the "app/" mapping.
import { helper } from 'app/utils.js';
console.log('Today is:', moment().format('MMMM Do YYYY'));
Puretaanpa `imports`-objekti:
"moment": "https://cdn.skypack.dev/moment"
: Tämä on suora kartoitus. Aina kun selain näkeeimport ... from 'moment'
, se hakee moduulin määritetystä CDN-URL-osoitteesta."lodash": "/js/vendor/lodash-4.17.21.min.js"
: Tämä kartoittaa `lodash`-määrittelijän paikallisesti isännöityyn tiedostoon."app/": "/js/app/"
: Tämä on polkupohjainen kartoitus. Huomaa perässä oleva kauttaviiva sekä avaimessa että arvossa. Tämä kertoo selaimelle, että kaikki `app/`-merkillä alkavat import-määrittelijät tulisi määrittää suhteessa `/js/app/`-kansioon. Esimerkiksi `import ... from 'app/auth/user.js'` määritettäisiin `/js/app/auth/user.js`-tiedostoon. Tämä on uskomattoman hyödyllistä oman sovelluskoodisi jäsennellyssä käytössä ilman sotkuisia suhteellisia polkuja, kuten `../../`.
Ydinhyödyt
Jopa tällä yksinkertaisella käytöllä edut ovat selvät:
- Build-vapaa kehitys: Voit kirjoittaa modernia, modulaarista JavaScriptiä ja suorittaa sen suoraan selaimessa ilman bundleria. Tämä johtaa nopeampiin päivityksiin ja yksinkertaisempaan kehitysympäristöön.
- Irtikytketyt riippuvuudet: Sovelluskoodisi viittaa abstrakteihin määrittelijöihin (`'moment'`) kovakoodattujen URL-osoitteiden sijaan. Tämä tekee versioiden, CDN-palveluntarjoajien vaihtamisesta tai siirtymisestä paikallisesta tiedostosta CDNiin triviaalia muuttamalla vain import map JSONia.
- Parannettu välimuisti: Koska moduulit ladataan yksittäisinä tiedostoina, selain voi tallentaa ne välimuistiin itsenäisesti. Yhden pienen moduulin muuttaminen ei vaadi valtavan nipun uudelleenlataamista.
Perusteiden Yli: Esittelyssä `scopes` Tarkempaa Hallintaa Varten
Ylätason `imports`-avain tarjoaa globaalin kartoituksen koko sovelluksellesi. Mutta mitä tapahtuu, kun sovelluksesi kasvaa monimutkaisuudeltaan? Harkitse tilannetta, jossa olet rakentamassa suurta web-sovellusta, joka integroi kolmannen osapuolen chat-widgetin. Pääsovellus käyttää versiota 5 kaaviointikirjastosta, mutta legacy-chat-widget on yhteensopiva vain version 4 kanssa.
Ilman `scopes`-ominaisuutta kohtaisit vaikean valinnan: yrittää muokata widgetiä, etsiä toinen widget tai hyväksyä, että et voi käyttää uudempaa kaaviointikirjastoa. Juuri tähän ongelmaan `scopes` on suunniteltu ratkaisemaan.
`scopes`-avain import mapissa antaa sinun määrittää erilaisia kartoituksia samalle määrittelijälle riippuen siitä, mistä import tehdään. Se tarjoaa kontekstuaalisen tai rajatun moduulien määrittelyn.
`scopes`-Ominaisuuden Rakenne
`scopes`-arvo on objekti, jossa jokainen avain on URL-etuliite, joka edustaa "scope path" -polkua. Jokaisen scope path -polun arvo on `imports`-tyyppinen objekti, joka määrittää kartoitukset, jotka koskevat nimenomaan kyseistä scopea.
Ratkaistaan kaaviointikirjasto-ongelmamme esimerkin avulla:
<script type="importmap">
{
"imports": {
"charting-lib": "/libs/charting-lib/v5/main.js",
"api-client": "/js/api/v2/client.js"
},
"scopes": {
"/widgets/chat/": {
"charting-lib": "/libs/charting-lib/v4/legacy.js"
}
}
}
</script>
<script type="module" src="/js/app.js"></script>
<script type="module" src="/widgets/chat/init.js"></script>
Näin selain tulkitsee tämän:
- Skripti, joka sijaitsee osoitteessa `/js/app.js`, haluaa importata `charting-lib`. Selain tarkistaa, vastaako skriptin polku (`/js/app.js`) mitään scope path -poluista. Se ei vastaa polkua `/widgets/chat/`. Siksi selain käyttää ylätason `imports`-kartoitusta, ja `charting-lib` määritetään `/libs/charting-lib/v5/main.js`-tiedostoon.
- Skripti, joka sijaitsee osoitteessa `/widgets/chat/init.js`, haluaa myös importata `charting-lib`. Selain näkee, että tämän skriptin polku (`/widgets/chat/init.js`) kuuluu `/widgets/chat/`-scopeen. Se etsii tästä scopesta `charting-lib`-kartoitusta ja löytää sellaisen. Näin ollen tälle skriptille ja kaikille moduuleille, jotka se importtaa kyseisen polun sisällä, `charting-lib` määritetään `/libs/charting-lib/v4/legacy.js`-tiedostoon.
`scopes`-ominaisuuden avulla olemme onnistuneesti sallineet kahden sovelluksemme osan käyttää saman riippuvuuden eri versioita, jotka elävät rauhallisesti rinnakkain ilman konflikteja. Tämä on hallintataso, joka oli aiemmin saavutettavissa vain monimutkaisilla bundler-konfiguraatioilla tai iframe-pohjaisella eristyksellä.
Ydinkonsepti: Scope Inheritance -Ominaisuuden Ymmärtäminen ja Moduulien Määrittelyhierarkia
Nyt saavumme asian ytimeen. Miten selain päättää, mitä scopea käyttää, kun useat scopet voisivat mahdollisesti vastata tiedoston polkua? Ja mitä tapahtuu ylätason `imports`-kartoituksille? Tätä ohjaa selkeä ja ennustettava hierarkia.
Kultainen Sääntö: Tarkin Scope Voittaa
Scope-määrittelyn perusperiaate on spesifisyys. Kun moduuli tietyssä URL-osoitteessa pyytää toista moduulia, selain katsoo kaikkia `scopes`-objektin avaimia. Se löytää pisimmän avaimen, joka on pyytävän moduulin URL-osoitteen etuliite. Tätä "tarkinta" vastaavaa scopea käytetään vain importin määrittämiseen. Kaikki muut scopet ohitetaan tässä tietyssä määrittelyssä.
Havainnollistetaan tätä monimutkaisemmalla tiedostorakenteella ja import mapilla.
Tiedostorakenne:
- `/index.html` (sisältää import mapin)
- `/js/main.js`
- `/js/feature-a/index.js`
- `/js/feature-a/core/logic.js`
Import Map tiedostossa `index.html`:
{
"imports": {
"api": "/js/api/v1/api.js",
"ui-kit": "/js/ui/v2/kit.js"
},
"scopes": {
"/js/feature-a/": {
"api": "/js/api/v2-beta/api.js"
},
"/js/feature-a/core/": {
"api": "/js/api/v3-experimental/api.js",
"ui-kit": "/js/ui/v1/legacy-kit.js"
}
}
}
Jäljitetään nyt `import api from 'api';`- ja `import ui from 'ui-kit';`-määrittely eri tiedostoista:
-
Tiedostossa `/js/main.js`:
- Polku `/js/main.js` ei vastaa polkua `/js/feature-a/` tai `/js/feature-a/core/`.
- Mikään scope ei vastaa. Määrittely palautuu ylätason `imports`-ominaisuuteen.
- `api` määritetään `/js/api/v1/api.js`-tiedostoon.
- `ui-kit` määritetään `/js/ui/v2/kit.js`-tiedostoon.
-
Tiedostossa `/js/feature-a/index.js`:
- Polun `/js/feature-a/index.js` etuliite on `/js/feature-a/`. Sitä ei ole etuliitetty polulla `/js/feature-a/core/`.
- Tarkin vastaava scope on `/js/feature-a/`.
- Tämä scope sisältää kartoituksen `api`-määritykselle. Siksi `api` määritetään `/js/api/v2-beta/api.js`-tiedostoon.
- Tämä scope ei sisällä kartoitusta `ui-kit`-määritykselle. Tämän määrittelijän määrittely palautuu ylätason `imports`-ominaisuuteen. `ui-kit` määritetään `/js/ui/v2/kit.js`-tiedostoon.
-
Tiedostossa `/js/feature-a/core/logic.js`:
- Polun `/js/feature-a/core/logic.js` etuliite on sekä `/js/feature-a/` että `/js/feature-a/core/`.
- Koska `/js/feature-a/core/` on pidempi ja siten tarkempi, se valitaan voittavaksi scopeksi. `/js/feature-a/`-scope ohitetaan kokonaan tässä tiedostossa.
- Tämä scope sisältää kartoituksen `api`-määritykselle. `api` määritetään `/js/api/v3-experimental/api.js`-tiedostoon.
- Tämä scope sisältää myös kartoituksen `ui-kit`-määritykselle. `ui-kit` määritetään `/js/ui/v1/legacy-kit.js`-tiedostoon.
Totuus "Inheritance"-ominaisuudesta: Se On Fallback, Ei Yhdistäminen
On tärkeää ymmärtää yleinen sekaannuksen aiheuttaja. Termi "scope inheritance" voi olla harhaanjohtava. Tarkempi scope ei peri tai yhdisty vähemmän tarkan (ylä)scopen kanssa. Määrittelyprosessi on yksinkertaisempi ja suorempi:
- Etsi yksittäinen tarkin vastaava scope importoivan skriptin URL-osoitteelle.
- Jos tämä scope sisältää kartoituksen pyydetylle määrittelijälle, käytä sitä. Prosessi päättyy tähän.
- Jos voittava scope ei sisällä kartoitusta määrittelijälle, selain välittömästi tarkistaa ylätason `imports`-objektin kartoituksen varalta. Se ei tarkastele mitään muita, vähemmän tarkkoja scopeja.
- Jos kartoitus löytyy ylätason `imports`-ominaisuudesta, sitä käytetään.
- Jos kartoitusta ei löydy voittavasta scopesta tai ylätason `imports`-ominaisuudesta, heitetään `TypeError`-virhe.
Palataan viimeiseen esimerkkiimme tämän vahvistamiseksi. Kun `ui-kit` määritettiin tiedostosta `/js/feature-a/index.js`, voittava scope oli `/js/feature-a/`. Tämä scope ei määritellyt `ui-kit`-määritystä, joten selain ei tarkistanut `/`-scopea (jota ei ole olemassa avaimena) tai mitään muuta yläscopeta. Se meni suoraan globaaliin `imports`-ominaisuuteen ja löysi kartoituksen sieltä. Tämä on fallback-mekanismi, ei CSS:n kaltainen kaskadoiva tai yhdistävä inheritance.
Käytännön Sovellukset ja Edistyneet Tilanteet
Rajattujen import maps -ominaisuuksien teho loistaa todella monimutkaisissa, todellisissa sovelluksissa. Tässä on joitain arkkitehtonisia malleja, joita ne mahdollistavat.
Mikro-Frontendit
Tämä on kiistatta tappavan hyvä käyttötapaus import map scopeille. Kuvittele verkkokauppasivusto, jossa tuotehaku, ostoskori ja kassalle siirtyminen ovat kaikki erillisiä sovelluksia (mikro-frontendejä), jotka eri tiimit ovat kehittäneet. Ne kaikki on integroitu yhdelle isäntäsivulle.
- Hakutiimi voi käyttää Reactin uusinta versiota.
- Koritiimi saattaa olla Reactin vanhemmassa, vakaassa versiossa legacy-riippuvuuden vuoksi.
- Isäntäsovellus saattaa käyttää Preactia kuoressaan ollakseen kevyt.
Import map voi orkestroida tämän saumattomasti:
{
"imports": {
"react": "/libs/preact/v10/preact.js",
"react-dom": "/libs/preact/v10/preact-dom.js",
"shared-state": "/js/state-manager.js"
},
"scopes": {
"/apps/search/": {
"react": "/libs/react/v18/react.js",
"react-dom": "/libs/react/v18/react-dom.js"
},
"/apps/cart/": {
"react": "/libs/react/v17/react.js",
"react-dom": "/libs/react/v17/react-dom.js"
}
}
}
Tässä jokainen mikro-frontend, joka tunnistetaan sen URL-polun perusteella, saa oman eristetyn React-version. Ne voivat silti kaikki importata `shared-state`-moduulin ylätason `imports`-ominaisuudesta kommunikoidakseen keskenään. Tämä tarjoaa vahvan kapseloinnin ja mahdollistaa silti hallitun yhteentoimivuuden ilman monimutkaisia bundler-federaatioasetuksia.
A/B-Testaus ja Ominaisuuksien Lipputus
Haluatko testata kassavirran uutta versiota tietyllä prosenttiosuudella käyttäjistäsi? Voit tarjota hieman erilaisen `index.html`-tiedoston testiryhmälle muokatulla import mapilla.
Kontrolliryhmän Import Map:
{
"imports": {
"checkout-flow": "/js/checkout/v1/flow.js"
}
}
Testiryhmän Import Map:
{
"imports": {
"checkout-flow": "/js/checkout/v2-beta/flow.js"
}
}
Sovelluskoodisi pysyy identtisenä: `import start from 'checkout-flow';`. Sen reititys, mikä moduuli ladataan, hoidetaan kokonaan import map -tasolla, joka voidaan luoda dynaamisesti palvelimella käyttäjäevästeiden tai muiden kriteerien perusteella.
Monorepojen Hallinta
Suuressa monorepossa sinulla saattaa olla monia sisäisiä paketteja, jotka ovat riippuvaisia toisistaan. Scopet voivat auttaa hallitsemaan näitä riippuvuuksia puhtaasti. Voit kartoittaa jokaisen paketin nimen sen lähdekoodiin kehityksen aikana.
{
"imports": {
"@my-corp/design-system": "/packages/design-system/src/index.js",
"@my-corp/utils": "/packages/utils/src/index.js"
},
"scopes": {
"/packages/design-system/": {
"@my-corp/utils": "/packages/design-system/src/vendor/utils-shim.js"
}
}
}
Tässä esimerkissä useimmat paketit saavat pääasiallisen `utils`-kirjaston. Kuitenkin `design-system`-paketti, ehkä tietystä syystä, saa shimmied- tai erilaisen `utils`-version, joka on määritetty sen omassa scopessa.
Selainten Tuki, Työkalut ja Käyttöönottonäkökohdat
Selainten Tuki
Vuoden 2023 lopulla natiivi tuki import mapeille on saatavilla kaikissa tärkeimmissä moderneissa selaimissa, mukaan lukien Chrome, Edge, Safari ja Firefox. Tämä tarkoittaa, että voit alkaa käyttää niitä tuotannossa suurelle osalle käyttäjäkuntaasi ilman polyfillsejä.
Fallbackit Vanhemmille Selaimille
Sovelluksille, joiden on tuettava vanhempia selaimia, joissa ei ole natiivia import map -tukea, yhteisöllä on vankka ratkaisu: `es-module-shims.js`-polyfill. Tämä yksittäinen skripti, kun se sisällytetään ennen import mapiasi, backporttaa tuen import mapeille ja muille moderneille moduuliominaisuuksille (kuten dynaaminen `import()`) vanhempiin ympäristöihin. Se on kevyt, taistelutestattu ja suositeltava lähestymistapa laajan yhteensopivuuden varmistamiseen.
<!-- Polyfill for older browsers -->
<script async src="https://ga.jspm.io/npm:es-module-shims@1.8.2/dist/es-module-shims.js"></script>
<!-- Your import map -->
<script type="importmap">
...
</script>
Dynaamiset, Palvelimella Luodut Mapit
Yksi tehokkaimmista käyttöönottoon liittyvistä malleista on se, että HTML-tiedostossasi ei ole lainkaan staattista import mappia. Sen sijaan palvelimesi voi luoda JSONin dynaamisesti pyynnön perusteella. Tämä mahdollistaa:
- Ympäristön Vaihdon: Tarjoa un-minified, lähdekartoitettuja moduuleja `kehitys`-ympäristössä ja minified, tuotantovalmiita moduuleja `tuotanto`-ympäristössä.
- Käyttäjäroolipohjaiset Moduulit: Järjestelmänvalvoja voi saada import mapin, joka sisältää kartoituksia vain järjestelmänvalvojille tarkoitettuihin työkaluihin.
- Lokalisointi: Kartoita `translations`-moduuli eri tiedostoihin käyttäjän `Accept-Language`-otsikon perusteella.
Parhaat Käytännöt ja Mahdolliset Sudenkuopat
Kuten minkä tahansa tehokkaan työkalun kanssa, on noudatettava parhaita käytäntöjä ja vältettävä sudenkuoppia.
- Pidä Se Luettavana: Vaikka voit luoda erittäin syviä ja monimutkaisia scope-hierarkioita, siitä voi tulla vaikea tehdä virheenkorjausta. Pyri yksinkertaisimpaan scope-rakenteeseen, joka vastaa tarpeitasi. Kommentoi import map JSONiasi, jos siitä tulee monimutkainen.
- Käytä Aina Perässä Olevia Kauttaviivoja Poluille: Kun kartoitat polun etuliitettä (kuten hakemistoa), varmista, että sekä import mapin avain että URL-arvo päättyvät `/`-merkkiin. Tämä on ratkaisevan tärkeää, jotta vastaava algoritmi toimii oikein kaikissa kyseisen hakemiston tiedostoissa. Tämän unohtaminen on yleinen virhelähde.
- Sudenkuoppa: Ei-Inheritance-Ansa: Muista, että tietty scope ei peri vähemmän tietystä scopesta. Se palautuu *vain* globaaliin `imports`-ominaisuuteen. Jos teet virheenkorjausta määrittelyongelmassa, tunnista aina ensin yksittäinen voittava scope.
- Sudenkuoppa: Import Mapin Välimuisti: Import mappisi on koko moduuligraafisi lähtökohta. Jos päivität moduulin URL-osoitteen mapissa, sinun on varmistettava, että käyttäjät saavat uuden mapin. Yleinen strategia on olla tallentamatta pääasiallista `index.html`-tiedostoa voimakkaasti välimuistiin tai ladata import map dynaamisesti URL-osoitteesta, joka sisältää sisältöhashin, vaikka edellinen on yleisempi.
- Virheenkorjaus on Ystäväsi: Modernit selainten kehittäjätyökalut ovat erinomaisia moduuliongelmien virheenkorjaukseen. Verkko-välilehdeltä näet tarkalleen, mitä URL-osoitetta pyydettiin kullekin moduulille. Konsolissa määrittelyvirheet ilmoittavat selvästi, mikä määrittelijä epäonnistui määrittämään mistä importoivasta skriptistä.
Johtopäätös: Build-Vapaan Web-kehityksen Tulevaisuus
JavaScript Import Maps ja erityisesti niiden `scopes`-ominaisuus edustavat paradigman muutosta frontend-kehityksessä. Ne siirtävät merkittävän logiikan osan – moduulien määrittelyn – esikääntävältä build-vaiheelta suoraan selaimen natiiviin standardiin. Kyse ei ole vain mukavuudesta; kyse on joustavampien, dynaamisempien ja joustavampien web-sovellusten rakentamisesta.
Olemme nähneet, miten moduulien määrittelyhierarkia toimii: tarkin scope path voittaa aina ja se palautuu globaaliin `imports`-objektiin, ei yläscopeihin. Tämä yksinkertainen mutta tehokas sääntö mahdollistaa hienostuneiden sovellusarkkitehtuurien, kuten mikro-frontendien, luomisen ja mahdollistaa dynaamiset käyttäytymiset, kuten A/B-testauksen yllättävän helposti.
Web-alustan jatkaessa kypsymistä riippuvuus raskaista, monimutkaisista build-työkaluista kehityksessä vähenee. Import maps ovat kulmakivi tälle "build-vapaalle" tulevaisuudelle tarjoten yksinkertaisemman, nopeamman ja standardisoidumman tavan hallita riippuvuuksia. Hallitsemalla scope- ja määrittelyhierarkian käsitteet et vain opi uutta selainrajapintaa; varustat itsesi työkaluilla, joilla voit rakentaa seuraavan sukupolven sovelluksia globaalille webille.