☀️Siang
Web Development

JavaScript ES Modules: Import, Export & Tree Shaking

Tutorial lengkap ES Modules — named exports, default exports, dynamic imports, top-level await, re-exports, tree shaking, dan best practices modular JavaScript

Artikel: Javascript Es Modules Artikel: Javascript Es Modules


1. Pengenalan ES Modules

ES Modules (ESM) adalah sistem modul resmi yang diperkenalkan dalam spesifikasi ECMAScript 2015 (ES6). ESM memungkinkan Anda memecah kode JavaScript menjadi file-file terpisah yang saling terhubung melalui mekanisme import dan export. Ini adalah standar modern yang didukung oleh semua browser utama dan Node.js.

Sebelum ESM, JavaScript tidak memiliki sistem modul bawaan di browser. Pengembang mengandalkan pola seperti IIFE (Immediately Invoked Function Expressions), CommonJS (Node.js), atau AMD (RequireJS). ESM menyatukan semuanya menjadi standar yang konsisten.

Mengapa ES Modules Penting?

Keunggulan Penjelasan
ModularitasKode terorganisir dalam file terpisah, mudah dikelola dan dipelihara
EncapsulationSetiap modul memiliki scope sendiri, variabel tidak bocor ke global
Tree ShakingBundler bisa menghapus kode yang tidak digunakan untuk mengurangi ukuran bundle
Static AnalysisStruktur import/export bisa dianalisis saat compile time
Browser NativeDidukung langsung oleh browser modern tanpa perlu bundler
Async LoadingDynamic import memungkinkan lazy loading modul secara asynchronous

ES Modules vs CommonJS vs AMD

Aspek ES Modules CommonJS AMD
Sintaksimport/exportrequire/module.exportsdefine/require
LoadingStatic & AsyncSynchronousAsynchronous
EnvironmentBrowser & Node.jsNode.js (server)Browser
Tree Shaking✅ Ya❌ Tidak❌ Tidak
Top-level Await✅ Ya❌ Tidak❌ Tidak
Strict ModeOtomatisManualManual
Diagram: Arsitektur ES Modules

Build Tool (Vite/webpack) • Resolusi dependency...

components.js export default class Komponen imp...

main.js import ( fungsi) import default

utils.js export ( fungsi) → import

Setup Awal

Untuk menggunakan ES Modules di browser, tambahkan atribut type="module" pada tag script di HTML:

Diagram

main.js (100 KB)

Laporan (+PDF) (200KB)

Dash- board (80KB)

Profil (40KB)

Settin gs (30KB)



7. Top-Level Await

Top-level await memungkinkan Anda menggunakan await di luar fungsi async, langsung pada level modul. Fitur ini tersedia di ES2022 dan sangat berguna untuk inisialisasi modul yang membutuhkan operasi asynchronous.

Penggunaan Top-Level Await

Diagram
1. Static analysis: deteksi export/import 2. Ma...

✅ formatRupiah ✅ validateEmail



9. Best Practices ES Modules

Berikut adalah panduan dan best practices untuk menggunakan ES Modules secara efektif dalam proyek JavaScript modern.

Organisasi File & Struktur Modul

File Structure — Best Practices
Diagram: src/
src/
/"features/"/
auth/
/"AuthContext.js"/
/"LoginForm.js"/
/"useAuth.js"/
/"authApi.js"/
/"index.js"/
/"dashboard/"/
/"DashboardPage.js"/
/"DashboardCharts.js"/
/"index.js"/
/"profile/"/
/"ProfilePage.js"/
/"ProfileForm.js"/
/"index.js"/
shared/
/"components/"/
/"Button.js"/
/"Modal.js"/
/"index.js"/
hooks/
/"useLocalStorage.js"/
/"useDebounce.js"/
/"index.js"/
utils/
/"formatters.js"/
/"validators.js"/
/"index.js"/
app.js
/"routes.js"/

Aturan Penting ES Modules

JavaScript — Aturan & Pola Umum
// ====== 1. SELALU gunakan file extension (.js) ======
// Beberapa bundler membutuhkan eksplicit extension
import { helper } from './utils/helper.js';  // ✅
import { helper } from './utils/helper';      // ❌ bisa error di Deno/native ESM

// ====== 2. Hindari circular dependencies ======
// a.js mengimpor b.js, b.js mengimpor a.js → masalah!
// Solusi: pindahkan shared code ke modul ketiga (c.js)

// ====== 3. Satu ekspor per file (Single Responsibility) ======
// ✅ Bagus: file fokus pada satu hal
// UserService.js — hanya tentang User Service
export class UserService {
  async getUser(id) { /* ... */ }
  async updateUser(id, data) { /* ... */ }
  async deleteUser(id) { /* ... */ }
}

// ❌ Kurang baik: file menangani banyak hal berbeda
// utils.js — terlalu banyak ekspor berbeda
export function formatRupiah() { /* ... */ }
export function validateEmail() { /* ... */ }
export function sendNotification() { /* ... */ }
export class DatabaseService { /* ... */ }
export const API_URL = '...';

// ====== 4. Gunakan constants terpisah ======
// constants/routes.js
export const ROUTES = {
  HOME: '/',
  LOGIN: '/login',
  DASHBOARD: '/dashboard',
  PROFILE: '/profile',
  SETTINGS: '/settings',
};

export const API_ENDPOINTS = {
  USERS: '/api/users',
  PRODUCTS: '/api/products',
  ORDERS: '/api/orders',
};

// ====== 5. Type exports (untuk TypeScript/JSDoc) ======
/** @typedef {{ id: number, nama: string, email: string }} User */

// Export type sebagai value (untuk JSDoc runtime checking)
export const UserType = Object.freeze({
  id: 'number',
  nama: 'string',
  email: 'string',
});

Anti-Pattern yang Harus Dihindari

JavaScript — Anti-Patterns
// ❌ 1. Mutable exports — bisa menyebabkan bug tak terduga
export let counter = 0;
export function increment() { counter++; }
// Importer mendapat binding langsung, bukan copy!

// ✅ Solusi: Gunakan getter pattern
let _counter = 0;
export function getCounter() { return _counter; }
export function increment() { _counter++; }

// ❌ 2. Import dengan side effects yang tidak disadari
import './analytics'; // Apa yang dilakukan modul ini?

// ✅ Solusi: Jelaskan mengapa Anda mengimpor side-effect module
// Load analytics untuk tracking page views
import './analytics';

// ❌ 3. Deep import dari internal package
import helper from 'some-package/lib/internal/utils/helper.js';

// ✅ Solusi: Gunakan public API
import { helper } from 'some-package';

// ❌ 4. Barrel file yang terlalu besar
// components/index.js — re-export 100+ komponen!
export { Button } from './Button';
export { Modal } from './Modal';
// ... 98 lagi

// ✅ Solusi: Sub-barrel files
// components/forms/index.js — hanya form components
// components/layout/index.js — hanya layout components

// ❌ 5. Mix CommonJS dan ESM secara acak
const fs = require('fs');         // CommonJS
import path from 'path';          // ESM
// Di Node.js, ini bisa menyebabkan error!

// ✅ Solusi: Pilih satu, konsisten
import fs from 'node:fs/promises'; // ESM sepanjang jalan
import path from 'node:path';


10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang ES Modules:

Pertanyaan 1: Apa yang membedakan named export dari default export?

a) Named export bisa banyak per file, default export hanya satu per file
b) Default export lebih cepat daripada named export
c) Named export hanya untuk fungsi, default export hanya untuk class
d) Tidak ada perbedaan, hanya sintaks berbeda

Pertanyaan 2: Apa keuntungan utama dynamic import (import())?

a) Kode berjalan lebih cepat
b) Memungkinkan code splitting dan lazy loading modul
c) Menghilangkan kebutuhan akan bundler
d) Menggantikan static import sepenuhnya

Pertanyaan 3: Apa itu tree shaking dalam konteks ES Modules?

a) Proses menghapus komentar dari kode
b) Proses mengompresi file menjadi format binary
c) Teknik menghapus kode yang tidak digunakan dari bundle
d) Teknik untuk mengacak kode agar tidak bisa dibaca

Pertanyaan 4: Apa yang terjadi saat top-level await digunakan dalam sebuah modul?

a) Modul akan error karena await hanya bisa di dalam async function
b) Modul menunggu operasi selesai dan memblokir modul pengimpor
c) Modul langsung diekspor tanpa menunggu
d) Browser akan me-refresh halaman otomatis

Pertanyaan 5: Bagaimana cara mengimpor semua named exports dari modul sebagai satu objek?

a) import all from './module.js'
b) import { * } from './module.js'
c) import * as nama from './module.js'
d) import everything './module.js'
🔍 Zoom
100%
🎨 Tema