CommonJS va ES Modules — require, import, export, farqlari
Muammodan boshlaylik
Siz katta loyiha yozyapsiz. Hamma kodni bitta faylga yozsangiz — 5000 qatorlik fayl. Qayerda nima borligini topish qiyin. Bir narsa o'zgarsa — hamma narsa sinishi mumkin.
Yechim — kodni modullarga bo'lish. Har bir modul o'z ishini qiladi. Kerak bo'lganda import qilinadi.
Node.js da buning ikki yo'li bor: CommonJS (eski, lekin hali keng tarqalgan) va ES Modules (zamonaviy standart). Ikkalasini bilmasangiz — boshqa odamning kodini o'qiyotganda chalkashib qolasiz.
CommonJS — Node.js ning asl modul tizimi
Node.js 2009 yilda yaratilganda brauzerda modul tizimi yo'q edi. Node.js o'zining modul tizimini yaratdi — CommonJS. Hozir ham ko'plab loyihalar, npm paketlar CommonJS ishlatadi.
Eksport qilish — module.exports
// hisob.js
function qoshish(a, b) {
return a + b;
}
function ayirish(a, b) {
return a - b;
}
function kopaytirish(a, b) {
return a * b;
}
module.exports = { qoshish, ayirish, kopaytirish };
module.exports — bu fayldan tashqariga chiqariladigan narsa. Boshqa fayl bu modulni import qilganda aynan module.exports da nima bo'lsa — shuni oladi.
Import qilish — require
// main.js
const hisob = require('./hisob');
console.log(hisob.qoshish(5, 3)); // 8
console.log(hisob.ayirish(10, 4)); // 6
console.log(hisob.kopaytirish(3, 7)); // 21
Yoki destructuring bilan:
const { qoshish, ayirish } = require('./hisob');
console.log(qoshish(5, 3)); // 8
console.log(ayirish(10, 4)); // 6
require qanday ishlaydi?
const hisob = require('./hisob');
Bu qator ishlayotganda Node.js quyidagilarni qiladi:
1. './hisob' ni qidiradi
→ './hisob.js' bormi? → ha → o'qiydi
→ './hisob/index.js' bormi? → ...
2. Faylni o'qiydi va bajaradi
3. module.exports ni qaytaradi
4. Natijani cache ga saqlaydi
→ Keyingi require('./hisob') — cache dan oladi
→ Fayl qayta o'qilmaydi!
Cache — muhim tushuncha. Bir modul bir marta yuklanadi. Ikkinchi marta require qilsangiz — xuddi shu ob'ekt qaytadi.
// a.js
const mod = require('./counter');
mod.count++;
// b.js
const mod = require('./counter');
console.log(mod.count); // a.js da oshirilgan qiymat!
// Chunki ikkalasi bir xil ob'ektni ko'rsatadi
Eksport qilishning boshqa usullari
// Usul 1 — to'liq ob'ekt
module.exports = { qoshish, ayirish };
// Usul 2 — bitta-bitta
module.exports.qoshish = function(a, b) { return a + b; };
module.exports.ayirish = function(a, b) { return a - b; };
// Usul 3 — exports (module.exports ning qisqartmasi)
exports.qoshish = function(a, b) { return a + b; };
// Usul 4 — bitta funksiya yoki class eksport
module.exports = function(a, b) { return a + b; };
// Ishlatish:
const qoshish = require('./hisob');
qoshish(5, 3); // 8
⚠️ Diqqat:
exportsvamodule.exportsfarqi bor.exports = { ... }deb yozsangiz — ishlamaydi!exportsfaqatmodule.exportsga property qo'shish uchun. To'liq ob'ekt berishdamodule.exports = { ... }ishlating.
ES Modules — zamonaviy standart
ES Modules (ESM) — 2015 yilda JavaScript standartiga kiritildi. Brauzer va Node.js ikkalasida ham ishlaydi. Node.js 12 dan boshlab to'liq qo'llab-quvvatlanadi.
Fayl kengaytmasi yoki package.json
Node.js da ESM ishlatish uchun ikki yo'l:
1. Fayl kengaytmasi: .mjs
hisob.mjs → ESM
2. package.json da:
{ "type": "module" }
→ barcha .js fayllar ESM bo'ladi
Eksport qilish — ikki tur
ESM da eksportning ikki turi bor. Bu farqni tushunmasdan import da chalkashib qolasiz.
Birinchi tur — nomli eksport. Bir moduldan xohlagancha narsa chiqarish mumkin. Har birining o'z nomi bor.
// hisob.js
export function qoshish(a, b) {
return a + b;
}
export function ayirish(a, b) {
return a - b;
}
export const PI = 3.14159;
Yoki oxirida bir joyda:
function qoshish(a, b) { return a + b; }
function ayirish(a, b) { return a - b; }
const PI = 3.14159;
export { qoshish, ayirish, PI };
Nomli eksportni import qilganda — nom aniq yozilishi shart, { } ichida:
import { qoshish, ayirish, PI } from './hisob.js';
// Nom noto'g'ri yozilsa — xato!
// import { qoshish2 } from './hisob.js' → xato
Ikkinchi tur — asosiy eksport (default). Har bir modulda faqat bitta bo'lishi mumkin. Modulning "bosh" narsasi.
// logger.js
export default function log(xabar) {
console.log(`[LOG] ${xabar}`);
}
default eksportni import qilganda — nom o'zingiz belgilaysiz, { } shart emas:
import log from './logger.js'; // "log" — o'zingiz qo'ydingiz
import xabarYoz from './logger.js'; // bu ham ishlaydi, xuddi shu funksiya
Ikkalasini birgalikda ishlatish:
// api.js
export const BASE_URL = 'https://api.example.com';
export const TIMEOUT = 5000;
export default class ApiClient {
get(url) { /* ... */ }
post(url, data) { /* ... */ }
}
import ApiClient, { BASE_URL, TIMEOUT } from './api.js';
// ↑ default ↑ nomli eksportlar
Qachon qaysinisini ishlatish kerak?
default export:
✓ Modulning asosiy maqsadi bitta narsa bo'lganda
✓ Class, bitta funksiya, bitta ob'ekt eksport qilganda
Misol: React komponenti, Express router, Mongoose model
Nomli export:
✓ Bir moduldan bir nechta narsa chiqarganda
✓ Utility funksiyalar, konstantalar
Misol: hisob.js, format.js, helpers.js
Import qilish — import
// main.js
// Nomli import — { } ichida, nom aniq bo'lishi shart
import { qoshish, ayirish } from './hisob.js';
// Default import — { } siz, nom o'zingiz belgilaysiz
import log from './logger.js';
// Ikkalasi birgalikda
import ApiClient, { BASE_URL, TIMEOUT } from './api.js';
// Hammasini bitta ob'ektga — namespace import
import * as hisob from './hisob.js';
// hisob.qoshish(5, 3)
// hisob.PI
console.log(qoshish(5, 3)); // 8
console.log(hisob.PI); // 3.14159
log('Dastur boshlandi'); // [LOG] Dastur boshlandi
import * as hisob — moduldan hamma narsani hisob ob'ektiga yuklaydi. Qachon foydali? Bir moduldan juda ko'p narsa import qilganda, yoki nom to'qnashuvi bo'lganda.
💡 ESM da fayl kengaytmasini yozish shart:
'./hisob.js'CommonJS da'./hisob'yetarli edi.
Dynamic import — kerak bo'lganda yuklash
// Faqat kerak bo'lganda yuklash
async function yuklash() {
const { qoshish } = await import('./hisob.js');
console.log(qoshish(5, 3));
}
yuklash();
Bu — katta loyihalarda performance uchun muhim. Hamma modulni boshida yuklamasdan, kerak bo'lganda yuklanadi.
CommonJS vs ES Modules — asosiy farqlar
| CommonJS | ES Modules | |
|---|---|---|
| Sintaksis | require / module.exports |
import / export |
| Yuklash | Sinxron (kutadi) | Asinxron (kutmaydi) |
| Qachon bajariladi | Runtime da | Parse vaqtida |
| Top-level await | Yo'q | Bor |
| Tree shaking | Qiyin | Oson |
| Brauzerda | Yo'q | Ha |
| Node.js da | Ha (default) | Ha (.mjs yoki "type":"module") |
Yuklash farqi — muhim
CommonJS sinxron yuklaydi. Ya'ni require chaqirilganda — fayl o'qilib, bajarilguncha keyingi qator kutadi.
// CommonJS — sinxron
const a = require('./a'); // kutadi
const b = require('./b'); // a tayyor bo'lgandan keyin
ES Modules asinxron yuklaydi. Barcha importlar parallel yuklanishi mumkin — bu tezroq.
// ESM — asinxron, parallel
import a from './a.js';
import b from './b.js';
// Ikkalasi parallel yuklanadi
Tree shaking
ESM da tree shaking ishlaydi — ishlatilmagan eksportlar bundle dan chiqarib tashlanadi. CommonJS da bu qiyin, chunki require dinamik — runtime da qaysi eksport kerakligini bilish qiyin.
// hisob.js — 3 ta funksiya eksport qiladi
export function qoshish(a, b) { return a + b; }
export function ayirish(a, b) { return a - b; }
export function bolish(a, b) { return a / b; }
// main.js — faqat qoshish ishlatiladi
import { qoshish } from './hisob.js';
// Tree shaking: ayirish va bolish bundle ga kirmaydi
Amaliy misol — modul tuzilmasi
Kichik loyihada modullarni qanday tashkil qilish:
my-project/
├── package.json
├── index.js
├── utils/
│ ├── hisob.js
│ ├── format.js
│ └── index.js ← barcha utils ni re-export qiladi
└── config/
└── index.js
// utils/hisob.js
export function qoshish(a, b) { return a + b; }
export function ayirish(a, b) { return a - b; }
// utils/format.js
export function pulFormat(son) {
return son.toLocaleString('uz-UZ') + " so'm";
}
// utils/index.js — barrel export
export { qoshish, ayirish } from './hisob.js';
export { pulFormat } from './format.js';
// index.js — faqat bitta joydan import
import { qoshish, pulFormat } from './utils/index.js';
console.log(qoshish(100, 200)); // 300
console.log(pulFormat(1500000)); // 1 500 000 so'm
Bu barrel export pattern — katta loyihalarda import larni tartibli saqlash uchun.
CommonJS va ESM ni aralashtirib ishlatish
Ba'zan CommonJS paketni ESM loyihada ishlatish kerak bo'ladi. Bu mumkin, lekin ehtiyot bo'lish kerak.
// ESM faylda CommonJS paketni import qilish
import express from 'express'; // ko'plab paketlar CJS, lekin bu ishlaydi
// CJS faylda ESM modulni import qilish — to'g'ridan-to'g'ri MUMKIN EMAS
// Yechim: dynamic import
async function main() {
const { default: chalk } = await import('chalk'); // ESM paketi
console.log(chalk.green('Yashil matn'));
}
⚠️ CJS faylda
importkalit so'zi ishlamaydi. ESM fayldarequireishlamaydi. Aralashtirishda dynamicimport()yordamga keladi.
Qaysi birini tanlash?
Yangi loyiha boshlayapsizmi? — ES Modules. Zamonaviy standart, brauzer va Node.js ikkalasida ishlaydi, tree shaking bor.
Mavjud Node.js loyiha? — CommonJS ni saqlab qoling. Hamma narsani ESM ga o'tkazish katta ish, foyda kam.
npm paket yozyapsizmi? — Ikkalasini ham qo'llab-quvvatlang (dual package). Ko'plab zamonaviy paketlar shunday qiladi.
Xulosa
CommonJS — Node.js ning asl modul tizimi. require va module.exports. Sinxron yuklaydi. Hali keng tarqalgan.
ES Modules — zamonaviy standart. import va export. Asinxron yuklaydi. Tree shaking ishlaydi. Brauzerda ham ishlaydi.
Ikkalasini bilish shart — chunki npm da ikkalasi ham uchraydi. Yangi loyihalarda ESM, eski loyihalarda CJS.
Keyingi dars
5-dars: package.json va npm — scripts, dependencies, npx