Series: JavaScript Basics Lesson 98

Import yo'llari

Relative va absolute/alias import yo'llari farqi, chuqur papka strukturasida ../../../../ muammosi, Path Alias yechimi (tsconfig/jsconfig va bundler resolve.alias), Node.js module resolution algoritmi, bundler'da build vaqtida alias hal qilinishi, va circular import xavfi.

98-dars

Import yo'llari

Import yo'li — bu shunchaki "kerakli fayl qayerda ekanini ko'rsatuvchi manzil":

import Button from './Button';

'./Button' — bu manzil. Xuddi pochta manzili kabi: "shu yerdan, shu tomonga bor, shu faylni top".


1. Ikki xil yo'l turi bor

// 1) Relative (nisbiy) — HOZIRGI fayldan hisoblanadi
import Button from './Button';
import Header from '../shared/Header';
import formatDate from '../../utils/formatDate';

// 2) Absolute/Alias (mutlaq) — loyiha ILDIZIDAN hisoblanadi
import Button from '@shared/components/Button';
import formatDate from '@shared/utils/formatDate';

Farqi: relative yo'l — "hozir turgan joyimdan necha qadam yurish kerak" deydi (./ — shu papka, ../ — bir qadam yuqoriga). Absolute/alias — "loyihaning boshidan boshlab, to'g'ridan-to'g'ri manzil" deydi, hozir qayerda turganingdan qat'i nazar.


2. Muammo qanday ko'rinadi — aniq misol

Tasavvur qiling, struktura shunday:

src/
  shared/
    components/
      Button.jsx
      Card.jsx
    utils/
      formatDate.js
  features/
    user/
      profile/
        settings/
          NotificationSettings.jsx   ← BU YERDAN import qilamiz

NotificationSettings.jsx ichida Buttonni ishlatish kerak:

import Button from '../../../../shared/components/Button';

Nega 4 ta ../? Chunki settings/ → profile/ → user/ → features/ — to'rt qadam yuqoriga chiqib, keyin pastga shared/components/ga tushish kerak. Buni qo'lda hisoblash — o'zi xato qilish oson (bitta ../ kamroq yoki ko'proq yozib qo'yish — juda oson xato).


3. Nega bu muammo — 3 ta aniq sabab

Sabab 1 — fayl ko'chirilsa, hammasi buziladi

NotificationSettings.jsxni settings/ papkasidan notifications/ papkasiga ko'chirsangiz:

Eski:  ../../../../shared/components/Button  (4 qadam)
Yangi: ../../../shared/components/Button      (3 qadam kerak, lekin eski qoladi!)

IDE avtomatik to'g'irlamasa (ba'zan to'g'irlaydi, ba'zan yo'q, ayniqsa boshqa fayldan import qilingan bo'lsa), kod ishlamay qoladi — va xato xabari ko'pincha aniq bo'lmaydi (Module not found), qaysi faylda ekanini qidirib yurishga to'g'ri keladi.

Sabab 2 — o'qish qiyin

import { validateEmail } from '../../../../shared/utils/validation';

Shu qatorni o'qib, darhol "qaysi modul import qilinyapti" deb tushunish qiyin — miya avval ../../../../ni "hisoblab", keyin nima ekanini tushunishi kerak. Bu — kichik, lekin doimiy takrorlanadigan aqliy yuk.

Sabab 3 — copy-paste qilganda buziladi

Kod bo'lagini bir fayldan ikkinchisiga ko'chirib qo'ysangiz (masalan, komponentni boshqa feature'ga ko'chirish), relative importlar yangi joyda noto'g'ri bo'lib qoladi — chunki ular eski joyga nisbatan hisoblangan edi.


4. Yechim — Path Alias

JavaScript loyihalarda (jsconfig.json) yoki TypeScript'da (tsconfig.json)

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@shared/*": ["src/shared/*"],
      "@features/*": ["src/features/*"]
    }
  }
}

Endi:

import Button from '@shared/components/Button';

Bu — qayerdan import qilinishidan qat'i nazar bir xil ko'rinadi. NotificationSettings.jsxdan ham, App.jsxdan ham — bir xil qator.

Muhim — bu ikki xil joyda sozlanishi kerak (ko'p odam shu yerda adashadi)

tsconfig.json/jsconfig.jsondagi paths — bu faqat IDE va TypeScript kompilyatori uchun. Lekin bundler (Webpack, Vite) — bu faylni o'qimaydi, uning o'z sozlamasi bor:

// vite.config.js
import path from 'path';

export default {
  resolve: {
    alias: {
      '@shared': path.resolve(__dirname, 'src/shared'),
      '@features': path.resolve(__dirname, 'src/features'),
    }
  }
};
// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      '@shared': path.resolve(__dirname, 'src/shared'),
      '@features': path.resolve(__dirname, 'src/features'),
    }
  }
};

Agar faqat tsconfig.jsonni sozlab, bundlerni sozlamasangiz: IDE'da hammasi to'g'ri ko'rinadi (qizil chiziq yo'q, avtomat to'ldirish ishlaydi), lekin build qilganda xato chiqadi — Module not found: '@shared/components/Button'. Bu — boshlang'ichlar eng ko'p tushib qoladigan tuzoq.


5. V8/Node.js darajasida — import yo'li aslida qanday "hal qilinadi" (module resolution)

Bu — chuqurroq qism. Qachonki siz import X from '@shared/Button' yozsangiz, kimdir bu satrni haqiqiy fayl yo'liga aylantirishi kerak. Bu jarayon — module resolution deyiladi.

Node.js'da (server tomonida, alias'siz, oddiy holat)

Node.js'ning o'zining qat'iy algoritmi bor — require('./foo') yozilsa:

  1. Avval ./foo.js bormi — tekshiradi
  2. Bo'lmasa, ./foo.json bormi
  3. Bo'lmasa, ./foo/index.js bormi (agar foo — papka bo'lsa)
  4. Bo'lmasa — xato
require('./utils');
// Node ichida qidirish tartibi:
// 1. ./utils.js       ← topilsa, shu ishlatiladi
// 2. ./utils.json
// 3. ./utils/index.js

Bu — fayl tizimiga haqiqiy so'rov (I/O operatsiyasi) demakdir — Node har safar diskdan "bu fayl bormi" deb tekshiradi. Katta loyihada, minglab require/import bo'lsa, bu qidiruvlar yig'ilib, ishga tushish vaqtiga (startup time) qo'shiladi.

Bundler'da (Webpack/Vite — brauzer uchun paketlashda)

Bundler bundan-da murakkabroq ishlaydi: u butun loyihani oldindan tahlil qiladi (build vaqtida), har bir importni statik o'qib, qaysi fayl ekanini aniqlaydi, va aliaslarni ham shu yerda "haqiqiy yo'lga" almashtiradi:

import Button from '@shared/components/Button';
        │
        ▼ (build vaqtida, bundler resolve.alias orqali)
import Button from '/Users/.../src/shared/components/Button.jsx';
        │
        ▼ (bundler bu faylni o'qiydi, kodini "bog'lam" ichiga joylaydi)

Muhim farq: bu runtimeda (brauzerda dastur ishlab turganda) sodir bo'lmaydi — bu build vaqtida, oldindan hal qilinadi. Ya'ni foydalanuvchi brauzerida @shared/... degan satr umuman yo'q — u allaqachon haqiqiy fayl kontenti bilan almashtirilgan bo'ladi. Shuning uchun alias ishlatish — runtime performance'ga hech qanday salbiy ta'sir qilmaydi, bu faqat dasturchi qulayligi uchun, build jarayonining bir qismi.


6. Circular import — import yo'llari bilan bog'liq yashirin xavf

Bu — struktura noto'g'ri bo'lganda ko'p chiqadigan muammo: ikki fayl bir-birini import qilsa.

// user.js
import { getOrders } from './order';
export function getUser() { return { orders: getOrders() }; }

// order.js
import { getUser } from './user';
export function getOrders() { return { owner: getUser() }; }

Nima bo'ladi? JS moduli birinchi marta yuklanganda, uning kodi yuqoridan pastga bajariladi. user.js yuklanayotganda order.jsni import qiladi, order.js esa qaytadan user.jsni import qilmoqchi bo'ladi — lekin user.js hali to'liq yuklanib bo'lmagan (u hozir aynan shu jarayonda "to'xtab turibdi"). Natijada order.js ichida getUser — undefined bo'lib chiqishi mumkin.

Bu — nima uchun feature-based struktura foydali ekanini yana bir bor tasdiqlaydi: agar user va order alohida feature papkalarida bo'lsa, va ular faqat shared/ga bog'liq bo'lsa (bir-biriga emas), circular import fizik jihatdan yuzaga kelishi qiyinlashadi — chunki struktura o'zi "bog'liqlik yo'nalishini" bir tomonlama qilib qo'yadi.