IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq16/32-dars23 daqiqa
Mundarija (37)

JavaScript dinamik import() va top-level await: modulni kerak bo'lganda yuklash

Qisqacha: Dinamik import("./modul.js") — modulni kod ishlayotgan paytda, kerak bo'lganda yuklaydi va modul obyekti bilan bajariladigan Promise qaytaradi: const { eksportMatni } = await import("./paket.js"). Sahifa ochilishi tezlashadi: kam ishlatiladigan kod faqat kerak bo'lganda keladi. Top-level await — modulning eng yuqori qatorida, funksiyasiz await yozish; qulay, lekin shu modulni import qilganlarning hammasini kuttiradi. JSON fayl esa with { type: "json" } belgisi bilan import qilinadi.

Bu darsda

  • import() ni yoza olasiz va u statik import dan nimasi bilan farq qilishini tushuntira olasiz.
  • Modul obyektidan nomli va default eksportlarni ola olasiz (.default tuzog'i bilan).
  • Modulni tugma bosilganda yoki shartga qarab yuklay olasiz va yuklash xatosini to'g'ri ushlaysiz.
  • Top-level await qayerda ishlashini va uning narxini bilasiz.
  • JSON faylni import attributes bilan import qila olasiz.
  • vazifalar ilovasida paket.js ni faqat «JSON qilib chiqarish» bosilganda yuklaysiz.

Oldin bilishingiz kerak: ESM import, Brauzerda ESM va import maps, CommonJS va ESM bilan o'zaro ishlash, async / await, Ushlanmagan xatolar.

1. Nega bu kerak?

ESM import darsida importlar faylning boshida yozilishini va kod ishga tushishidan oldin ulanishini ko'rgan edik. Bu ishonchli: brauzer sahifa ochilishi bilan butun modul grafigini biladi va hammasini yuklaydi.

Lekin "hammasini" — ba'zan ortiqcha. vazifalar ilovasini eslang: oltita modul, ulardan biri — paket.js (eksport va import). Foydalanuvchilarning ko'pi vazifa qo'shadi, belgilaydi, o'chiradi — «JSON qilib chiqarish» tugmasini esa kamdan-kam bosadi. Baribir paket.js har safar, sahifa ochilishi bilan yuklanadi.

Kichik ilovada bu sezilmaydi. Katta ilovada esa farq katta. «Bahor» saytida chekni PDF qilib chiqaradigan kutubxona yuzlab kilobayt bo'lishi mumkin. Uni menyuni ochgan har mehmon yuklasa, sahifa sekin ochiladi. Aslida uni faqat «Chekni yuklab olish» ni bosgan bir necha kishi ishlatadi.

Shuning uchun ESM import darsida va'da bergan edik: modulni shartga qarab yoki kerak bo'lganda yuklash uchun boshqa vosita bor. U Promise qaytaradi — endi Promise'ni bilasiz, navbat keldi.

2. import() asoslari

2.1 Funksiyaga o'xshagan kalit so'z

Dinamik import — import(manzil). U modulni yuklaydi, bajaradi va modul obyekti bilan bajariladigan Promise qaytaradi:

js
const util = await import("node:util");
console.log(util.format("%s — %d so'm", "Osh", 35000));

const vada = import("node:util");
console.log(vada instanceof Promise);

Konsolda:

text
Osh — 35000 so'm
true

Qatorma-qator:

  • await import("node:util") — Node'ning tayyor util modulini yuklaydi. util.format matnga qiymat qo'yadi: %s o'rniga satr, %d o'rniga son.
  • import(...) ning o'zi — Promise. Ikkinchi qism buni instanceof bilan ko'rsatdi.

Bu yerda await funksiyadan tashqarida yozilgan — buni top-level await deyishadi. U faqat modullarda ishlaydi va uni shu darsning «Top-level await» bo'limida ochamiz. Blok ostida «Ishga tushir» yo'q, chunki node:util faqat Node'da bor. Bunday bloklarni va bir nechta fayldan iborat misollarni kompyuteringizda, .mjs faylda sinang.

import(...) funksiyaga o'xshaydi, lekin funksiya emas — maxsus sintaksis. Uni o'zgaruvchiga yozib yoki import.call(...) qilib bo'lmaydi.

2.2 Statik va dinamik importni taqqoslash

Statik import ... from Dinamik import()
Qayerda faylning eng yuqori darajasida istalgan joyda: funksiya, if, tinglovchi
Qachon yuklanadi kod ishga tushishidan oldin shu qator bajarilganda
Manzil faqat tayyor satr istalgan ifoda: `./tillar/${til}.mjs`
Natija nomlar faylga ulanadi Promise → modul obyekti
Oddiy skript va CommonJS'da yo'q bor

Oxirgi qator — muhim. Statik import faqat ESM modulda ishlaydi. import() esa oddiy <script> da ham, CommonJS (.cjs) faylda ham ishlaydi. CommonJS va ESM darsida aytilgan "import() ishlating" maslahati shundan.

2.3 Modul obyekti

import() qaytaradigan obyekt — modul nomlar maydoni (module namespace object). Unda modulning har nomli eksporti — xususiyat, default eksporti esa default nomli xususiyat. ESM import darsidagi menyu.mjs ni dinamik yuklaymiz:

js
// menyu.mjs
console.log("menyu.mjs bajarildi");

export const narxlar = { osh: 35000, manti: 30000 };

export function narxniTop(taom) {
  return narxlar[taom] ?? 0;
}

export default function menyuniChiqar() {
  console.log("Osh, Manti");
}
js
// asosiy.mjs
console.log("asosiy.mjs boshlandi");
const menyu = await import("./menyu.mjs");
console.log(Object.keys(menyu));
menyu.default();
console.log(menyu.narxniTop("osh"));

const { default: chiqar, narxniTop } = await import("./menyu.mjs");
console.log(chiqar === menyu.default, narxniTop("manti"));

node asosiy.mjs bilan ishga tushirdik:

text
asosiy.mjs boshlandi
menyu.mjs bajarildi
[ 'default', 'narxlar', 'narxniTop' ]
Osh, Manti
35000
true 30000

To'rt narsaga qarang:

  • Tartib. asosiy.mjs boshlandi birinchi chiqdi. Statik importda esa menyu.mjs doim birinchi bajarilardi — importlar kod ishga tushishidan oldin ulanadi (Modul semantikasi). Dinamik import — shu qatorga yetib kelgandagina.
  • default — oddiy xususiyat. Default eksportni chaqirish: menyu.default().
  • Destructuring. const { default: chiqar, narxniTop } = ... — kerakli nomlarni darhol olish. default — zaxiralangan so'z, uni o'zgaruvchi nomi qilib bo'lmaydi, shuning uchun default: chiqar bilan qayta nomlaymiz (Obyekt destructuring).
  • Bir marta bajariladi. Ikkinchi import("./menyu.mjs") da menyu.mjs bajarildi qayta chiqmadi. Modul keshda: har import() o'sha bitta modul obyektini qaytaradi.

Modul obyektini o'zgartirib bo'lmaydi — undagi nomlar faqat o'qish uchun:

js
const menyu = await import("./menyu.mjs");
menyu.narxlar = {};
text
TypeError: Cannot assign to read only property 'narxlar' of object '[object Module]'

"Modul obyektining faqat o'qiladigan narxlar xususiyatiga qiymat berib bo'lmaydi". Bu — Modul semantikasi darsidagi "import — faqat o'qish uchun" qoidasining o'zi. Bu xato faqat qat'iy rejimda chiqadi — modullar esa doim qat'iy rejimda ishlaydi.

Tekshirib ko'ring: const m = await import("./menyu.mjs"); m(); nima beradi?

Javob

TypeError: m is not a function. m — funksiya emas, modul obyekti. Default funksiya uning ichida: m.default(). Bu import() dagi eng ko'p uchraydigan xato — statik import menyuniChiqar from da default to'g'ridan-to'g'ri kelardi, dinamikda esa .default orqali.

3. Brauzerda: bosilganda yuklash

3.1 Jonli misol

«Bahor» saytida buyurtma qabul qilinganda ekranda konfetti "otilsin". Konfetti kutubxonasi faqat shu lahzada kerak — sahifa ochilganda uni yuklash shart emas. CDN'dagi canvas-confetti (1.9.4 versiyasi) kutubxonasini tugma bosilganda yuklaymiz:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  button { font: inherit; padding: 0.4rem 0.8rem; }
  canvas { display: block; width: 100%; height: 160px; }
</style>
<button id="buyurtma" type="button">Buyurtma berish</button>
<p id="holat" role="status"></p>
<canvas id="bayram"></canvas>
<script type="module">
  const KONFETTI =
    "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/+esm";
  const tugma = document.getElementById("buyurtma");
  const holat = document.getElementById("holat");

  tugma.addEventListener("click", async () => {
    holat.textContent = "Buyurtma qabul qilindi!";
    const modul = await import(KONFETTI);
    console.log("Modul keldi:", Object.keys(modul));
    const otish = modul.default.create(
      document.getElementById("bayram"),
      { resize: true, useWorker: false },
    );
    otish({ particleCount: 80, disableForReducedMotion: true });
  });

  console.log("Sahifa tayyor: konfetti hali yuklanmagan");
</script>

Konsolda:

text
Sahifa tayyor: konfetti hali yuklanmagan

Endi «Buyurtma berish» ni bosing. Konsolga yangi qator qo'shiladi va konfetti otiladi (tizimingizda "harakatni kamaytirish" yoqilgan bo'lsa — otilmaydi: disableForReducedMotion). Birinchi bosishdan keyin konsolda:

text
Sahifa tayyor: konfetti hali yuklanmagan
Modul keldi: (2) ['create', 'default']

Ikkinchi bosishda kutubxona qayta yuklanmaydi — modul keshda, import() darhol tayyor obyektni beradi. Konsoldagi (2) ['create', 'default'] — Chrome'ning massiv ko'rinishi: ikki element, kutubxonaning ikki eksporti. Node buni [ 'create', 'default' ] deb yozardi — ma'nosi bir xil. useWorker: false — kutubxona odatda alohida oqim (Web Worker) ochadi, saytdagi natija oynasi esa xavfsizlik uchun bunga ruxsat bermaydi. O'z sahifangizda bu sozlama shart emas.

+esm — jsDelivr CDN'ning xizmati: npm paketini brauzer to'g'ridan-to'g'ri import qila oladigan ES modulga aylantirib beradi. Paket va CDN'lar haqida — npm'ga birinchi qadam va 16-qismda.

3.2 Tarmoqda nima bo'ladi

Vaqt bo'yicha: sahifa ochilganda — faqat sahifaning o'z kodi. Bosilganda — kutubxona.

sequenceDiagram
  participant B as Brauzer
  participant S as Server / CDN
  B->>S: index.html, asosiy.js
  S-->>B: sahifa tayyor
  Note over B: Foydalanuvchi o'qiydi...
  B->>B: «Buyurtma berish» bosildi
  B->>S: import("…canvas-confetti…")
  S-->>B: modul
  Note over B: Ikkinchi bosish — keshdan, so'rovsiz

DevTools'ning Network panelida buni o'z ko'zingiz bilan ko'rasiz: sahifa ochilganda ro'yxatda konfetti fayli yo'q, bosgandan keyin paydo bo'ladi (DevTools Network).

3.3 Yuklash xatosi

Tarmoq uzilsa yoki fayl topilmasa, import() Promise'i rad etiladi. Chrome'dagi xabar:

text
TypeError: Failed to fetch dynamically imported module: https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/yoq.mjs

"Dinamik import qilinayotgan modulni olib bo'lmadi". Sababini (404 yoki internet yo'q) xabar aytmaydi — u Network panelida yoki konsoldagi oldingi Failed to load resource qatorida. Bu xato ham oddiy rad etilish: uni try/catch va await bilan ushlaysiz. Ushlamasangiz — konsolda Uncaught (in promise) TypeError: ... (Ushlanmagan xatolar).

Bitta nozik joy bor — uni Chrome 154 da sinab ko'rdik. Agar modul bir marta yuklanmasa, keyingi import() uni qayta so'ramaydi: brauzer muvaffaqiyatsizlikni ham eslab qoladi va darhol o'sha xatoni qaytaradi. Internet qaytgandan keyin ham tugmani qayta bosish yordam bermaydi — faqat sahifani yangilash. Shuning uchun foydalanuvchiga "qayta bosing" emas, "sahifani yangilang" deb yozing.

Tekshirib ko'ring: Konfetti misolida import() ni tinglovchi ichidan olib, faylning boshiga statik import confetti from "https://..." qilib qo'ysak, nima o'zgaradi?

Javob

Kutubxona sahifa ochilishi bilan, hech kim bosmasa ham yuklanadi. Ishlash bir xil, lekin har mehmon ortiqcha faylni yuklaydi. Yana bir farq: CDN ishlamay qolsa, statik importda butun modul ishlamaydi — tugma ham, sahifaning qolgan kodi ham. Dinamik importda faqat konfetti bo'lmaydi.

4. Shartga qarab yuklash

4.1 Manzil — o'zgaruvchidan

ESM import darsida import { matnlar } from "./" + til + ".mjs" xato bergan edi: statik importda manzil faqat tayyor satr. import() da esa istalgan ifoda bo'ladi. «Bahor» sayti ikki tilda: o'zbekcha va inglizcha. Har til matnlari alohida faylda, faqat tanlangan til yuklanadi:

js
// tillar/uz.mjs
export const salom = "Xush kelibsiz!";
export const menyu = "Menyu";
js
// tillar/en.mjs
export const salom = "Welcome!";
export const menyu = "Menu";
js
// til.mjs — tanlangan tildagi matnlarni yuklaydi
const TILLAR = ["uz", "en"];
const til = process.argv[2] ?? "uz";

if (!TILLAR.includes(til)) {
  console.log(`«${til}» tili yo'q, o'zbekcha ochamiz`);
}
const tanlangan = TILLAR.includes(til) ? til : "uz";
const matnlar = await import(`./tillar/${tanlangan}.mjs`);
console.log(matnlar.salom, "|", matnlar.menyu);

Terminalda uch marta ishga tushirdik — process.argv[2] — buyruqdagi birinchi qo'shimcha so'z (process obyekti):

text
$ node til.mjs
Xush kelibsiz! | Menyu
$ node til.mjs en
Welcome! | Menu
$ node til.mjs fr
«fr» tili yo'q, o'zbekcha ochamiz
Xush kelibsiz! | Menyu

TILLAR ro'yxati — oq ro'yxat (JSON chuqur darsidagi g'oya): import qilinadigan fayl faqat ma'lum to'plamdan. Ro'yxatsiz fr uchun Node xato berardi:

text
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '…/tillar/fr.mjs' imported from …/til.mjs

"til.mjs dan import qilingan tillar/fr.mjs modulini topib bo'lmadi". Oq ro'yxatning yana bir, muhimroq sababi bor — keyingi bo'limda.

4.2 Code splitting g'oyasi

Haqiqiy loyihalarda kodni bundler (ko'p faylni brauzer uchun yig'adigan vosita, masalan Vite — 16-qismda) yig'adi. U import() ni ko'rganda o'sha modulni alohida faylga — bo'lak (chunk) ga — ajratadi. Asosiy fayl kichrayadi, bo'lak esa faqat import() bajarilganda yuklanadi. Buni code splitting (kodni bo'lish) deyishadi. Sahifalar bo'yicha bo'lish (har sahifa — alohida bo'lak) va og'ir komponentlarni kechiktirish (React'da lazy) — hammasi import() ustiga qurilgan. Chuqur — Code splitting va lazy darslarida; hozir bilish shart emas.

5. Top-level await

5.1 await funksiyasiz

Shu paytgacha await ni faqat async funksiya ichida yozdik. ES modullarda esa uni faylning eng yuqori darajasida ham yozish mumkin — bu top-level await (yuqori darajadagi await):

js
function serverdanOl() {
  return new Promise((resolve) => {
    setTimeout(resolve, 200, { ochilish: 7, yopilish: 23 });
  });
}

console.log("Sozlamalar so'raldi");
const { ochilish, yopilish } = await serverdanOl();
console.log(`Ish vaqti: ${ochilish}:00–${yopilish}:00`);

Konsolda:

text
Sozlamalar so'raldi
Ish vaqti: 7:00–23:00

async function boshla() { ... } boshla(); o'rami kerak bo'lmadi. Kirish faylida (ilova boshlanadigan joyda) bir martalik sozlash uchun bu juda qulay.

5.2 Narxi: import qilganlar kutadi

Top-level await li modul tugamaguncha, uni import qilgan modullar bajarilmaydi. Uch faylda ko'ramiz:

js
// sozlamalar.mjs — "serverdan" ish vaqtini oladi
function serverdanOl() {
  return new Promise((resolve) => {
    setTimeout(resolve, 500, { ochilish: 7, yopilish: 23 });
  });
}

console.log("sozlamalar: so'rov yuborildi");
export const ishVaqti = await serverdanOl();
console.log("sozlamalar: javob keldi");
js
// menyu.mjs
console.log("menyu: bajarildi");
export const taomlar = ["osh", "lag'mon", "manti"];
js
// asosiy.mjs
import { ishVaqti } from "./sozlamalar.mjs";
import { taomlar } from "./menyu.mjs";

console.log("asosiy: boshlandi");
const { ochilish, yopilish } = ishVaqti;
console.log(`${ochilish}:00–${yopilish}:00`, taomlar);

node asosiy.mjs:

text
sozlamalar: so'rov yuborildi
menyu: bajarildi
sozlamalar: javob keldi
asosiy: boshlandi
7:00–23:00 [ 'osh', "lag'mon", 'manti' ]

Kim kimni import qiladi — sxemada. Strelka "import qiladi" degani:

flowchart TD
  A["asosiy.mjs<br/>(kutadi)"] --> S["sozlamalar.mjs<br/>await 500 ms"]
  A --> M["menyu.mjs<br/>(darhol bajariladi)"]

Kuzating:

  • sozlamalar.mjs birinchi bajarildi va await da to'xtadi.
  • menyu.mjs kutmadi — u sozlamalar ga bog'liq emas, shuning uchun shu orada bajarildi.
  • asosiy.mjs esa ikkalasini import qiladi. U 500 ms kutdi — sozlamalar tugaguncha uning birorta qatori ham ishlamadi.

Endi tasavvur qiling: sozlamalar.mjs ni loyihadagi 20 ta modul import qiladi, server esa sekin — 3 soniya. Butun ilova 3 soniya "o'lik" turadi. Yoki server javob bermaydi — ilova umuman ochilmaydi. Bu top-level await ning narxi.

Qoidalar:

  • Kirish faylida — mayli. asosiy.js da bir martalik sozlash, til faylini yuklash.
  • Ko'p import qilinadigan modulda — ehtiyot bo'ling. Ayniqsa tarmoq so'rovi bilan: unga Asinxron naqshlar dagi timeout qo'ying yoki so'rovni funksiyaga o'rab, kerakli joyda chaqiring.
  • Kutubxonada — yo'q. Kutubxonangizni ishlatgan har loyiha kutadi, CommonJS'dan esa require qilib bo'lmaydi.

5.3 Brauzerda

<script type="module"> ham modul — unda top-level await ishlaydi. Ikki modul skripti bor sahifa. Birinchisi kutubxonaning package.json faylini JSON sifatida yuklaydi (with { type: "json" } — keyingi bo'limda):

html
<p>Konsolni kuzating.</p>
<script type="module">
  console.log("1-modul: boshlandi");
  const MANZIL =
    "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/package.json";
  const paket = await import(MANZIL, { with: { type: "json" } });
  console.log("1-modul:", paket.default.name, paket.default.version);
</script>
<script type="module">
  console.log("2-modul: ishladi");
</script>

Konsolda:

text
1-modul: boshlandi
2-modul: ishladi
1-modul: canvas-confetti 1.9.4

Birinchi modul tarmoqni kutayotganda ikkinchisi ishladi: top-level await faqat shu modulni import qilganlarni kuttiradi, sahifadagi boshqa skriptlarni emas.

5.4 Qayerda ishlamaydi

Oddiy skriptda (<script> — type="module" siz) va CommonJS faylda top-level await — sintaksis xatosi. Node'da .cjs fayl:

text
SyntaxError: await is only valid in async functions and the top level bodies of modules

"await faqat async funksiyalarda va modullarning yuqori darajasida yaroqli". Xabarning o'zi qoidani aytib turibdi. Tuzatish — faylni modulga aylantirish (.mjs, "type": "module" yoki type="module") yoki async funksiya ichiga olish.

Maslahat: Saytimizdagi «Ishga tushir» tugmasi kodni odatda oddiy skript sifatida bajaradi. Blokda top-level await yoki import bo'lsa esa — <script type="module"> dagidek, modul sifatida. Shuning uchun «await funksiyasiz» bo'limidagi blok saytda ham ishlaydi. Oldingi darslarda await ni async funksiya ichida yozganimizning sababi — o'sha paytda top-level await hali o'tilmagan edi.

Tekshirib ko'ring: asosiy.mjs faqat menyu.mjs ni import qiladi. menyu.mjs esa sozlamalar.mjs ni (unda 500 ms top-level await). asosiy.mjs kutadimi?

Javob

Ha. Kutish zanjir bo'ylab yuqoriga tarqaladi: sozlamalar tugamaguncha menyu bajarilmaydi, menyu tugamaguncha — asosiy. Grafikning istalgan joyidagi top-level await uni import qilgan hamma "ota" modullarni kuttiradi.

5.5 CommonJS'dan ESM yuklash

CommonJS va ESM darsida top-level await li ESM'ni require qilib bo'lmasligini ko'rgan edik (ERR_REQUIRE_ASYNC_MODULE). Endi sababini bilasiz: bunday modul darhol tayyor bo'lmaydi, require esa kutishni bilmaydi. Yechim — import(). U .cjs faylda ham ishlaydi, faqat top-level await yo'q — shuning uchun async funksiya ichida:

js
// hisobot.cjs — CommonJS fayl
async function boshla() {
  const { ishVaqti } = await import("./sozlamalar.mjs");
  console.log("hisobot:", ishVaqti.yopilish);
}
boshla();
console.log("hisobot: boshla() chaqirildi");

sozlamalar.mjs — «Narxi: import qilganlar kutadi» bo'limidagi fayl (500 ms li top-level await bilan). Natija:

text
hisobot: boshla() chaqirildi
sozlamalar: so'rov yuborildi
sozlamalar: javob keldi
hisobot: 23

boshla() birinchi await da to'xtab, boshqaruvni qaytardi — shuning uchun "chaqirildi" birinchi chiqdi.

6. Import attributes: JSON modul

6.1 with { type: "json" }

Menyu narxlari alohida narxlar.json faylida. Uni to'g'ridan-to'g'ri import qilish mumkin — faqat brauzer va Node'ga bu JSON ekanini aytish kerak:

json
{ "osh": 35000, "lagmon": 28000, "manti": 30000, "choy": 5000 }
js
// narx.mjs
import narxlar from "./narxlar.json" with { type: "json" };
console.log(narxlar.osh);

const m = await import("./narxlar.json", { with: { type: "json" } });
console.log(m.default === narxlar, Object.keys(m));

node narx.mjs:

text
35000
true [ 'default' ]
  • with { type: "json" } — import attributes (import belgilari): modulga qo'shimcha ma'lumot. Statik importda — manzildan keyin, dinamikda — ikkinchi argumentdagi with xususiyati.
  • JSON moduldan faqat default eksport keladi — butun obyekt. Nomli import (import { osh } from ...) bo'lmaydi.
  • Modul keshi bu yerda ham ishlaydi: ikkala import bitta obyektni berdi.

6.2 Nega belgi shart

Belgisiz yozib ko'ramiz — import narxlar from "./narxlar.json";. Node 24:

text
TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///…/narxlar.json" needs an import attribute of "type: json"

"Modulga type: json import belgisi kerak". Chrome esa konsolga yozadi:

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

"Modul skriptini yuklab bo'lmadi: JavaScript modul kutilgan edi, server esa application/json turini qaytardi". Nega bunchalik qattiq? Xavfsizlik uchun. Brauzer faylning turini serverning javobidan biladi. Agar .json faylni JavaScript sifatida qabul qilsa, buzilgan yoki begona server JSON o'rniga kod yuborib, uni sizning sahifangizda bajarishi mumkin edi. type: "json" — "men faqat ma'lumot kutyapman, kod bo'lsa — rad et" degan kafolat.

Eski maqolalarda assert { type: "json" } ni uchratasiz — bu taklifning birinchi varianti. U olib tashlangan: Node 24 da SyntaxError: Unexpected identifier 'assert'. Yangi kodda faqat with.

6.3 Qayerda ishlaydi

  • JSON modullar — Chrome va Edge 123, Firefox 138, Safari 17.2. 2025 yil aprelidan Baseline (hamma asosiy brauzerlarda). Node'da — barqaror, ogohlantirishsiz (Node 24 da tekshirildi).
  • CSS modullar — import jadval from "./menyu.css" with { type: "css" }. Natija — tayyor CSSStyleSheet obyekti, u sahifaga document.adoptedStyleSheets orqali ulanadi. Chrome va Edge 123, Firefox 147 da bor, Safari'da hali yo'q — Baseline emas (web-features 3.40). Asosan Web Components bilan ishlatiladi (Web Components). ESM import darsida va'da qilgan with { type: "css" } — shu.

Vite kabi bundlerlar JSON va CSS importlarini belgisiz ham qabul qiladi — ular faylni yig'ish paytida o'zlari aylantiradi. Bundlersiz, brauzerda yoki Node'da esa belgi majburiy.

Hujumchi nigohi

import() ning manzili o'zgaruvchidan kelsa, "bu o'zgaruvchini kim boshqaradi?" degan savol tug'iladi.

Hujum. Sardor tilni URL'dagi ?til= qiymatidan olishni yozdi: import(`./tillar/${new URLSearchParams(location.search).get("til")}.js`). URLSearchParams URL parametrlarini o'qiydi — uni URL va URLSearchParams darsida o'rganamiz. Hujumchi foydalanuvchiga havola yuboradi: ?til=../../yuklamalar/boshqa. Agar saytda foydalanuvchilar fayl yuklay olsa, brauzer hujumchining faylini kod sifatida bajaradi — sizning sahifangiz nomidan, mehmonning tizimga kirgan holatida. Node serverda esa so'rovdan kelgan nom bilan import() — serverdagi istalgan .mjs faylni ishga tushirish imkoniyati.

Himoya. Bugungi kod bunga qarshi turadi: manzil hech qachon foydalanuvchi matnidan to'g'ridan-to'g'ri yig'ilmaydi. Faqat oq ro'yxatdagi qiymat (TILLAR.includes(til)) ishlatiladi, ro'yxatda yo'q bo'lsa — standart til. import() — kod bajarish; unga beriladigan manzil eval ga beriladigan matn kabi ehtiyot bo'lishni talab qiladi.

7. Ko'p uchraydigan xatolar

7.1 .default ni unutish

const chiqar = await import("./menyu.mjs"); chiqar(); → TypeError: chiqar is not a function. Tuzatish: (await import(...)).default yoki const { default: chiqar } = await import(...).

7.2 Yo'l qayerdan hisoblanadi

import("./paket.js") dagi ./ — sahifaga emas, shu qator yozilgan modul fayliga nisbatan. assets/js/asosiy.js ichida ./paket.js — assets/js/paket.js. index.html ga nisbatan yozib (./assets/js/paket.js) qo'ysangiz — assets/js/assets/js/paket.js qidiriladi va 404.

7.3 Yuklash xatosini ushlamaslik

Tarmoq uzilganda await import() rad etiladi. Ushlanmasa — tugma "jim" buziladi. Tuzatish: try/catch, foydalanuvchiga "sahifani yangilang" xabari (Chrome muvaffaqiyatsiz importni qayta so'ramaydi).

7.4 Foydalanuvchi matni manzilda

import(`./${nom}.js`), nom esa URL yoki formadan — begona kodni bajarish yo'li. Tuzatish: oq ro'yxat.

7.5 Kutubxonada top-level await

Sizning modulingizni import qilgan har kod kutadi va CommonJS'dan require qilolmaydi. Tuzatish: asinxron ishni eksport qilinadigan funksiyaga o'rang — chaqiruvchi o'zi qachon kutishni hal qilsin.

8. Mashqlar

1-mashq (oson): Nima chiqadi?

taom.mjs fayli:

js
export const nom = "Manti";
export default 30000;

Boshqa modulda:

js
const m = await import("./taom.mjs");
console.log(m.nom);
console.log(m.default);
console.log(Object.keys(m).length);
const { default: narx } = await import("./taom.mjs");
console.log(narx === m.default);
Yechim
text
Manti
30000
2
true

Modul obyektida ikki xususiyat: default va nom. Default eksport — son, u default xususiyatida. Ikkinchi import() o'sha keshlangan modulni qaytaradi, shuning uchun narx va m.default — bir xil qiymat.

2-mashq (o'rta): Hisobot faqat kerak bo'lganda

kassa.mjs dasturi kunlik savdoni chiqaradi. Hisobotni Excel'ga mos CSV matniga aylantiradigan kod og'ir va kam ishlatiladi — u csv.mjs da. Shunday yozing:

  • csv.mjs — export function csvYasa(qatorlar): har qatorni nom,narx ko'rinishiga keltirib, \n bilan birlashtiradi (sarlavha qatori — taom,narx). Fayl boshida console.log("csv.mjs yuklandi").
  • kassa.mjs — savdo massivini konsolga chiqaradi. Agar buyruqda --csv bo'lsa (process.argv.includes("--csv")), csv.mjs ni dinamik import qilib, CSV matnini ham chiqaradi.

node kassa.mjs da "csv.mjs yuklandi" chiqmasligi, node kassa.mjs --csv da esa chiqishi kerak. Savdo: osh 35 000, manti 30 000. Fayllarni kurs/mashqlar/11/16-dinamik-import/ ga saqlang. Ishora: massivni satrga aylantirish — map va join (Callback imzosi va HOF metodlari).

Yechim
js
// csv.mjs
console.log("csv.mjs yuklandi");

export function csvYasa(qatorlar) {
  const satrlar = qatorlar.map((q) => `${q.nom},${q.narx}`);
  return ["taom,narx", ...satrlar].join("\n");
}
js
// kassa.mjs
const savdo = [
  { nom: "osh", narx: 35000 },
  { nom: "manti", narx: 30000 },
];

console.log(`Bugun: ${savdo.length} ta taom sotildi`);

if (process.argv.includes("--csv")) {
  const { csvYasa } = await import("./csv.mjs");
  console.log(csvYasa(savdo));
}
text
$ node kassa.mjs
Bugun: 2 ta taom sotildi
$ node kassa.mjs --csv
Bugun: 2 ta taom sotildi
csv.mjs yuklandi
taom,narx
osh,35000
manti,30000

Birinchi buyruqda csv.mjs umuman o'qilmadi. Statik import { csvYasa } from "./csv.mjs" bo'lsa, u har safar yuklanardi va "csv.mjs yuklandi" ikkala holatda chiqardi.

bash
git add 11/16-dinamik-import/
git commit -m "11/16: CSV hisobotni faqat --csv bilan yukla"

3-mashq: Vazifalar qadami — eksport modulini bosilganda yuklash

vazifalar v2 da asosiy.js paket.js ni statik import qiladi, index.html esa uni modulepreload bilan oldindan yuklaydi (Brauzerda ESM va import maps). Natijada eksport kodi har sahifa ochilganda keladi. Endi:

  1. Yangi branch: git switch -c feature/dinamik-import.
  2. asosiy.js dan paket.js ning statik importini olib tashlang.
  3. «JSON qilib chiqarish» (#eksport-tugma) bosilganda paket.js ni await import("./paket.js") bilan yuklang va eksportMatni ni modul obyektidan oling. «Import qilish» tugmasi ham paketniOqi ni ishlatadi — unga ham shu yo'l.
  4. Yuklash muvaffaqiyatsiz bo'lsa — elonQil bilan tushunarli xabar, ro'yxat o'zgarmasin.
  5. index.html dagi paket.js ning modulepreload qatorini olib tashlang — aks holda brauzer uni baribir oldindan yuklaydi. README.md ni yangilang.

Ishora: ikkala tinglovchiga bitta yordamchi yozing — paketniYukla() modul obyektini yoki xatoda null qaytarsin. Tinglovchilar async bo'ladi.

Yechim

asosiy.js boshidagi importlardan import { eksportMatni, paketniOqi } from "./paket.js"; qatori o'chiriladi. VazifaXatosi importi (vazifa.js dan) qoladi — vazifa.js ilova ochilishida baribir kerak. Yordamchi — tinglovchilardan oldin:

js
// paket.js — faqat eksport yoki import bosilganda yuklanadi
async function paketniYukla() {
  try {
    return await import("./paket.js");
  } catch (xato) {
    elonQil("Eksport moduli yuklanmadi. Sahifani yangilang.");
    console.error(xato);
    return null;
  }
}

Ikki tinglovchi:

js
eksportTugma.addEventListener("click", async () => {
  const paket = await paketniYukla();
  if (paket === null) {
    return;
  }
  eksportMatn.value = paket.eksportMatni(holat.vazifalar);
  eksportMatn.focus();
  eksportMatn.select();
  elonQil("JSON tayyor. Nusxa olish uchun Ctrl+C bosing.");
});

importTugma.addEventListener("click", async () => {
  importXatosiniTozala();
  elonQil("");
  const paket = await paketniYukla();
  if (paket === null) {
    return;
  }
  try {
    const malumotlar = paket.paketniOqi(importMatn.value);
    royxatniAlmashtir(malumotlar);
    elonQil(`${malumotlar.length} ta vazifa import qilindi.`);
  } catch (xato) {
    if (xato instanceof VazifaXatosi) {
      importXatosiniKorsat(xato.message);
      return;
    }
    importXatosiniKorsat("Kutilmagan xato: import bajarilmadi.");
    throw xato;
  }
});

Nimalar o'zgardi:

  • Tinglovchilar async. await paketniYukla() birinchi bosishda faylni yuklaydi, keyingilarida keshdan darhol oladi.
  • Yuklash xatosi paketniYukla ichida ushlanadi: #elon da xabar, konsolda texnik tafsilot (console.error). Tinglovchi null ni ko'rib, hech narsaga tegmay chiqadi.
  • Importdagi try/catch o'zgarmadi — u endi faqat paketniOqi xatolarini ushlaydi. Modul yuklanmasa, import matni joyida qoladi va ro'yxat almashtirilmaydi.
  • elonQil — Taymerlar darsidagi versiya: xabar 3 soniyada o'zi yo'qoladi.

index.html dan bitta qator o'chiriladi:

html
<link rel="modulepreload" href="assets/js/paket.js">

README.md dagi modullar ro'yxatida:

text
- `paket.js` — eksport va import: `{ versiya: 1, vazifalar }`.
  Statik import qilinmaydi: `asosiy.js` uni tugma bosilganda
  `import()` bilan yuklaydi, `modulepreload` ro'yxatida yo'q.

Live Server bilan sinang (DevTools → Network, "JS" filtri):

  • Sahifani yangilang — ro'yxatda paket.js yo'q. Vazifa qo'shish, belgilash, filtr — hammasi avvalgidek.
  • «JSON qilib chiqarish» — paket.js paydo bo'ladi, maydonda JSON, e'lon chiqadi. Yana bosing — yangi so'rov yo'q.
  • Eksport matnini «Import qilish» maydoniga qo'yib, import qiling — ro'yxat almashtiriladi. Buzuq matn — avvalgidek xato xabari.
  • Yuklash xatosi: sahifani yangilang, Network panelidagi "No throttling" ro'yxatidan Offline ni tanlang va «JSON qilib chiqarish» ni bosing — e'londa "Eksport moduli yuklanmadi. Sahifani yangilang.", ro'yxat joyida. Online'ga qaytaring va yana bosing — xabar takrorlanadi (brauzer muvaffaqiyatsiz importni eslab qolgan). Sahifani yangilang — eksport ishlaydi.

Bu qadamlarni biz Chrome 154 da avtomatik sinadik: paket.js faqat birinchi bosishda so'raldi, oflayn holatdan keyin esa faqat sahifani yangilash yordam berdi.

Maslahat: Bu qaror abadiy emas. Qism davomida vazifalar ga saqlash moduli qo'shiladi va paket.js sahifa ochilishida ham kerak bo'lib qoladi. Shunda yakuniy loyihada o'lchab ko'rib, statik importga qaytamiz. Texnikaning o'zi — kam ishlatiladigan kodni kechiktirish — to'g'riligicha qoladi.

bash
git add assets/js/asosiy.js index.html README.md
git commit -m "Eksport: paket.js ni bosilganda import() bilan yukla"
git switch main
git merge feature/dinamik-import
git branch -d feature/dinamik-import
git push

9. Real ishda

  • Sahifalar bo'yicha bo'lish. React, Vue, Next.js ilovalarida har sahifa — alohida bo'lak: "Profil" sahifasining kodi faqat unga o'tganda yuklanadi. Ichida — import().
  • Og'ir kutubxonalar. PDF yasash, grafik chizish, matn muharriri, xarita — tugma bosilganda yoki element ko'ringanda yuklanadi. Katta ilovalarning veb-versiyalari shu bilan tez ochiladi.
  • Tillar va mintaqalar. Tarjima fayllari tanlangan til bo'yicha dinamik yuklanadi — bugungi tillar/ misoli.
  • Node. CLI dasturlar kam ishlatiladigan buyruqlarning kodini faqat kerak bo'lganda import qiladi — dastur tezroq ishga tushadi. Konfiguratsiya fayllari (vite.config.js, eslint.config.js) ham ichkarida import() bilan o'qiladi.
  • Intervyu. "Code splitting nima va qanday qilinadi?", "import() va import farqi?", "Top-level await ning kamchiligi?"

Xulosa

  • import(manzil) — modulni ish paytida yuklaydi va modul obyekti bilan bajariladigan Promise qaytaradi; oddiy skript va CommonJS'da ham ishlaydi.
  • Modul obyekti: nomli eksportlar + default; faqat o'qish uchun; modul bir marta bajariladi va keshlanadi.
  • Kam ishlatiladigan kodni bosilganda yuklang; yuklash xatosini ushlang — Chrome muvaffaqiyatsiz importni qayta so'ramaydi.
  • Manzil o'zgaruvchidan bo'lsa — faqat oq ro'yxat orqali: import() — kod bajarish.
  • Top-level await — faqat modullarda; uni import qilgan hamma modul kutadi — kirish faylida mayli, kutubxonada yo'q.
  • JSON — with { type: "json" } bilan (Baseline 2025); CSS modullar hali Baseline emas.

Keyingi dars: fetch asoslari — serverdan haqiqiy ma'lumot olamiz: «Bahor» mashq API'sidan menyu, Response, response.ok va nega fetch 404 da rad etilmaydi. Shu paytgacha "o'ynab" kelgan server o'rinbosarlarimiz haqiqiy so'rovlarga almashadi.

Manbalar

  • ECMAScript: "Import Calls", "Module Namespace Exotic Objects", "Import Attributes" — tc39.es/ecma262
  • HTML Living Standard: "JSON module scripts", "CSS module scripts" — html.spec.whatwg.org
  • MDN: "import()", "await — top level await", "Import attributes" — developer.mozilla.org
  • Node.js 24 hujjatlari: "Modules: ECMAScript modules" (import(), JSON modullar, import attributes) — nodejs.org/api/esm.html
  • web-features 3.40: json-modules, css-modules, top-level-await — github.com/web-platform-dx/web-features
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript dinamik import() va top-level await: modulni kerak bo'lganda yuklash — IlmHamroh