Seriya: JavaScript Basics Dars 77

Minification

Minification jarayoni: Tree Shaking'dan farqi, minifier'ning AST orqali ishlashi (mangling, constant folding, DCE), Gzip/Brotli bilan minification farqi, va V8'ning parse vaqtiga ta'siri haqiqatda qanday ekani.

77-dars

Minification

Minification — kodning bajarilishiga hech qanday ta'sir qilmasdan, uning hajmini kichraytirish jarayoni: keraksiz bo'shliqlar, izohlar, uzun o'zgaruvchi nomlari olib tashlanadi/qisqartiriladi.

// Minifikatsiyadan OLDIN (real loyihada minglab qator)
function calculateTotalPrice(price, taxRate) {
  // Soliqni qo'shib umumiy narxni hisoblaymiz
  const total = price + (price * taxRate);
  return total;
}
// Minifikatsiyadan KEYIN
function calculateTotalPrice(n,t){return n+n*t}

Diqqat qiling: price → n, taxRate → tga aylandi, izoh yo'q qilindi, bo'shliqlar olib tashlandi, hatto oraliq total o'zgaruvchisi ham butunlay yo'qotildi (chunki u faqat bir marta ishlatilib, keyin qaytarilgan — natijani to'g'ridan-to'g'ri hisoblash mumkin).


1. Nega Tree shaking'dan farq qiladi — chalkashtirmaslik uchun

Bu ikkalasi ko'pincha bitta vosita (Terser, esbuild) orqali, bitta build bosqichida ishlaydi, shuning uchun chalkashib ketadi:

Tree Shaking Minification
Nima qiladi Ishlatilmagan kodni butunlay o'chiradi Qolgan (kerakli) kodni qisqartiradi
Mantiqiy o'zgarish Dastur mantig'idan kod yo'qoladi Dastur mantig'i bir xil qoladi, faqat matn shakli o'zgaradi
Misol unused() funksiyasi butunlay yo'qoladi function add(a,b){return a+b} qisqaroq yoziladi

Ya'ni: avval tree shaking "kerak bo'lmagan butun bloklarni" kesib tashlaydi, keyin minification "qolgan har bir belgini" siqadi.


2. Minifier (masalan, Terser) qanday ishlaydi — bosqichma-bosqich

Bu yerda muhim narsa: minifier kodni matn sifatida regex bilan o'zgartirmaydi (bu juda xavfli bo'lardi — string ichidagi bo'shliqni ham o'chirib qo'yishi mumkin). Buning o'rniga, u to'liq compiler pipeline'dan foydalanadi — xuddi V8'ning o'zi ichida bo'lgani kabi:

1-bosqich: Parsing → AST (Abstract Syntax Tree)

Kod avval tokenlarga (lekser), keyin daraxt tuzilmasiga (parser) aylantiriladi — bu aynan V8'ning Ignition bosqichida qilgan ishiga o'xshash. Masalan:

function add(a, b) { return a + b; }

AST ko'rinishida (soddalashtirilgan):

FunctionDeclaration
  name: "add"
  params: [Identifier "a", Identifier "b"]
  body: ReturnStatement
    argument: BinaryExpression "+"
      left: Identifier "a"
      right: Identifier "b"

2-bosqich: Transformatsiyalar (AST darajasida)

Minifier AST ustida bir qancha xavfsiz o'zgartirishlarni amalga oshiradi:

Nomlarni qisqartirish (mangling) — lokal o'zgaruvchilar va parametrlar a, b, c, ... kabi bir harfli nomlarga almashtiriladi. Bu faqat lokal scope'dagi nomlarga tegishli (closure orqali tashqaridan ko'rinadigan yoki global nomlarga tegilmaydi — aks holda kod buzilib qolardi).

function calculateTotalPrice(price, taxRate) { /* ... */ }
// →
function calculateTotalPrice(n, t) { /* ... */ }  // funksiya nomi qoladi (export bo'lishi mumkin), parametrlar qisqaradi

O'lik kodni yo'q qilish (DCE, funksiya ichida) — if (false) {...}, ishlatilmagan lokal o'zgaruvchilar olib tashlanadi (bu — tree shaking'ning modul darajasida emas, funksiya ichidagi kichik versiyasi).

Konstant ifodalarni oldindan hisoblash (constant folding):

const SECONDS_IN_DAY = 60 * 60 * 24;
// →
const SECONDS_IN_DAY = 86400; // build vaqtida hisoblab qo'yiladi

Qisqaroq ekvivalent sintaksisga almashtirish:

if (x === true) { return true; } else { return false; }
// →
return x === true;

3-bosqich: Generatsiya (Codegen)

Optimallashtirilgan AST qaytadan **matn (kod)**ga aylantiriladi — bu safar minimal bo'shliq, qisqa nomlar bilan, yangi qator belgilari ham deyarli olib tashlanadi (; bilan ajratilgan bitta uzun qator).


3. Bu jarayon Tree Shaking bilan qanday birgalikda ishlaydi (real pipeline)

Zamonaviy build jarayonida ketma-ketlik odatda shunday:

1. Bundling         — barcha modullar bitta grafga yig'iladi
2. Tree Shaking     — ishlatilmagan export/importlar o'chiriladi
3. Minification     — qolgan kod qisqartiriladi (nomlar, bo'shliqlar, constant folding)
4. (ixtiyoriy) Compression — Gzip/Brotli orqali tarmoq orqali yuborishda yana siqiladi

Gzip/Brotli bilan farqi — bu muhim, chunki ko'pincha aralashtiriladi: Minification — fayl mazmunini o'zgartiradi (build vaqtida, bir marta), Gzip/Brotli esa transport siqish — server fayl yuborayotganda runtime'da siqadi, brauzer qabul qilib avtomatik ochadi (decompress), fayl diskda/keshda siqilmagan holatda turadi. Ikkalasi birga ishlatilganda eng katta yutuq bo'ladi: minifikatsiya qilingan kod Gzip orqali yana **qo'shimcha 60-70%**gacha kichrayishi mumkin (chunki qisqa, takrorlanuvchi nomlar Gzip'ning algoritmi uchun yanada samarali siqiladi).


4. V8 darajasida — bu Parse/Compile vaqtiga qanday ta'sir qiladi

Bu yerda ikkita alohida ta'sirni ajratish kerak:

  1. Tarmoq orqali yuklash vaqti — eng katta yutuq shu yerda. Kichikroq fayl = tezroq yuklanadi, ayniqsa sekin tarmoqlarda.
  2. V8'ning parse vaqti — bu yerda kutilmagan nozik nuqta bor: minifikatsiya V8'ning parse tezligiga sezilarli darajada katta ta'sir qilmaydi, chunki V8 parser baribir belgi-belgi (character by character) ishlaydi — qisqa nom (n) yoki uzun nom (taxRate) parse qilish uchun deyarli bir xil vaqt ketadi (farq — necha baytni o'qish, bu mikrosekundlar darajasida). Asosiy yutuq bayt hajmi kamayishidan (tarmoq + xotira), parse algoritmik tezligidan emas.

Biroq, bilvosita foyda bor: kichikroq fayl V8'ning lazy parsing mexanizmiga (Code splitting mavzusida eslatilgan) yordam beradi — kichikroq funksiyalar V8'ning ichki heuristikasida tezroq "to'liq parse qilishga arziydi" deb baholanishi mumkin, lekin bu — ikkinchi darajali effekt, minifikatsiyaning asosiy maqsadi emas.


Amaliy vositalar

  • Terser — hozirgi standart JS minifier (Webpack'da default, UglifyJS'ning vorisi — UglifyJS ES6+ sintaksisni to'liq qo'llab-quvvatlamagani uchun almashtirilgan).
  • esbuild — Go tilida yozilgan, minifikatsiyani o'nlab marta tezroq bajaradi.
  • SWC — Rust'da yozilgan, Next.js'da default ishlatiladigan minifier/compiler.