IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur63/65-dars19 daqiqa
Mundarija (39)

Brauzerda JavaScript modullari: type="module", CORS va import maps

Qisqacha: Brauzer har import uchun serverdan alohida fayl so'raydi — shuning uchun modullar faqat http:// orqali ishlaydi (file:// da CORS xatosi), server esa ularni JavaScript MIME turi bilan berishi kerak. Yalang nomni (dayjs) brauzer o'zi tushunmaydi — uni <script type="importmap"> dagi import xaritasi manzilga bog'laydi. <link rel="modulepreload"> chuqur importlarni oldindan yuklab, sahifani tezlashtiradi, import.meta.url esa modulning o'z manzilini beradi.

Bu darsda

  • Brauzer modul grafigini qanday yuklashini va nega u server talab qilishini tushuntira olasiz.
  • file:// dagi CORS xatosini, MIME va 404 xatolarini o'qib, sababini topa olasiz.
  • Import xaritasi bilan yalang nomlarni lokal fayl yoki CDN manziliga bog'lay olasiz.
  • Import xaritasi va Vite kabi bundler qachon kerakligini solishtira olasiz.
  • modulepreload va import.meta.url ni ishlatasiz.
  • vazifalar ga modulepreload qo'shib, yuklanishni Network panelida tekshirasiz.

Oldin bilishingiz kerak: Modul semantikasi, Skriptlarni ulash: defer, async, module, Origin va Same-Origin Policy, Brauzer muhiti.

1. Nega bu kerak?

O'tgan ikki darsda vazifalar oltita modulga bo'lindi. Live Server'da hammasi ishlaydi. Lekin index.html ni ikki marta bosib ochsangiz — ro'yxat bo'sh, konsolda uzun qizil xato. Node'da esa .mjs fayllar papkadan to'g'ridan-to'g'ri ishlardi. Farq qayerda?

Node fayllarni diskdan o'qiydi. Brauzer esa har modulni tarmoq orqali so'raydi — xuddi rasm yoki CSS kabi. Bu bitta farqdan bir nechta yangi savol tug'iladi:

  • Fayl qayerdan kelyapti va brauzer unga ishonsa bo'ladimi? (CORS)
  • Server faylni "JavaScript" deb aytdimi? (MIME)
  • "dayjs" — qaysi serverdagi qaysi fayl? (import xaritasi)
  • Oltita fayl — oltita so'rov. Ular ketma-ket ketmaydimi? (modulepreload)

Skriptlarni ulash darsida bu savollarning ba'zilariga qisqa javob berib, "JavaScript qismida chuqurroq" degan edik. Birinchi dastur darsida esa "CORS nima ekanini keyinroq ko'ramiz" degan edik. Mana o'sha dars.

Hayotdan o'xshatish: choyxona ombori. Rustam aka ombor kalitini faqat o'z oshxonasi xodimlariga beradi. Ko'chadan kelgan "menga guruch kerak" degan notanish odamga ombor ochilmaydi — u kim ekanini va qayerdan kelganini aytishi kerak. Brauzer modullar uchun aynan shunday "kim so'rayapti?" tekshiruvini qiladi.

2. Brauzer modulni qanday yuklaydi

2.1 Har import — tarmoq so'rovi

<script type="module" src="assets/js/asosiy.js"> ni ko'rgan brauzer:

  1. asosiy.js ni yuklaydi va o'qiydi. Unda beshta import: vazifa.js, royxat.js, paket.js, holat.js, render.js.
  2. Beshalasini bir vaqtda so'raydi. Ular yuklangach, ularning importlarini o'qiydi: render.js → holat.js, vazifa.js va boshqalar. Bularning hammasi allaqachon so'ralgan — grafik to'liq.
  3. Hammasini bog'laydi va Modul semantikasi darsidagi tartibda bajaradi.

vazifalar ni ochib, DevTools → Network panelida JS fayllarni kuzating. Biz Chrome 154 da so'rovlar tartibini yozib oldik:

text
asosiy.js
vazifa.js  royxat.js  paket.js  holat.js  render.js

Ikki "to'lqin". Ikkinchi to'lqin birinchisi yuklanib, o'qilgandan keyin boshlanadi — brauzer holat.js haqida faqat asosiy.js ni o'qigach biladi. Bizning grafik sayoz: asosiy.js hamma modulni o'zi import qiladi. Agar royxat.js ni faqat holat.js import qilganda edi, uchinchi to'lqin ham bo'lardi — chuqur grafikda har pog'ona yana bitta kutish. Bu sxemada so'rov va javoblarning ketma-ketligi ko'rinadi:

sequenceDiagram
  participant B as Brauzer
  participant S as Server
  B->>S: asosiy.js
  S-->>B: javob — 5 ta import
  B->>S: vazifa, royxat, paket, holat, render
  S-->>B: javob — yangi import yo'q
  Note over B: bog'lash va bajarish

Lokal kompyuterda bu millisekundlar. Telefonda, sekin internetda har to'lqin yuzlab millisekund kutish. Bu muammoga «modulepreload» bo'limida qaytamiz.

2.2 Modul skriptning xulqi — eslatma

Skriptlarni ulash darsidan qisqa takror:

  • Kechiktiriladi — defer kabi: HTML o'qib bo'lingach bajariladi. Ichki <script type="module">...</script> ham.
  • Bir marta — bitta URL ikki teg bilan ulansa ham (Modul semantikasi dagi modul xaritasi).
  • async qo'shsa bo'ladi — unda modul grafigi tayyor bo'lishi bilan, HTML'ni kutmasdan bajariladi.
  • Qat'iy rejim, o'z muhiti — o'tgan darsdagi this === undefined.

Bu darsdagi yangi narsa — beshinchi xususiyat: modul CORS rejimida so'raladi. U nima ekanini ko'ramiz.

3. CORS va file://

3.1 Modul — "kim so'rayapti?" tekshiruvi bilan

Origin va Same-Origin Policy darsida originni o'rgangansiz: sxema + host + port (http://127.0.0.1:5500). Brauzer bir origin'dagi sahifaga boshqa origin'dan kelgan ma'lumotni o'qishni sukut bo'yicha taqiqlaydi. CORS (Cross-Origin Resource Sharing) — server "bu origin'ga ruxsat beraman" deb aytadigan qoida: javobga Access-Control-Allow-Origin sarlavhasini qo'shadi.

Oddiy <script src> 1995-yildan beri istalgan saytdan skript yuklay oladi — bu eski erkinlik saqlanib qolgan. Modul skript esa yangi, qat'iyroq qoida bilan keldi: boshqa origin'dagi modul faqat CORS ruxsati bilan yuklanadi. Tajriba: sahifa http://127.0.0.1:8081 da, ikkala skript esa http://localhost:8081 dan. Port bir xil, lekin host nomi boshqa — demak origin boshqa:

html
<script type="module"
        src="http://localhost:8081/assets/js/menyu.js"></script>
<script src="http://localhost:8081/assets/js/oddiy.js"></script>

Chrome konsolida:

text
Access to script at 'http://localhost:8081/assets/js/menyu.js' from origin 'http://127.0.0.1:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
oddiy.js bajarildi

Tarjimasi: "127.0.0.1:8081 origin'idan localhost:8081/.../menyu.js skriptiga kirish CORS qoidasi bilan bloklandi: so'ralgan resursda Access-Control-Allow-Origin sarlavhasi yo'q". Oddiy skript ishladi, modul — yo'q. Server bir xil, fayllar bir xil — farq faqat type="module" da.

CDN'lar (jsDelivr, unpkg — npm paketlarini tarqatadigan serverlar tarmog'i) bu sarlavhani yuboradi, shuning uchun ulardan modul olish mumkin — «Import xaritasi» bo'limida ko'ramiz.

3.2 file:// — origin'siz sahifa

Faylni ikki marta bosib ochganda manzil file:///C:/... bo'ladi. Bunday sahifaning origin'i — null: u hech qanday saytga tegishli emas. Brauzer null origin'dan CORS so'rovini umuman qilmaydi:

text
Access to script at 'file:///C:/Users/Aziz/kurs/vazifalar/assets/js/asosiy.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome, chrome-experimental-site-token-provider, chrome-extension, chrome-untrusted, data, http, https, isolated-app.

Tarjimasi: "null origin'idan asosiy.js skriptiga kirish bloklandi: boshqa origin so'rovlari faqat shu sxemalarda qo'llab-quvvatlanadi: http, https, data va Chrome'ning ichki sxemalari". Ro'yxatda file yo'q. Chrome 154 dagi to'liq matn shu — versiyaga qarab sxemalar ro'yxati biroz farq qiladi (Skriptlarni ulash darsidagi matnda bittasi kam edi).

Nega bunday qattiqqo'llik? file:// sahifasi kompyuteringizdagi boshqa fayllarni o'qiy olsa edi, yuklab olingan begona HTML fayl ochilishi bilan hujjatlaringizni "o'g'irlashi" mumkin edi. Brauzer xavfsizlik uchun file:// ni "hech kimga ishonilmaydigan joy" deb hisoblaydi.

Yechim — lokal server. Kursda Live Server (Birinchi sahifa): VS Code pastidagi "Go Live", manzil http://127.0.0.1:5500. Endi origin bor va hamma modul bitta origin'dan — CORS muammosi yo'q. Terminalni yaxshi ko'rsangiz, Node bilan ham bo'ladi: npx --yes http-server — papkani http://127.0.0.1:8080 da ochadi (npm va npx ni frontend asboblari qismida batafsil o'rganamiz).

Tekshirib ko'ring: Sahifa http://127.0.0.1:5500/index.html, modul esa http://127.0.0.1:5500/assets/js/asosiy.js. CORS sarlavhasi kerakmi?

Javob

Kerak emas. Sxema (http), host (127.0.0.1) va port (5500) bir xil — bitta origin. CORS faqat boshqa origin'dan yuklashda ishga tushadi. O'z saytingiz fayllari uchun hech narsa sozlash shart emas.

4. Server nimani to'g'ri berishi kerak

4.1 MIME turi

Server har faylni yuborganda "bu nima" ekanini Content-Type sarlavhasida aytadi: text/html, text/css, text/javascript. Bu — MIME turi (HTTP sarlavhalari darsida ko'rgansiz). Xuddi posilkadagi "ichida: kitob" yorlig'i kabi. Oddiy skript uchun brauzer bunga ko'z yumadi. Modul uchun — yo'q. Server .js ni text/plain ("oddiy matn") deb yuborsa:

text
Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain". Strict MIME type checking is enforced for module scripts per HTML spec.

Tarjimasi: "Modul skriptni yuklab bo'lmadi: JavaScript yoki Wasm (WebAssembly — brauzerda ishlaydigan boshqa, ikkilik formatdagi dastur) modul kutilgan edi, server esa text/plain MIME turi bilan javob berdi. Modul skriptlar uchun HTML spetsifikatsiyasi bo'yicha qat'iy MIME tekshiruvi qo'llanadi". Live Server, GitHub Pages va boshqa odatiy serverlar .js va .mjs ni to'g'ri yuboradi. Bu xatoni o'zingiz yozgan yoki noto'g'ri sozlangan serverda uchratasiz.

4.2 404 — butun grafik to'xtaydi

Modul fayli topilmasa, unga bog'liq hech bir modul bajarilmaydi. Grafik bo'lak-bo'lak ishlamaydi: yo hammasi, yo hech biri. Masalan, import ... from "./assets/js/menu.js" (harf tushib qolgan):

text
Failed to load resource: the server responded with a status of 404 (Not Found)

Sahifadagi ichki modul skriptning birinchi qatorida console.log bor edi — u ham chiqmadi: importlar to'liq yuklanmaguncha modulning bitta qatori ham bajarilmaydi. Network panelida qizil qator — menu.js, holati 404. Topish usuli: Network → qizil qator → so'ralgan manzilni haqiqiy fayl nomi bilan solishtiring.

4.3 Katta-kichik harf tuzog'i

Windows'da fayl tizimi katta-kichik harfni farqlamaydi. import ... from "./Holat.js" deb yozsangiz, lokal server holat.js faylini baribir beradi — biz sinadik, javob 200. GitHub Pages esa Linux serverda ishlaydi va harflarni farqlaydi: u yerda Holat.js — 404, sayt ishlamaydi. Lokal ishlab, Pages'da buzilgan modul ko'rsangiz — avval import manzillaridagi harflarni tekshiring. Eng oson himoya: fayl nomlari doim kichik harflarda.

5. Import xaritasi

5.1 Yalang nom muammosi

ESM import darsida Node yalang nomni (node:fs, dayjs) o'zi topishini ko'rdik. Brauzerda node_modules ham, paketlar ro'yxati ham yo'q:

text
Uncaught TypeError: Failed to resolve module specifier "bahor-yordam". Relative references must start with either "/", "./", or "../".

Tarjimasi: "bahor-yordam modul manzilini aniqlab bo'lmadi. Nisbiy manzillar /, ./ yoki ../ bilan boshlanishi kerak". Brauzer faqat manzilni tushunadi.

5.2 Nomni manzilga bog'lash

Import xaritasi (import map) — yalang nomlarni manzillarga bog'laydigan JSON. Skriptlarni ulash darsida uni "telefon kitobcha" deb atagan edik. Lokal kutubxonamiz bilan:

html
<script type="importmap">
  {
    "imports": {
      "bahor-yordam": "./lib/bahor-yordam/index.js"
    }
  }
</script>
<script type="module">
  import { somYoz } from "bahor-yordam";
  console.log(somYoz(35000));
</script>
text
35 000 so'm

"bahor-yordam" endi ./lib/bahor-yordam/index.js ga qaraydi. Manzil xarita turgan sahifaga nisbatan o'qiladi. Kodda esa qisqa, tushunarli nom qoladi — ertaga kutubxona boshqa joyga ko'chsa, faqat xarita o'zgaradi.

CDN bilan esa haqiqiy npm paketini to'g'ridan-to'g'ri ishlatish mumkin. Bu oynada dayjs jsDelivr'dan yuklanadi — internet kerak:

html
<p>Bron vaqti — dayjs bilan</p>
<script type="importmap">
  {
    "imports": {
      "dayjs": "https://cdn.jsdelivr.net/npm/dayjs@1.11.13/+esm"
    }
  }
</script>
<script type="module">
  import dayjs from "dayjs";

  const bron = dayjs("2026-10-04T19:30");
  console.log(bron.format("DD.MM.YYYY HH:mm"));
  console.log(bron.add(2, "hour").format("HH:mm"));
</script>

Konsolda:

text
04.10.2026 19:30
21:30

Malika soat 19:30 ga bron qildi, stol ikki soatga band. dayjs@1.11.13 — versiya qotirilgan: ertaga yangi versiya chiqsa ham, sizning sahifangiz shu versiyada qoladi. +esm — jsDelivr'ga "paketni ES modul ko'rinishida ber" degan buyruq.

5.3 Prefiks va qoidalar

Nom / bilan tugasa, u prefiks bo'ladi — butun papkani bog'laydi:

json
{
  "imports": {
    "bahor/": "./lib/bahor/"
  }
}

Endi import { somYoz } from "bahor/som.js" — ./lib/bahor/som.js, "bahor/vaqt.js" — ./lib/bahor/vaqt.js. Har fayl uchun alohida qator kerak emas.

Qoidalar:

  • Faqat JSON. Kalitlar qo'shtirnoqda, oxirgi vergul yo'q (JSON asoslari). { imports: ... } (qo'shtirnoqsiz kalit) — Uncaught SyntaxError: Failed to parse import map: invalid JSON.
  • Birinchi moduldan oldin. Chrome 133+ va Safari 18.4+ kech kelgan xaritani ham qabul qiladi (Chrome 154 da sinadik), Firefox esa yo'q. Hamma brauzer uchun qoida — xarita <head> da, modul skriptlardan yuqorida.
  • scopes — ma'lum papkadagi modullar uchun boshqa manzil (masalan, eski kod dayjs ning eski versiyasini olsin). Kam kerak bo'ladi, hujjatda bor.

Baseline ma'lumotiga ko'ra import xaritalari 2023-yil martdan hamma asosiy brauzerlarda bor, 2025-yil sentyabrdan esa "keng qo'llab-quvvatlanadi" (eski brauzerlar ham yangilanib ulgurgan).

5.4 Import xaritasi yoki Vite?

Skriptlarni ulash darsida va'da qilgan solishtirish. Vite — zamonaviy frontend yig'ish asbobi (bundler + ishlab chiqish serveri; 16-qismda o'rganamiz, hozir bilish shart emas):

Import xaritasi Vite (bundler)
Qurish bosqichi yo'q — fayllar o'zi npm run build
So'rovlar soni har modul — alohida bir necha yig'ilgan fayl
Keraksiz kodni tashlash yo'q bor (tree-shaking)
npm paketlari CDN orqali, qo'lda node_modules dan avtomatik

Qachon nima: kichik sayt, prototip, ta'lim loyihasi, bir nechta modul — import xaritasi va oddiy modullar yetarli, hech narsa o'rnatmaysiz. vazifalar hozircha shunday. Ko'p kutubxonali, yuzlab modulli ilova — bundler: u so'rovlarni kamaytiradi, kodni siqadi va tree-shaking qiladi (Modul semantikasi). vazifalar 16-qismda Vite'ga o'tadi.

Tekshirib ko'ring: Import xaritasida "bahor/": "./lib/bahor/" bor. import x from "bahor" (oxirida / siz) ishlaydimi?

Javob

Yo'q. "bahor/" faqat bahor/ bilan boshlanadigan nomlarni bog'laydi. "bahor" — boshqa kalit, xaritada yo'q, shuning uchun Failed to resolve module specifier "bahor". Kerak bo'lsa, alohida qator qo'shiladi: "bahor": "./lib/bahor/index.js".

6. modulepreload: to'lqinlarni qisqartirish

6.1 Oldindan aytib qo'yish

«Har import — tarmoq so'rovi» bo'limidagi ikki to'lqinni eslang. Brauzer beshta modul haqida asosiy.js ni o'qigach biladi. Lekin biz buni oldindan bilamiz! HTML'da aytib qo'yamiz:

html
<link rel="modulepreload" href="assets/js/holat.js">
<link rel="modulepreload" href="assets/js/render.js">
<link rel="modulepreload" href="assets/js/royxat.js">
<link rel="modulepreload" href="assets/js/paket.js">
<link rel="modulepreload" href="assets/js/vazifa.js">
<script type="module" src="assets/js/asosiy.js"></script>

<link rel="modulepreload"> — "bu modul kerak bo'ladi: hozirdan yuklab, o'qib qo'y". Brauzer HTML'ni o'qiyotib oltala faylni bir to'lqinda so'raydi. asosiy.js importlariga yetganda, ular allaqachon tayyor.

Oddiy rel="preload" dan farqi: modulepreload faylni modul sifatida (CORS rejimida) yuklaydi va o'qib ham qo'yadi, keyin uni modul xaritasiga yozadi. Oddiy preload modul uchun noto'g'ri rejimda yuklab, faylni ikki marta so'ratib qo'yishi mumkin.

6.2 O'lchov

Farqni o'lchadik. vazifalar ni lokal Node serverda ochdik va har .js javobiga sun'iy 150 ms kechikish qo'shdik — sekin mobil internetni taqlid qilish uchun. Har variant 7 marta ochildi, Chrome 154 (headless), kesh o'chirilgan; ustunlarda — ro'yxat chizilguncha o'tgan vaqtning medianasi:

vazifalar: sahifa ochilishidan ro'yxat chizilguncha
  • Lokal, preload'siz192 ms
  • Lokal, modulepreload162 ms
  • Sekin tarmoq, preload'siz2 to'lqin449 ms
  • Sekin tarmoq, modulepreload1 to'lqin277 ms

Manba: O'lchangan: Chrome 154 headless, lokal Node server, sekin tarmoqda har .js javobiga 150 ms kechikish, 7 o'lchov medianasi (2026-10-05)

Lokal kompyuterda farq kichik — 30 ms. Sekin tarmoqda esa sahifa qariyb 170 ms tezroq tayyor bo'ldi: ikki to'lqin o'rniga bitta — bitta kutish kamaydi. Modul grafigi qancha chuqur bo'lsa, foyda shuncha katta: har pog'ona yana bitta to'lqin.

6.3 Narxi

  • Ro'yxatni qo'lda yuritish. Yangi modul qo'shilsa yoki nomi o'zgarsa, index.html ni ham yangilash kerak. Unutsangiz, xato chiqmaydi — shunchaki yana sekinlashadi. Ortiqcha modulepreload esa keraksiz faylni yuklaydi.
  • Bundler buni o'zi qiladi. Vite yig'ilgan sahifaga modulepreload qatorlarini avtomatik yozadi. Qo'lda yozish — bundler'siz loyihalar uchun.

Baseline ma'lumotiga ko'ra modulepreload 2023-yil sentyabrdan hamma asosiy brauzerlarda bor (Firefox 115 dan), 2026-yil martdan "keng qo'llab-quvvatlanadi".

Tekshirib ko'ring: vazifalar ga yangi teglar.js moduli qo'shildi, uni royxat.js import qiladi. index.html dagi modulepreload ro'yxatini yangilamasangiz nima bo'ladi?

Javob

Hech qanday xato chiqmaydi — ilova ishlayveradi. Faqat teglar.js kechroq, alohida to'lqinda yuklanadi: brauzer u haqida royxat.js ni o'qigandan keyingina biladi. modulepreload — tezlik uchun maslahat, majburiy ro'yxat emas. Shuning uchun uni unutish oson va README'ga eslatma yozish foydali.

7. import.meta: modulning o'zi haqida

7.1 import.meta.url

Har modulda import.meta degan maxsus obyekt bor: modul haqidagi ma'lumot. Eng muhimi — import.meta.url, modulning o'z to'liq manzili. Sahifa http://127.0.0.1:8081/prefiks.html, modul — assets/js/meta.js:

js
console.log(import.meta.url);
console.log(new URL("../images/osh.webp", import.meta.url).href);
console.log(document.baseURI);
text
http://127.0.0.1:8081/assets/js/meta.js
http://127.0.0.1:8081/assets/images/osh.webp
http://127.0.0.1:8081/prefiks.html

Nega kerak? Rasm yoki boshqa faylning nisbiy yo'li img.src = "../images/osh.webp" kabi yozilsa, u sahifaga nisbatan o'qiladi — sahifa boshqa papkada bo'lsa, yo'l buziladi. Ikkinchi qatordagi usul esa yo'lni modulga nisbatan hisoblaydi: modul qaysi sahifaga ulanmasin, rasm topiladi. Bu yerda URL — manzilni bo'laklarga ajratadigan va qo'shadigan o'rnatilgan klass; ikkinchi argument — "nimaga nisbatan".

Node'da ham import.meta.url bor (file:///...), Node 20.11 dan esa qulay qo'shimchalar: import.meta.dirname (papka) va import.meta.filename (fayl yo'li) — CommonJS va ESM darsida ular eski __dirname ning o'rnini bosishini ko'ramiz.

7.2 import.meta.resolve

import.meta.resolve(nom) — manzilni yuklamasdan hisoblaydi. Import xaritasi bilan qaysi faylga borishini tekshirish uchun qulay:

js
console.log(import.meta.resolve("bahor/som.js"));
text
http://127.0.0.1:8081/lib/bahor/som.js

Xarita noto'g'ri bo'lsa, aynan shu qator sizga haqiqiy manzilni ko'rsatadi. Barcha asosiy brauzerlarda 2023-yildan (Safari 16.4) bor.

Tekshirib ko'ring: assets/js/menyu-rasm.js modulida import.meta.url nimaga teng bo'ladi — modul index.html dan ulanganda va menyu/index.html dan ulanganda?

Javob

Ikkalasida ham bir xil: .../assets/js/menyu-rasm.js. import.meta.url — modulning o'z manzili, uni qaysi sahifa ulagani ahamiyatsiz. Sahifaning manzili esa document.baseURI da — u ikki holatda har xil. Shuning uchun modulga nisbatan yo'l new URL(..., import.meta.url) bilan hisoblanadi.

8. nomodule haqida bir eslatma

Skriptlarni ulash darsida <script nomodule> ni ko'rgan edik — modulni tushunmaydigan eski brauzerlar uchun zaxira. Modullar barcha asosiy brauzerlarda 2018-yildan bor, shuning uchun yangi loyihada nomodule deyarli kerak emas. Lekin bitta foydali saboq qoladi: JavaScript umuman ishlamasa (o'chirilgan, yuklanmagan), sahifa bo'sh qolmasligi kerak. Buning uchun <noscript> bor (Skriptlarni ulash): skript ishlamasa, foydalanuvchi hech bo'lmasa tushuntirish ko'radi.

9. Ko'p uchraydigan xatolar

9.1 Faylni ikki marta bosib ochish

...from origin 'null' has been blocked by CORS policy — sahifa file:// da. Tuzatish: Live Server.

9.2 Noto'g'ri sozlangan server

Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain" (yoki "text/html"). Ikkinchi holat ko'pincha 404 sahifasi: server "topilmadi" HTML sahifasini qaytargan. Tuzatish: Network panelida faylning holati va Content-Type ini tekshiring.

9.3 Mutlaq yo'l GitHub Pages'da

import ... from "/assets/js/holat.js" — / bilan boshlangan manzil saytning ildiziga nisbatan. Loyiha sayti login.github.io/vazifalar/ da turadi va /assets/... — login.github.io/assets/... bo'lib qoladi: 404. Bu Birinchi deploy darsidagi href tuzog'ining o'zi. Tuzatish: modullar orasida — faqat ./ va ../.

9.4 Harf o'lchami

Lokal ishlaydi, Pages'da 404 — «Katta-kichik harf tuzog'i» bo'limi.

9.5 Eski kesh

Modulni o'zgartirdingiz, brauzer eskisini ishlatyapti. Modullar ham keshlanadi. Tuzatish: DevTools ochiq holda Network → "Disable cache" yoki Ctrl+Shift+R.

10. Mashqlar

1-mashq (oson): Xatoni toping

Uch holat. Har birida konsolda qanday xato chiqadi?

  1. index.html file:///D:/kurs/bahor/index.html da ochildi, unda <script type="module" src="app.js">.
  2. <script src="app.js"> — type yo'q, app.js ning birinchi qatori import { somYoz } from "./yordam.js";.
  3. <script type="module">import dayjs from "dayjs";</script> — import xaritasi yo'q.
Yechim
  1. ...from origin 'null' has been blocked by CORS policy... — file:// dan modul yuklanmaydi.
  2. Uncaught SyntaxError: Cannot use import statement outside a module — "modul tashqarisida import ko'rsatmasini ishlatib bo'lmaydi". Oddiy skript import ni bilmaydi; type="module" kerak.
  3. Uncaught TypeError: Failed to resolve module specifier "dayjs"... — yalang nom uchun import xaritasi kerak.

Biz uchala holatni Chrome 154 da sinab, xabarlarni oldik. Ikkinchi xabar ko'pincha internetdan ko'chirilgan kodni oddiy <script> ga qo'yganda chiqadi.

2-mashq (o'rta): «Bahor» kutubxonasi prefiks bilan

Papka tuzilmasi:

text
bahor-sayt/
├── index.html
├── assets/js/asosiy.js
└── lib/bahor/
    ├── som.js
    └── vaqt.js

som.js — somYoz(son) ("28 000 so'm" qaytaradi), vaqt.js — ochiqmi(soat) (7:00–23:00). asosiy.js ularni bahor/som.js va bahor/vaqt.js nomlari bilan import qilsin va somYoz(28000) hamda ochiqmi(8) ni chiqarsin. index.html ni yozing.

Yechim
html
<!DOCTYPE html>
<html lang="uz">
<head>
  <meta charset="utf-8">
  <title>Bahor</title>
  <script type="importmap">
    {
      "imports": {
        "bahor/": "./lib/bahor/"
      }
    }
  </script>
  <script type="module" src="assets/js/asosiy.js"></script>
</head>
<body>
  <h1>Bahor</h1>
</body>
</html>
js
// assets/js/asosiy.js
import { somYoz } from "bahor/som.js";
import { ochiqmi } from "bahor/vaqt.js";

console.log(somYoz(28000), ochiqmi(8));

Live Server'da konsolda:

text
28 000 so'm true

Xaritadagi ./lib/bahor/ — index.html ga nisbatan. asosiy.js assets/js/ ichida tursa ham, "bahor/som.js" to'g'ri faylga boradi: yalang nom xarita orqali, xarita esa sahifaga nisbatan hal qilinadi.

3-mashq (qiyin): Modulga nisbatan rasm

assets/js/menyu-rasm.js moduli <img> yaratib, sahifaga qo'shsin. Rasm — assets/images/osh.webp. Talab: modul istalgan sahifadan (index.html dan ham, menyu/index.html dan ham) ulanganda rasm to'g'ri topilsin. alt — "Osh". Ishora: «import.meta.url» bo'limidagi new URL(...); element yaratish — Element yaratish.

Yechim
js
// assets/js/menyu-rasm.js
const rasm = document.createElement("img");
rasm.src = new URL("../images/osh.webp", import.meta.url).href;
rasm.alt = "Osh";
document.querySelector("main").append(rasm);

menyu/index.html da modul <script type="module" src="../assets/js/menyu-rasm.js"> bilan ulanadi. import.meta.url ikkala sahifada ham bir xil — .../assets/js/menyu-rasm.js, demak rasm manzili ham doim .../assets/images/osh.webp. Agar rasm.src = "assets/images/osh.webp" yozilsa, menyu/ sahifasida u menyu/assets/images/osh.webp ni qidirib, 404 olardi.

4-mashq: Vazifalar qadami — modulepreload va brauzer tekshiruvi

refactor/modullar branch'ida davom etamiz.

  1. index.html ga beshta modul uchun modulepreload qo'shing.
  2. DevTools → Network (JS filtri, "Disable cache") da yuklanishni oldin va keyin solishtiring. "Slow 4G" tezligini tanlab ham ko'ring.
  3. Pages'ga chiqishdan oldingi tekshiruv: barcha import manzillari nisbiy va kichik harflardami?
Yechim

index.html ning <head> i:

html
<link rel="stylesheet" href="assets/css/vazifalar.css">
<link rel="modulepreload" href="assets/js/holat.js">
<link rel="modulepreload" href="assets/js/render.js">
<link rel="modulepreload" href="assets/js/royxat.js">
<link rel="modulepreload" href="assets/js/paket.js">
<link rel="modulepreload" href="assets/js/vazifa.js">
<script type="module" src="assets/js/asosiy.js"></script>

asosiy.js uchun modulepreload shart emas — uni <script> ning o'zi so'raydi. Yo'llar index.html ga nisbatan, nisbiy — Pages'da ham ishlaydi.

Network'da. modulepreload siz: avval asosiy.js, u kelgach qolgan beshtasi — Waterfall ustunida ikki zina. Bilan: oltala fayl deyarli bir vaqtda boshlanadi. "Slow 4G" da farq aniq ko'rinadi — biz o'lchagan 449 ms va 277 ms shu farqning raqami.

Tekshiruv. Barcha from lar ./ bilan boshlanadi va fayl nomlari kichik harfda: ./holat.js, ./render.js, ./royxat.js, ./paket.js, ./vazifa.js. VS Code'da Ctrl+Shift+F bilan from " ni qidirsangiz, hamma importlar bitta ro'yxatda ko'rinadi.

Ilovani Live Server'da qayta sinang — xulq o'zgarmagan bo'lishi kerak: biz 22 holatli ssenariyni modulepreload li versiyada ham o'tkazdik, natija bir xil.

bash
git add index.html
git commit -m "Vazifalar: modullarni oldindan yuklashni qo'sh"

Yangi modul qo'shsangiz — shu ro'yxatni ham yangilashni unutmang. Buni README.md dagi "Modullar" bo'limiga bir qator qilib yozib qo'ying.

11. Real ishda

  • Bundler'siz sahifalar. Hujjat saytlari, kichik vidjetlar, admin panellar, ta'lim loyihalari import xaritasi + CDN bilan yashaydi: hech narsa o'rnatilmaydi, index.html ochiladi va ishlaydi. Deno va ba'zi freymvorklar (masalan Rails) ham import xaritasiga tayanadi.
  • CDN modullari. jsDelivr, esm.sh, unpkg npm paketlarini ES modul ko'rinishida beradi. Versiyani doim qotiring (@1.11.13) — aks holda bir kun kelib sayt yangi versiya bilan buzilishi mumkin.
  • Production tezligi. Katta sayt jamoalari modul grafigining chuqurligini va modulepreload ni Lighthouse va Network paneli bilan kuzatadi. Vite buni avtomatik qiladi (16-qism), sayt tezligini esa 13-qismda o'lchaymiz.
  • Intervyu. "Nega ES modul file:// da ishlamaydi?", "Import map nima?", "modulepreload nima uchun?" — frontend intervyularida uchraydi. Javob kaliti: modul CORS rejimida so'raladi; yalang nom → manzil; to'lqinlarni bittaga qisqartirish.

Xulosa

  • Brauzer har import uchun serverdan so'raydi; grafik "to'lqin"lar bilan yuklanadi, so'ng bog'lanadi va bajariladi.
  • Modul CORS rejimida so'raladi: boshqa origin — faqat Access-Control-Allow-Origin bilan; file:// (origin null) — umuman yo'q. Yechim — lokal server.
  • Server modulni JavaScript MIME turi bilan berishi kerak; bitta 404 butun grafikni to'xtatadi; Pages harf o'lchamini farqlaydi.
  • Import xaritasi yalang nomni manzilga bog'laydi ("dayjs": "https://...", prefiks "bahor/"); u modullardan oldin, toza JSON bo'lsin.
  • modulepreload chuqur importlarni oldindan yuklaydi — sekin tarmoqda sezilarli tezlik.
  • import.meta.url — modulning o'z manzili; new URL(yol, import.meta.url) — modulga nisbatan yo'l.

Keyingi dars: CommonJS va ESM bilan o'zaro ishlash — Node'ning eski modul tizimi require va module.exports ni, "type": "module" ni va ikki tizim bir-biri bilan qanday gaplashishini o'rganib, vazifalar modullarini main ga qo'shamiz.

Manbalar

  • MDN: "JavaScript modules" — "Applying the module to your HTML", "Importing modules using import maps", "import.meta", <link rel="modulepreload"> — developer.mozilla.org
  • HTML Living Standard: "Import maps", "Fetching scripts" — html.spec.whatwg.org
  • web-features: js-modules, import-maps (Baseline 2023-03, keng 2025-09), modulepreload (Baseline 2023-09, keng 2026-03)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Brauzerda JavaScript modullari: type="module", CORS va import maps — IlmHamroh