IlmHamroh
JavaScript Full-stack/16-qism. Frontend asboblari: npm, bundlerlar, config20/48-dars18 daqiqa
Mundarija (27)

Bundler nima: bog'liqlik grafi, entry, chunk va asset

Qisqacha: Bundler (yig'uvchi) kirish faylidan (entry) boshlab hamma import larni kuzatadi va bog'liqlik grafini quradi. Keyin graf tugunlarini (modullarni) bir necha chunk ga (yig'ilgan JS fayllarga) birlashtiradi, rasm, CSS va shriftlarni esa asset sifatida nomiga hash qo'shib chiqaradi. Dinamik import() alohida chunk yaratadi, ishlatilmagan kod tashlanadi. Bugun vazifalar ga vite build qo'shamiz: 20 ta so'rov o'rniga 4 ta, gzip bilan 32.0 KB o'rniga 14.4 KB.

Bu darsda

  • Entry, bog'liqlik grafi, modul, chunk, asset va output atamalarini haqiqiy build chiqishida ko'rsata olasiz.
  • Kichik loyihani vite build bilan yig'ib, har chunk ichida qaysi modullar borligini o'z plaginingiz bilan chiqarasiz.
  • Dev server va production build bir xil kodni qanday turlicha berishini ko'rasiz.
  • vazifalar ni tsc + nusxalashdan vite build ga o'tkazasiz va public/ papkasi nega kerakligini bilasiz.

Oldin bilishingiz kerak: Frontend asboblar zanjiri, Modul formatlari, Dinamik import va top-level await, TypeScript build: tsc, bundler'lar va tsgo.

1. Nega bu kerak?

Asboblar zanjiri darsida vazifalar v5 ning dist/ ini o'lchagan edik. Unda 21 ta JavaScript va CSS fayl bor, brauzer ulardan 20 tasini alohida so'raydi. web-vitals paketini esa qo'lda vendor/ papkasiga ko'chirgan edik, chunki brauzer import "web-vitals" ni qayerdan izlashni bilmaydi.

tsc va bundler'lar darsida esa esbuild'ni fayl-fayl ishlatib ko'rdik va tuzoqqa tushdik: import "./royxat.ts" yo'li o'zgarmay qoldi. O'shanda va'da berdik: bundler bu muammoni ildizidan yechadi, chunki unda "yo'l" umuman qolmaydi — modullar bitta faylga kiradi.

Bugun shu va'dani bajaramiz. Hayotiy o'xshatish: «Bahor» oshxonasiga 15 ta yetkazib beruvchidan mahsulot keladi — har biri alohida mashinada, alohida vaqtda. Bundler — omborchi: ertalab hammasini bir joyga yig'ib, oshpazga bitta tayyor savat beradi. Kam qatnov, kam kutish.

Maslahat: Chiqishlar 2026-10-10 da Windows 11, Node 24.21.0 va Vite 8.3.3 bilan olingan. Ranglar olib tashlangan. Fayl nomlaridagi hash (index-ys8EmKCO.js) va vaqtlar (built in 114ms) sizda boshqacha bo'ladi.

2. Kichik loyiha: «Bahor» menyusi

2.1 Fayllar

Tushunchalarni avval kichik, sof JavaScript loyihada ko'ramiz. vazifalar dagi 24 modul — keyin. Papka tuzilmasi:

text
menyu/
├── index.html
├── package.json        "type": "module", vite 8.3.3
└── src/
    ├── main.js         kirish nuqtasi
    ├── dishes.js       taomlar ro'yxati
    ├── price.js        narxni yozish
    ├── report.js       hisobot — tugma bosilganda
    ├── menyu.css
    └── logo.svg

index.html da faqat ikki ulanish:

html
<link rel="stylesheet" href="/src/menyu.css">
<script type="module" src="/src/main.js"></script>

main.js:

js
// main.js — kirish nuqtasi (entry)
import { dishes } from "./dishes.js";
import { formatPrice } from "./price.js";
import logoUrl from "./logo.svg";

const list = document.querySelector("#menyu");
for (const dish of dishes) {
  const item = document.createElement("li");
  item.textContent = `${dish.name} — ${formatPrice(dish.price)}`;
  list.append(item);
}
document.title += ` (${logoUrl})`;

// Hisobot kerak bo'lgandagina yuklanadi — alohida chunk
const button = document.querySelector("#hisobot");
button.addEventListener("click", async () => {
  const { buildReport } = await import("./report.js");
  console.log(buildReport(dishes));
});

Uchinchi qatorga e'tibor bering: import logoUrl from "./logo.svg" — JavaScript'da rasm import qilinmoqda! Brauzer buni tushunmaydi, lekin bundler tushunadi: u rasmni alohida faylga chiqarib, logoUrl ga uning manzilini beradi.

price.js da ikki funksiya, ulardan biri hech qayerda ishlatilmaydi:

js
// price.js — narxni chiroyli yozish
export function formatPrice(sum) {
  return `${String(sum).replace(/\B(?=(\d{3})+$)/g, " ")} so'm`;
}

// Hech qayerda ishlatilmaydi — build'ga tushmasligi kerak
export function formatDollar(sum) {
  return `$${(sum / 12800).toFixed(2)}`;
}

console.log(formatPrice(35000)); // 35 000 so'm

(Oxirgi qator faqat shu darsda natijani ko'rsatish uchun; loyihadagi faylda u yo'q.) report.js ham formatPrice ni import qiladi va jami narxni hisoblaydi.

2.2 Bog'liqlik grafi

Bundler index.html dan boshlaydi va har import ni o'q sifatida chizadi:

flowchart TB
  H["index.html"] --> M["main.js<br/>(entry)"]
  H --> C["menyu.css"]
  M --> D["dishes.js"]
  M --> P["price.js"]
  M --> L["logo.svg"]
  M -. "import()" .-> R["report.js"]
  R --> P

Uzluksiz o'qlar — statik import, uzuq o'q — dinamik import(). price.js ga ikki yo'l keladi: main.js dan ham, report.js dan ham. Bu muhim — pastda ko'ramiz.

Atamalarni shu rasm bilan aniqlaymiz:

  • Entry (kirish nuqtasi) — graf boshlanadigan fayl. Vite'da bu index.html, undan keyin <script type="module"> dagi main.js. Bitta loyihada bir nechta entry bo'lishi mumkin.
  • Modul — grafning bitta tuguni: JS, CSS, rasm — bundler uchun hammasi modul.
  • Bog'liqlik grafi (dependency graph) — modullar va ular orasidagi import lar xaritasi. Xuddi metro sxemasi: bekatlar — modullar, yo'llar — importlar.

3. Build: graf qanday fayllarga aylanadi

3.1 Birinchi vite build

Chiqishda ikki joyga qarang: modules transformed qatoridagi son (grafdagi modullar) va dist/ bilan boshlanadigan qatorlar (brauzerga boradigan fayllar).

bash
npx vite build --assetsInlineLimit 0
text
vite v8.3.3 building client environment for production...
transforming...
✓ 10 modules transformed.
rendering chunks...
computing gzip size...
dist/assets/logo-mWEg-Urw.svg   0.11 kB │ gzip: 0.12 kB
dist/index.html                 0.38 kB │ gzip: 0.25 kB
dist/assets/index-CRKnUwHd.css  0.06 kB │ gzip: 0.08 kB
dist/assets/report-12McAH2s.js  0.15 kB │ gzip: 0.15 kB
dist/assets/index-ys8EmKCO.js   2.64 kB │ gzip: 1.30 kB

✓ built in 114ms

Bosqichlar nomi bilan yozilgan:

Qator Bosqich Nima bo'ldi
transforming... o'girish har modul o'qildi, kerak bo'lsa o'girildi
10 modules transformed graf grafda 10 ta modul
rendering chunks... yig'ish modullar chunk'larga birlashtirildi
computing gzip size... o'lchash siqilgan hajm hisoblandi

--assetsInlineLimit 0 nima ekanini pastda, asset bo'limida ko'ramiz. 10 ta modul — bizning 6 ta faylimiz, index.html va Vite'ning o'zi qo'shgan yordamchilar.

3.2 Chunk

Chunk (bo'lak) — bir nechta modul birlashtirilgan bitta chiqish JS fayli. Bizda ikkita: index-….js va report-….js. Qaysi modul qaysi chunk'ga tushganini Vite chiqishda aytmaydi. Uni ko'rish uchun 10 qatorli o'z plaginimizni yozdik (to'liq kodi 3-mashqda):

text
chunk  assets/index-ys8EmKCO.js (entry)
         ← vite/modulepreload-polyfill.js
         ← src/menyu.css
         ← src/dishes.js
         ← src/price.js
         ← src/logo.svg
         ← vite/preload-helper.js
         ← src/main.js
         ← index.html
chunk  assets/report-12McAH2s.js (dinamik)
         ← src/report.js
asset  assets/index-CRKnUwHd.css
asset  assets/logo-mWEg-Urw.svg

Uchta qoida ko'rinib turibdi:

  1. Statik importlar — entry chunk'iga. main.js, dishes.js, price.js — bitta faylda.
  2. Dinamik import() — alohida chunk. report.js faqat tugma bosilganda yuklanadi. Bu Dinamik import darsidagi g'oya: bundler uni avtomatik bo'lak qiladi.
  3. Umumiy modul — bir marta. price.js ikkala chunk'ga ham kerak, lekin u faqat entry chunk'ida. report chunk'i uni o'sha yerdan oladi.

Uchinchi qoidani report chunk'ining ichida ko'rish mumkin:

text
import{t as e}from"./index-ys8EmKCO.js";function t(t){let n=t.reduce((e,t)=>e+t.price,0);return`Jami ${t.length} taom, ${e(n)}`}export{t as buildReport};

formatPrice endi t deb nomlangan va index-ys8EmKCO.js dan import qilinmoqda — chunk'lar o'zaro ham modul. vite/preload-helper.js — Vite'ning dinamik import uchun yordamchisi, vite/modulepreload-polyfill.js — eski brauzerlar uchun yamoq (Polyfill va Baseline darsida uni o'chiramiz).

3.3 O'lik kod

formatDollar qayerda?

bash
grep -c formatDollar dist/assets/*.js
text
dist/assets/index-ys8EmKCO.js:0
dist/assets/report-12McAH2s.js:0

Hech qayerda — hatto 12800 raqami ham yo'q. Bundler grafni ko'rib bildi: formatDollar ni hech kim import qilmaydi. Bu tree shaking — Tree shaking va minifikatsiya darsining mavzusi.

3.4 Asset va output

Asset — JavaScript bo'lmagan chiqish fayli: CSS, rasm, shrift, video. Bundler uni nusxalaydi, nomiga hash qo'shadi va kodda unga havolani yangi nomga almashtiradi. logoUrl endi /assets/logo-mWEg-Urw.svg ga teng.

Kichik assetlar boshqacha ishlanadi. --assetsInlineLimit 0 siz build'da logo.svg fayli chiqmadi: u 4 KB dan kichik bo'lgani uchun Vite uni to'g'ridan-to'g'ri JS ichiga data: manzil qilib joyladi. Bitta kichik so'rov tejaldi. Chegarani build.assetsInlineLimit belgilaydi (sukut — 4096 bayt). Biz uni 0 qilib, asset'ni ko'rinadigan qildik.

Output (chiqish) — hamma natija yoziladigan papka, sukut bo'yicha dist/. Vite index.html ni ham qayta yozdi:

html
<script type="module" crossorigin
        src="/assets/index-ys8EmKCO.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CRKnUwHd.css">

/src/main.js o'rniga — hash'li chunk, /src/menyu.css o'rniga — hash'li CSS. crossorigin atributi — modul skriptlar CORS bilan yuklanadi (Brauzerda ESM darsida).

Tekshirib ko'ring: report.js ham dishes.js ni import qilsa, dishes.js qaysi chunk'da bo'ladi — bitta yoki ikkala chunk'da?

Javob

Bitta — entry chunk'ida. U baribir sahifa ochilganda kerak (statik importda), shuning uchun u yerda bo'ladi; report chunk'i uni import qiladi. Bundler bir modulni ikki chunk'ga takrorlamaydi — aks holda u ikki marta yuklanib, ikki marta bajarilardi va modul semantikasi (bir marta bajarilish) buzilardi.

4. Plagin va loader

import logoUrl from "./logo.svg" qanday ishladi? JavaScript parser SVG'ni o'qiy olmaydi. Uni plagin o'qidi.

Plagin (plugin) — bundlerga yangi imkoniyat qo'shadigan kod bo'lagi. U build bosqichlariga hook (bosqichga ulanish nuqtasi) orqali ulanadi: resolveId (import yo'lini topish), load (faylni o'qish), transform (kodni o'zgartirish), generateBundle (chiqish fayllari tayyor bo'lganda). Vite'ning ichki plaginlari CSS, rasm, JSON, TypeScript'ni shunday ishlaydi. Biz yozgan "graf ko'rsatkich" ham generateBundle ga ulangan plagin.

Loader — webpack'dagi (eski, lekin hali keng tarqalgan bundler) shunga o'xshash tushuncha: ".svg fayllarini shu dastur o'qisin" (webpack konseptlari darsida). Vite'da loader yo'q — hammasi plagin.

Plaginlarni o'zimiz yozishni Vite plaginlari darsida chuqur o'rganamiz. Bugun vazifalar da bitta kichik plagin uchratamiz.

5. Dev va production

Asboblar zanjiri darsida dev server'ni oldindan ko'rgan edik. Endi aynan bir faylni ikki rejimda solishtiramiz. Dev server (npx vite) /src/main.js so'roviga shuni qaytardi (boshi):

js
// main.js — kirish nuqtasi (entry)
import { dishes } from "/src/dishes.js";
import { formatPrice } from "/src/price.js";
import logoUrl from "/src/logo.svg?import";

Va /src/price.js so'roviga — formatDollar bilan birga to'liq fayl. Farqlar:

Dev server Production build
Fayllar har modul — alohida so'rov bir necha chunk
Graf so'ralganda, bo'lak-bo'lak butunlay, oldindan
Importlar /src/price.js ga qayta yozilgan chunk ichida yo'q bo'lib ketgan
Ishlatilmagan kod bor (formatDollar) tashlangan
Izohlar, nomlar saqlangan minifikatsiya qilingan
Maqsad tezkor ishga tushish, HMR kichik va tez yuklanish

Dev'da Vite bundle qilmaydi: brauzer ESM'ni o'zi tushunadi, Vite faqat yo'llarni to'g'rilaydi va kerak bo'lsa o'giradi. Shuning uchun katta loyihada ham dev server bir zumda ishga tushadi. Nega bunday ekanini Vite I darsida ochamiz. Muhim xulosa: dev'da ishlagani production'da ishlashini kafolatlamaydi — har o'zgarishdan keyin build ham qilinadi va natijasi tekshiriladi.

6. vazifalar ning grafi

Endi haqiqiy loyiha. Kanon build chiqishidan (tsc dan keyin). Birinchi qator — ogohlantirish, uni pastda ochamiz; qolganida yana modullar soni va dist/ qatorlariga qarang:

text
<script src="assets/js/tema.js"> in "/index.html" can't be bundled without type="module" attribute
✓ 24 modules transformed.
dist/index.html                  5.27 kB │ gzip: 1.89 kB
dist/assets/index-BI1hyTgJ.css   3.59 kB │ gzip: 1.29 kB
dist/assets/js/tema.js           0.12 kB │ gzip: 0.12 kB
dist/assets/olchov-G4bloHwN.js   8.87 kB │ gzip: 3.38 kB
dist/assets/index-BewOu_Dy.js   25.86 kB │ gzip: 9.67 kB
✓ built in 217ms
sw.js: QOBIQ — 12 ta, VERSIYA — v6-3a23fab5

Tanish atamalar bilan o'qiymiz:

  • Entry — index.html → asosiy.ts. Grafda 24 modul: bizning .ts modullarimiz, web-vitals, Vite yordamchilari, CSS va HTML.
  • Ikki chunk. index-….js — ilovaning hamma statik modullari bitta faylda. olchov-….js — Core Web Vitals o'lchovi dinamik import("./olchov.ts") bilan yuklanadi va web-vitals paketi aynan shu chunk'ga tushdi: sahifa ochilishida u umuman yuklanmaydi.
  • Asset — index-….css.
  • tema.js — alohida va hash'siz. Bu ataylab.

6.1 Ogohlantirish: can't be bundled

Asboblar zanjiri darsida bu qatorni keyinga qoldirgan edik. Tarjimasi: "index.html dagi <script src="assets/js/tema.js"> ni type="module" atributisiz yig'ib bo'lmaydi".

tema.js — oddiy (modul bo'lmagan) skript. U Qorong'i rejim va temalash darsidan beri <head> da sahifa chizilishidan oldin ishlaydi va qorong'i/yorug' temani qo'yadi — aks holda sahifa bir lahza oq bo'lib "chaqnaydi". Modul skriptlar esa kechiktirib bajariladi. Shuning uchun tema.js oddiy skript bo'lib qolishi kerak. Vite faqat modullarni grafga oladi va oddiy skriptni "tegmayman" deb ogohlantiradi.

Yechim vite.config.ts da: tema.ts ni ikkinchi entry qilamiz va chiqish nomini o'zgarmas (assets/js/tema.js) qilamiz, toki index.html dagi manzil ishlayversin. Ogohlantirish qoladi — u "bu skriptni o'zing nazorat qil" degan ma'lumot.

6.2 Raqamlar

Asboblar zanjiri darsining 3-mashqida yozgan olcha.mjs bilan dist/ ni o'lchaymiz (kurs/mashqlar/16/01-hajm/ papkasidan):

bash
node olcha.mjs ../../../vazifalar/dist
text
assets/index-BI1hyTgJ.css          3596   1290
assets/index-BewOu_Dy.js          25864   9595
assets/js/tema.js                   128    127
assets/olchov-G4bloHwN.js          8878   3364
JAMI: 4 fayl, 38.5 KB, gzip 14.4 KB
vazifalar: brauzer yuklaydigan JS + CSS (gzip)
  • holat 16: tsc + nusxalash21 fayl, 20 so'rov32 KB
  • holat 20: vite build4 fayl14,4 KB

Manba: O'lchandi: vazifalar holat 16 va 20 dist/, olcha.mjs (gzip -9), Node 24.21.0, Vite 8.3.3, 2026-10-10

Xom hajm 85.8 → 38.5 KB, gzip 32.0 → 14.4 KB, brauzer yuklaydigan fayllar 20 → 4 (diskdagi 21 ta JS va CSS dan turlar.js so'ralmaydi — u bo'sh modul). Bularning hammasi — bitta buyruq. 16-qism oxirida (source map, target va boshqa qadamlardan keyin) — 38.1 va 14.3 KB.

7. Ko'p uchraydigan xatolar

7.1 public/ siz: manifest buzildi

vazifalar ga Vite qo'shishning birinchi urinishida sw.js, manifest va ikonlar eski joyida qoldi. Vite ularni ham "asset" deb ko'rdi (manifest index.html dagi <link> da):

text
dist/assets/ikon-CdIzd46i.svg               0.27 kB │ gzip: 0.20 kB
dist/assets/manifest-faQiLkSt.webmanifest   0.67 kB
dist/assets/ikon-180-ew4y6vgh.png           1.10 kB

Va index.html da: <link rel="manifest" href="./assets/manifest-faQiLkSt.webmanifest">. Uch muammo: manifest ichidagi ikon yo'llari endi noto'g'ri papkaga qaraydi; sw.js umuman dist/ ga tushmadi (uni hech kim import qilmaydi — grafda yo'q); Service Worker nomi o'zgarmasligi shart edi.

Yechim — public/ papkasi. Undagi fayllar grafga kirmaydi: Vite ularni dist/ ga o'zgarishsiz, o'sha nom bilan ko'chiradi. Nomi doim bir xil bo'lishi kerak bo'lgan fayllar (sw.js, manifest.webmanifest, robots.txt, favicon) — public/ ga. Batafsil — Vite IV darsida.

7.2 Yashil test aldaydi: blocking="render"

Vite <script type="module"> ni hash'li nom bilan qayta yozadi — va bizning blocking="render" atributimiz (DevTools: Performance darsidan) tushib qoldi. Natija: ro'yxat birinchi kadrda chizilmay, sahifa siljidi — brauzer tekshiruvi [Web Vitals] CLS: 0.057 ko'rsatdi (avval 0). Eng xavflisi: CLS tekshiruvi index.html da atribut bo'lmasa, o'tkazib yuborilardi va natija "HAMMASI " edi.

Tuzatish — vite.config.ts dagi 10 qatorli plagin: u Vite yozgan HTML'ga atributni qaytaradi. Saboq: bundler kodingizni qayta yozadi — build natijasini (dist/) tekshiring, manbani emas.

7.3 dist/ ni file:// bilan ochish

Vite yo'llari modul skriptlar — file:// da CORS xatosi (Asboblar zanjiri darsida ko'rganmiz). Build natijasini server orqali oching; Vite I da npm run preview qo'shamiz.

8. Mashqlar

Mashqlar kurs/mashqlar/16/20-bundler/ da.

1-mashq (oson): Atamani toping

«Bahor» menyusi build'i bo'yicha:

  • Graf boshlanadigan JS fayl:
  • Tugma bosilganda yuklanadigan chunk qaysi modul uchun:
  • Grafdagi modullar soni (modules transformed):
  • JS bo'lmagan, nomiga hash qo'shilgan ikki fayl turi: CSS va
Yechim

Entry — main.js (index.html uni <script type="module"> bilan ulaydi). Dinamik chunk — report.js. Grafda 10 ta modul: 6 ta o'zimizniki, index.html va Vite qo'shgan yordamchilar. Assetlar — index-….css va logo-….svg. Agar --assetsInlineLimit 0 siz yig'sangiz, SVG asset bo'lib chiqmaydi — u 4 KB dan kichik, JS ichiga joylanadi.

2-mashq (o'rta): Kichik loyihani yig'ing

Darsdagi menyu/ loyihasini yarating (npm init -y, npm pkg set type=module, npm i -D -E vite@8.3.3). So'ng:

  1. npx vite build (bayroqsiz). dist/assets/ da nechta fayl? SVG qayerda?
  2. report.js ga import { dishes } from "./dishes.js"; qo'shing va qayta yig'ing. Chunk'lar soni o'zgardimi?
  3. main.js dagi await import("./report.js") ni oddiy import { buildReport } from "./report.js"; ga almashtiring. Endi nechta JS chunk?
Yechim
  1. Uchta: index-….js, report-….js, index-….css. SVG — index-….js ichida data:image/svg+xml,… bo'lib turibdi (assetsInlineLimit sukut — 4 KB).
  2. O'zgarmadi — ikkita. dishes.js entry chunk'ida qoladi, report chunk'i uni o'sha yerdan import qiladi.
  3. Bitta. Statik import — "sahifa ochilishida kerak" degani, shuning uchun report.js ham entry chunk'iga qo'shildi. Dinamik import() — bundlerga "buni alohida qil" degan yagona signal.

3-mashq (qiyin): Grafni ko'rsatadigan plagin

vite.config.js yozing: unda generateBundle hook'iga ulangan plagin har chiqish faylini chiqarsin — chunk bo'lsa, entry yoki dinamik ekanini va ichidagi modullar ro'yxatini.

Ishoralar:

  • generateBundle(options, bundle) — bundle obyektida har chiqish fayli: type ("chunk" yoki "asset"), fileName, chunk'da isEntry va modules (kalitlari — modul yo'llari).
  • Yo'llar to'liq (C:/…/src/main.js) keladi — node:path dagi relative(process.cwd(), id) bilan qisqartiring.
  • Vite o'zi yasagan modul yo'li "\0" (nol belgi) bilan boshlanadi — uni olib tashlang.
Yechim
js
// vite.config.js — build'dan keyin har chunk ichidagi modullar
import { relative } from "node:path";
import { defineConfig } from "vite";

export default defineConfig({
  build: { assetsInlineLimit: 0 },
  plugins: [
    {
      name: "graf-korsat",
      generateBundle(_options, bundle) {
        for (const file of Object.values(bundle)) {
          if (file.type === "asset") {
            console.log(`asset  ${file.fileName}`);
            continue;
          }
          const kind = file.isEntry ? "entry" : "dinamik";
          console.log(`chunk  ${file.fileName} (${kind})`);
          for (const id of Object.keys(file.modules)) {
            // "\0" bilan boshlangan — Vite o'zi yasagan modul
            const name = id.startsWith("\0")
              ? id.slice(1)
              : relative(process.cwd(), id).replaceAll("\\", "/");
            console.log(`         ← ${name}`);
          }
        }
      },
    },
  ],
});

npx vite build chiqishi — «Chunk» bo'limidagi ro'yxat. Plaginning tuzilishi — name va hook funksiyalari bo'lgan oddiy obyekt. _options oldidagi pastki chiziq — "bu parametr ishlatilmaydi" degan kelishuv. Bunday plaginlarni Vite plaginlari darsida yozishni davom ettiramiz.

4-mashq: Vazifalar qadami — vite build

vazifalar da tsc faqat tekshiradi, JavaScript'ni Vite yozadi.

  • Branch: build/vite
  • Commit: build: vite build — tsc emit va nusxala.js o'rniga; public/, qobiq.js (tana bilan)
  1. npm i -D vite@8.3.3; web-vitals ni dependencies ga o'tkazing (endi u brauzer kodiga kiradi — bog'liqlik turlari): npm i web-vitals@6.2.3.
  2. olchov.ts: from "./vendor/web-vitals.js" → from "web-vitals". assets/js/vendor/, skriptlar/vendor.js va npm run vendor ni o'chiring.
  3. index.html: 15 ta modulepreload ni o'chiring (Vite kerak bo'lsa o'zi qo'yadi), src="assets/js/asosiy.js" → asosiy.ts.
  4. sw.js, manifest.webmanifest, assets/ikonlar/ → public/ ga (yo'llari dist/ da o'sha-o'sha qoladi). skriptlar/nusxala.js ni o'chiring.
  5. tsconfig.json: noEmit: true, allowImportingTsExtensions: true (rewriteRelativeImportExtensions, outDir, rootDir o'rniga).
  6. vite.config.ts va skriptlar/qobiq.js (Service Worker qobig'ini build'dan keyin yozadi). package.json: "build": "tsc && vite build", "postbuild": "node skriptlar/qobiq.js", prebuild o'chadi (Vite dist/ ni o'zi tozalaydi).
Yechim

vite.config.ts:

ts
// vite.config.ts — build sozlamasi (16/#20). Vite index.html dan
// boshlab import grafini quradi: asosiy.ts → render.ts → ... va
// hammasini bir nechta faylga (chunk) yig'adi
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    {
      // Vite <script type="module"> ni o'zi qayta yozadi (hash'li nom
      // bilan) va blocking="render" tushib qoladi. U kerak
      // (13/#30): ro'yxat birinchi kadrda chiziladi, CLS 0.
      // Plaginlar — 16/#31
      name: "blocking-render",
      transformIndexHtml: {
        order: "post",
        handler: (html) =>
          html.replace(
            '<script type="module" crossorigin',
            '<script type="module" blocking="render" crossorigin',
          ),
      },
    },
  ],
  // Sayt login.github.io/vazifalar/ da: yo'llar nisbiy
  // ("./assets/...") — papka nomi qanday bo'lsa ham ishlaydi
  base: "./",
  build: {
    rolldownOptions: {
      input: {
        index: "index.html",
        // tema.ts — <head> dagi ODDIY skript (modul emas): sahifa
        // chizilishidan oldin ishlaydi. Vite type="module" siz
        // <script> ni yig'maydi — alohida kirish nuqtasi qilamiz
        tema: "assets/js/tema.ts",
      },
      output: {
        // tema.js nomi o'zgarmaydi: index.html shu nom bilan so'raydi
        entryFileNames: (chunk) =>
          chunk.name === "tema"
            ? "assets/js/tema.js"
            : "assets/[name]-[hash].js",
      },
    },
  },
});

Kalitlar qatorma-qator:

Kalit Nima qiladi Sukut
plugins build bosqichlariga ulanadigan plaginlar []
base dist/ dagi yo'llar prefiksi "/"
rolldownOptions.input entry'lar (nom → fayl) index.html
output.entryFileNames entry chunk'lar nomi assets/[name]-[hash].js

base: "./" — GitHub Pages sayti login.github.io/vazifalar/ da turadi. "/" bo'lsa, brauzer /assets/… ni domen ildizidan izlardi va 404 olardi. [name]-[hash] — chunk nomi va tarkib hash'i (hash bilan keshlash darsida).

skriptlar/qobiq.js (asosiy qismi) — dist/ dagi hamma faylni sw.js ning QOBIQ ro'yxatiga, tarkib hash'ini VERSIYA ga yozadi. Hash'li nomlarni endi qo'lda yozib bo'lmaydi — ro'yxatni build'ning o'zi tuzadi:

js
const royxat = qobiqRoyxati(fayllar);
// Tarkib o'zgarsa — VERSIYA ham (yangi kesh)
const hash = createHash("sha256");
for (const yol of royxat.slice(1)) {
  hash.update(yol).update(readFileSync(new URL(yol, dist)));
}
const versiya = `v6-${hash.digest("hex").slice(0, 8)}`;

Natija — «vazifalar ning grafi» bo'limidagi chiqish: 4 ta yuklanadigan fayl, dist/ da jami 29 o'rniga 12 fayl, sw.js: QOBIQ — 12 ta. dist/index.html da:

html
<script src="assets/js/tema.js"></script>
<script type="module" blocking="render" crossorigin
        src="./assets/index-BewOu_Dy.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-BI1hyTgJ.css">

CSP (script-src 'self') bir harf ham o'zgarmagan: inline skript yo'q, hamma fayl o'z domenimizdan.

Testlar: 143 → 145 (qobiq.test.js +3; sw.test.js dagi diskdagi modullarni QOBIQ bilan solishtiruvchi 2 test endi ma'nosiz — nomlarda hash — o'rniga bitta "manbada faqat sahifa" testi). Brauzer: 159/159, SW oflayn ham.

Commit va PR — sarlavha 71 belgi (≤ 72), tafsilot commit tanasida (ikkinchi -m). Qo'shtirnoq ichidagi \ va yangi qator — telefon ekraniga sig'ishi uchun: shell ularni olib tashlab, satrni birlashtiradi.

bash
git switch -c build/vite
npm run build && npm test && npm run lint
npm run format:check
git add -A
git commit -m \
"build: vite build — tsc emit va nusxala.js o'rniga;\
 public/, qobiq.js" \
-m "web-vitals npm'dan (vendor/ o'chdi);\
 blocking=render plagini; e2e bor()"
git push -u origin build/vite
gh pr create --fill
gh pr merge --merge

Diff: 25 fayl, +965 −192 (lock bilan).

9. Real ishda

  • Har zamonaviy frontend loyihasi bundler bilan quriladi: Vite (React, Vue, Svelte shablonlari), Next.js ichida Turbopack, eski loyihalarda webpack. Atamalar — entry, chunk, asset — hammasida bir xil.
  • "Nega sahifa sekin?" savoliga javob ko'pincha grafda: katta kutubxona entry chunk'iga tushib qolgan. Buni ko'rish uchun bundle tahlili kerak — Vite V darsida.
  • Intervyu savollari: "Bundler nima qiladi?", "Chunk va asset farqi?", "Dinamik import bundle'ga qanday ta'sir qiladi?", "Dev va production build farqi?".

Xulosa

  • Bundler entry'dan boshlab import larni kuzatib bog'liqlik grafini quradi; grafdagi har fayl — modul.
  • Statik importlar entry chunk'iga, dinamik import() — alohida chunk'ga; umumiy modul bir marta, chunk'lar bir-biridan import qiladi.
  • Asset — CSS, rasm, shrift: hash'li nom bilan chiqadi, 4 KB dan kichigi JS ichiga joylanadi; public/ dagi fayllar grafga kirmaydi va o'zgarishsiz ko'chadi.
  • Plagin hook'lar orqali build'ga ulanadi; Vite'da loader yo'q — hammasi plagin.
  • vazifalar: vite build bilan brauzer yuklaydigan fayllar 20 → 4, gzip 32.0 → 14.4 KB; tema.js — ikkinchi entry, blocking="render" — o'z plaginimiz.

Keyingi dars: Transpilyatsiya: Babel, SWC, esbuild va Oxc — bundler grafni quradi, lekin .ts va yangi sintaksisni kim o'giradi? Transpilerlar ichiga — AST darajasida — qaraymiz.

Manbalar

  • Vite: "Building for Production", "Static Asset Handling" (assetsInlineLimit, public/), "Build Options" — vite.dev/guide, vite.dev/config/build-options
  • Rolldown: "Plugin API" (generateBundle, OutputChunk) — rolldown.rs
  • Rollup: "Plugin Development → Build Hooks / Output Generation Hooks" — rollupjs.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Bundler nima: bog'liqlik grafi, entry, chunk va asset — IlmHamroh