Javascript Modules Import Export Dynamic Import Structure
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama JavaScript untuk melanjutkan topik lainnya.
Kembali ke rujukan utama JavaScript untuk melanjutkan topik lainnya.
Module di JavaScript adalah unit kode yang bisa dipisahkan ke file lain lalu dipakai kembali lewat import dan export. Intinya sederhana: satu file tidak harus menampung semua hal. Saat aplikasi masih kecil, satu file besar sering terasa cepat. Saat proyek tumbuh, modularisasi membuat kode lebih mudah dibaca, diuji, dipindahkan, dan dirapikan.
Di praktik harian, module biasanya dipakai untuk:
Kalau diabaikan, file biasanya membesar tanpa arah: import saling silang, helper dipakai di mana-mana, dan perubahan kecil ikut memicu banyak file lain.
Pakai module saat satu file mulai punya lebih dari satu tanggung jawab yang jelas, misalnya:
Module juga berguna ketika kamu ingin memisahkan layer berdasarkan peran:
Prinsipnya bukan “semua harus dipisah”. Prinsipnya: pisahkan kalau ada batas tanggung jawab yang jelas, atau kalau file mulai sulit dipelihara.
JavaScript module memakai dua arah utama:
export untuk mengeluarkan nilai dari sebuah fileimport untuk memakai nilai itu di file lainNamed export cocok untuk banyak hal dalam satu file.
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}Lalu dipakai seperti ini:
import { PI, add } from './math.js';Ciri utama named export:
Kalau ingin mengganti nama saat import:
import { add as sum } from './math.js';Ini berguna kalau nama lokal bentrok, atau kamu ingin nama yang lebih jelas di context pemakaian.
Default export cocok saat file memang punya satu nilai utama.
// formatDate.js
export default function formatDate(date) {
return new Intl.DateTimeFormat('id-ID').format(date);
}Pemakaian:
import formatDate from './formatDate.js';Ciri utama default export:
| Aspek | Named export | Default export |
|---|---|---|
| Jumlah per file | banyak | satu |
| Cara import | pakai kurung kurawal | tanpa kurung kurawal |
| Nama import | harus cocok dengan export | bisa bebas |
| Cocok untuk | utility, konstanta, beberapa fungsi | satu entitas utama |
| Dampak refactor | lebih eksplisit | lebih fleksibel, tapi bisa kurang seragam |
Secara referensial, named export sering lebih enak untuk codebase besar karena lebih jelas saat dibaca dan lebih sulit “disalahpahami”. Default export lebih nyaman saat file memang hanya punya satu tanggung jawab utama.
Pilih named export kalau:
Contoh yang sehat:
// validators.js
export function isEmail(value) {
return /@/.test(value);
}
export function isRequired(value) {
return value != null && value !== '';
Pemakaian:
import { isEmail, isRequired } from './validators.js';Di proyek besar, pola ini biasanya lebih mudah dirawat karena pembaca langsung tahu dependency apa yang dipakai.
Pilih default export kalau:
Contoh:
// UserCard.jsx
export default function UserCard({ user }) {
return <div>{user.name}</div>;
}Pemakaian:
import UserCard from './UserCard.jsx';Namun default export perlu disiplin. Kalau satu folder berisi banyak default export yang namanya mirip, orang bisa lebih mudah tersesat saat tracing dependency.
Dynamic import dipakai saat module tidak perlu dimuat sejak awal. Bentuknya memakai import() dan mengembalikan Promise.
async function loadChart() {
const module = await import('./chart.js');
module.renderChart();
}Kalau module punya default export:
async function loadFormatter() {
const { default: formatDate } = await import('./formatDate.js');
console.log(formatDate(new Date()));
}Dynamic import cocok untuk:
Contoh yang realistis:
Dynamic import membantu menunda biaya loading. Ini tidak selalu membuat aplikasi “lebih cepat” secara keseluruhan, tapi bisa membuat pengalaman awal lebih ringan.
Yang diuntungkan biasanya:
Module yang sehat bukan cuma soal memindahkan kode ke file lain. Yang lebih penting adalah merapikan arah dependency.
Idealnya, layer atas memakai layer bawah, bukan sebaliknya.
Contoh arah yang sehat:
Kalau arah dependency bolak-balik, tanda pertamanya biasanya file mulai saling import dalam lingkaran.
Jangan cuma membuat folder utils/ besar lalu menumpuk semua hal di sana. Itu sering berubah jadi gudang sampah terselubung.
Lebih sehat kalau pemisahannya mengikuti peran:
features/auth/features/profile/shared/format/shared/http/entities/user/Dengan begitu, lokasi file ikut menjelaskan siapa yang boleh memakai apa.
Satu module sebaiknya punya permukaan API yang kecil. Kalau sebuah file mengekspos terlalu banyak hal, pemakai jadi tergoda mengambil semuanya.
Contoh kurang sehat:
import { a, b, c, d, e } from './big-module.js';Kalau pola ini sering terjadi, biasanya module itu sudah terlalu gemuk.
Contoh masalah umum:
Kalau helper kecil butuh tahu terlalu banyak hal di atasnya, struktur biasanya mulai retak.
Diagram berikut menunjukkan alur dependency yang sehat: layer atas memakai layer bawah, shared module dipakai lintas fitur, dan module berat dimuat belakangan dengan import().
Bacanya begini:
Pemecahan file yang sehat biasanya mengikuti aturan sederhana:
constants.js untuk nilai tetap yang dipakai lintas filehelpers.js untuk fungsi kecil yang benar-benar umumformatters.js untuk transformasi presentasivalidators.js untuk validasi yang bisa dipakai ulangservice.js untuk orkestrasi logicrepository.js untuk akses data eksternalContoh struktur yang lebih rapi:
features/
auth/
login-form.jsx
auth.service.js
auth.validators.js
profile/
profile-page.jsx
profile.service.js
shared/
format/
format-date.js
http/
api-client.jsStruktur seperti ini membantu pembaca menebak lokasi file tanpa harus membuka semua folder.
Circular dependency terjadi saat A mengimpor B, lalu B mengimpor A, langsung atau tidak langsung.
Akibatnya bisa macam-macam:
Gejala yang sering muncul:
undefinedBarrel export biasanya memakai file indeks untuk re-export banyak module.
Contoh:
// index.js
export * from './validators.js';
export * from './formatters.js';Ini berguna, tapi kalau terlalu agresif bisa menyembunyikan asal dependency dan memperbesar risiko circular dependency.
Pakai barrel export secukupnya. Kalau semua hal selalu lewat satu pintu, tracing dependency bisa jadi susah.
Folder utils/ sering dimulai dengan niat baik, lalu berakhir jadi campur aduk:
Kalau isinya terlalu umum, lebih baik pecah sesuai domain atau fitur.
Default export memang praktis, tapi kalau dipakai tanpa pola yang seragam, hasilnya sering membingungkan.
Masalah yang sering muncul:
File besar bukan masalah utama. File besar yang campur tanggung jawab itu masalah.
Tanda file sudah terlalu gemuk:
// string.js
export function trimLower(value) {
return String(value).trim().toLowerCase();
}
export function capitalize(value) {
const text = String(value);
return text.charAt
// Button.jsx
export default function Button({ children, ...props }) {
return <button {...props}>{children}</button>;
}async function openEditor() {
const { default: Editor } = await import('./Editor.jsx');
return Editor;
}utils/ menjadi tempat buanganKalau mau pakai module dengan sehat, pegang urutan ini:
Kalau modul membuat kode lebih mudah di-scan, lebih mudah diuji, dan lebih mudah diubah, berarti struktur itu sudah bekerja.