Dalam dunia pengembangan web modern, kebutuhan untuk menulis kode yang rapi, mudah dipelihara, serta dapat digunakan kembali semakin penting. JavaScript yang awalnya hanya dipakai untuk script kecil di dalam halaman HTML kini berkembang menjadi bahasa utama untuk aplikasi berskala besar—baik di browser maupun server. Perkembangan ini membawa tantangan baru: bagaimana mengelola kode agar tetap terstruktur dan tidak berantakan?
Di sinilah konsep modular JavaScript hadir sebagai solusi. Modular JavaScript memungkinkan developer memecah kode menjadi file-file kecil yang berdiri sendiri dan memiliki tanggung jawab jelas. Dengan modul, kita tidak lagi menempatkan seluruh logika dalam satu file besar, tetapi memilahnya berdasarkan fungsi, fitur, atau kategori tertentu. Hasilnya adalah kode yang jauh lebih bersih, mudah dibaca, dan dapat diperluas tanpa merusak keseluruhan aplikasi.
Daftar isi
Baca juga: Regular Expression (Regex) JavaScript Lengkap
Kenapa Modul Penting di Era Development Modern
Saat membangun aplikasi modern seperti dashboard interaktif, aplikasi real-time, sistem e-commerce, atau SPA (Single Page Application), ukuran kode JavaScript bisa dengan mudah mencapai ribuan baris. Jika semuanya berada dalam satu file, sangat sulit untuk:
- Menemukan bug
- Memperbaiki fitur tertentu
- Menambahkan fitur baru
- Bekerja dalam tim dengan banyak developer
Modul memberikan isolasi pada setiap bagian kode. Artinya, setiap file hanya fokus pada satu hal (single responsibility principle). Dengan adanya fitur import dan export, modul dapat saling terhubung tanpa kehilangan integritas masing-masing. Modul juga memudahkan pengembang untuk membagi pekerjaan, mengurangi konflik kode, dan mempercepat workflow.
Di sisi build tool modern seperti Webpack, Vite, dan Rollup, modul juga mendukung optimasi otomatis seperti tree-shaking, yaitu membuang kode yang tidak digunakan sehingga aplikasi menjadi lebih cepat dan ringan.
Hubungan Modularisasi dengan Maintainability dan Scalability
Modularisasi bukan sekadar memecah file — modularisasi adalah fondasi dari kode yang maintainable (mudah dirawat) dan scalable (mudah diperluas).
Berikut hubungannya:
1. Maintainability (Kemudahan Pemeliharaan)
Dengan modul:
- Kode lebih mudah dibaca karena terpisah berdasarkan fungsi
- Debugging lebih cepat karena kita tahu lokasi setiap bagian logika
- Refactoring tidak berisiko merusak bagian lain
- Developer baru lebih mudah memahami struktur aplikasi
Ini sangat krusial untuk proses development jangka panjang.
2. Scalability (Kemampuan Mengembangkan Aplikasi Besar)
Saat aplikasi tumbuh, modul:
- Memastikan setiap fitur tetap mandiri
- Memudahkan penambahan fitur baru tanpa memengaruhi modul lain
- Memungkinkan pemecahan kode menjadi chunk untuk performa lebih cepat
- Membuka peluang untuk mengintegrasikan berbagai tools dan optimasi
Modularisasi adalah pilar penting yang memungkinkan suatu aplikasi JavaScript berkembang dari proyek kecil menjadi sistem besar tanpa kehilangan kestabilan.
Pengenalan Modular JavaScript
Modular JavaScript adalah pendekatan penulisan kode JavaScript di mana sebuah aplikasi dibangun dengan cara memecah logika ke dalam bagian-bagian kecil yang disebut module. Setiap module memiliki tugas, fungsi, dan tanggung jawab yang jelas—mulai dari mengelola data, menangani UI, hingga memproses logika tertentu.
Dalam JavaScript modern (ES6+), module memungkinkan kita untuk:
- Menyimpan kode dalam file terpisah
- Meng-export fungsi, variabel, atau class agar bisa digunakan di file lain
- Meng-import bagian yang dibutuhkan tanpa membawa keseluruhan file
- Menjaga struktur kode tetap bersih, mudah di-maintain, dan mudah dikembangkan
Dengan kata lain, modular JavaScript membuat proyek yang kompleks menjadi lebih terorganisir, reusable, scalable, dan mudah diuji.
Kenapa Modularisasi Dibutuhkan dalam Aplikasi Modern
Aplikasi web zaman sekarang jauh lebih kompleks dibanding aplikasi web satu dekade lalu. Hampir semua website modern menjalankan banyak fitur seperti autentikasi, interaksi real-time, manajemen state, hingga integrasi API eksternal. Mengelola semua fitur tersebut dalam satu file JavaScript besar akan sangat berisiko.
Beberapa alasan kenapa modularisasi menjadi kebutuhan wajib:
1. Membuat Kode Lebih Terstruktur
Dengan modularisasi, kode disusun berdasarkan peran (misal: module untuk API, module utilitas, module UI). Ini membuat alur kerja developer jauh lebih jelas.
2. Skalabilitas Aplikasi
Ketika aplikasi tumbuh, module dapat ditambah tanpa mengacaukan struktur yang sudah ada. Developer baru juga bisa lebih cepat memahami alur aplikasi.
3. Reusability
Fungsi atau class tertentu bisa digunakan ulang di beberapa tempat tanpa harus menulis ulang. Reuse = efisiensi.
4. Maintainability
Jika terjadi bug, perbaikan dapat dilakukan pada module terkait tanpa efek samping ke bagian lain (selama dependensinya jelas).
5. Memudahkan Testing
Unit test dapat dilakukan per module, bukan satu file besar. Ini meningkatkan kualitas aplikasi secara keseluruhan.
6. Menghindari Global Namespace Pollution
Salah satu masalah klasik JavaScript adalah function atau variable yang menumpuk di scope global. Modularisasi menghindari bentrokan nama dan error yang sulit dilacak.
Singkatnya: modularisasi adalah fondasi untuk menjaga kualitas aplikasi web modern agar tetap rapi, aman, dan mudah dikembangkan jangka panjang.
Perbedaan Script Biasa vs Modul
Perbedaan antara script biasa dan module terletak pada cara keduanya dieksekusi, cara mengelola scope, serta dukungan fitur import dan export.
1. Script Biasa (<script> standar)
- Semua variable dan function berada di global scope
- Rentan tabrakan nama (name collision)
- Tidak mendukung import dan export
- Tidak strict mode secara default
- Ideal untuk proyek kecil
Contoh:
<script src="main.js"></script>Code language: HTML, XML (xml)
2. JavaScript Module (<script type="module">)
- Menjalankan kode dalam mode module scope, bukan global
- Mendukung import dan export
- Otomatis berjalan dalam strict mode
- Mendukung deferred loading
- File module hanya dieksekusi sekali, meskipun di-import berkali-kali
- Lebih aman dan modern
Contoh:
<script type="module" src="app.js"></script>Code language: HTML, XML (xml)
Ringkasan Perbedaannya
| Aspek | Script Biasa | JavaScript Module |
|---|---|---|
| Global scope | Ya | Tidak |
| import/export | Tidak | Ya |
| Strict mode default | Tidak | Ya |
| Eksekusi berulang | Ya | Tidak (cached) |
| Lingkup variabel | Global | Terisolasi |
| Use case | Proyek kecil | Proyek modern |
Contoh Kasus Sebelum dan Sesudah Modularisasi
Di bagian ini kita akan melihat perbedaan nyata antara aplikasi yang ditulis tanpa modul vs menggunakan modul.
Sebelum Modularisasi (Kode Menumpuk di Satu File)
Misalkan Anda membuat aplikasi todo sederhana.
File: app.js
// Data
let todos = [];
// Tambah todo
function addTodo(text) {
todos.push(text);
render();
}
// Render DOM
function render() {
const list = document.getElementById('todo-list');
list.innerHTML = '';
todos.forEach((item) => {
const li = document.createElement('li');
li.textContent = item;
list.appendChild(li);
});
}
// Event
document.getElementById('btn-add').addEventListener('click', () => {
const text = document.getElementById('input-todo').value;
addTodo(text);
});Code language: JavaScript (javascript)
Masalah:
- Semua logic bercampur
- Sulit diuji secara terpisah
- Jika aplikasi bertambah fitur: kacau dan sulit di-maintain
Sesudah Modularisasi (Dipisah Berdasarkan Fungsi)
File: todoModel.js
export let todos = [];
export function addTodo(text) {
todos.push(text);
}Code language: JavaScript (javascript)
File: todoView.js
export function render() {
const list = document.getElementById('todo-list');
list.innerHTML = '';
todos.forEach((item) => {
const li = document.createElement('li');
li.textContent = item;
list.appendChild(li);
});
}Code language: JavaScript (javascript)
File: main.js
import { todos, addTodo } from './todoModel.js';
import { render } from './todoView.js';
document.getElementById('btn-add').addEventListener('click', () => {
const text = document.getElementById('input-todo').value;
addTodo(text);
render(todos);
});Code language: JavaScript (javascript)
Keuntungan setelah modularisasi:
- Struktur proyek lebih jelas (Model—View—Main)
- Testing lebih mudah
- Perubahan UI tidak mengganggu data
- Module dapat berkembang tanpa mengacaukan kode lain
ES Modules (ESM) di JavaScript
ES Modules (ESM) adalah sistem modular bawaan JavaScript modern yang diperkenalkan melalui standar ECMAScript 2015 (ES6). Sistem ini menjadi fondasi penting dalam pengembangan aplikasi web saat ini, menggantikan berbagai pendekatan modular lama seperti CommonJS, RequireJS, dan IIFE. Pada bagian ini, kita akan membahas sejarahnya, bagaimana scope di modul bekerja, strict mode otomatis, serta keunggulannya dibanding sistem modul sebelumnya.
Sejarah dan Standar ES6
Sebelum ES6, JavaScript tidak memiliki sistem modul native. Para developer harus mengandalkan berbagai library atau pola manual untuk membuat struktur modular, seperti:
IIFE (Immediately Invoked Function Expression)
Menyembunyikan variabel dalam function scope.AMD (Asynchronous Module Definition) – RequireJS
Digunakan untuk browser, tetapi syntax-nya rumit.CommonJS (CJS) – Node.js
Menggunakan require() dan module.exports.
Masalahnya, masing-masing sistem memiliki cara berbeda dan tidak seragam. Ini menimbulkan fragmentasi dan ketergantungan pada tool pihak ketiga.
Lalu Hadir ES6 pada 2015
ES6 memperkenalkan sistem modul bawaan JavaScript yang:
- Menggunakan syntax import dan export
- Didukung langsung oleh browser modern dan Node.js
- Memiliki static structure sehingga dapat dioptimalkan oleh bundler
- Seragam dan konsisten di seluruh lingkungan
ES Modules dirancang untuk:
- Menjadi standar global
- Aman, efisien, dan mudah digunakan
- Berfungsi baik di browser maupun server-side environment
Singkatnya, ESM adalah hasil evolusi panjang untuk menjadikan JavaScript lebih profesional, scalable, dan ramah untuk proyek besar.
Module Scope: Bagaimana Variabel Diperlakukan dalam Modul
Salah satu perbedaan paling penting antara “script biasa” dan modul adalah module scope.
Apa itu module scope?
Setiap file JavaScript yang menggunakan ES Module memiliki scope sendiri sehingga:
- Variabel tidak bocor ke global.
- Dua modul berbeda yang punya variabel sama (misalnya count) tidak saling bertabrakan.
- Modularisasi menjadi aman dan terprediksi.
Contoh perbandingan:
Script biasa (tanpa modul)
var message = "Halo";
// masuk ke window.message (global)Code language: JavaScript (javascript)
Jika ada file lain dengan kode seperti ini.
var message = "Hai";
// menimpa variabel global lainnyaCode language: JavaScript (javascript)
Akibatnya: error sulit dilacak, tabrakan nama, dan perilaku tak terduga.
Modul (type="module")
export const message = "Halo dari modul";Code language: JavaScript (javascript)
Di file lain:
import { message } from "./modul.js";
console.log(message);Code language: JavaScript (javascript)
Variabel message hanya hidup di modul tersebut, bukan di window.
Browser tidak akan memindahkan variabel modul ke global. Semua terisolasi.
Kesimpulan module scope:
- Setiap modul = dunia kecilnya sendiri
- Variabel tidak saling mengganggu
- Aplikasi lebih aman dan mudah dikelola
- Kesalahan duplikasi nama dapat dihindari
Strict Mode Otomatis dalam Modul
Setiap file ES Module otomatis menggunakan strict mode tanpa perlu menulis:
"use strict";Code language: JavaScript (javascript)
Apa keuntungan strict mode?
- Melarang penggunaan variabel tanpa deklarasi (mencegah typo dan error tersembunyi)
- Tidak mengizinkan duplikasi nama parameter
- Melarang mengubah properti bawaan yang tidak dapat diubah
thisdi level global tidak lagi mengarah kewindow→ menghindari banyak bug klasik- Error ditangani lebih tegas
Contoh:
Script biasa tanpa strict mode:
x = 10; // tidak error, tapi berbahayaCode language: JavaScript (javascript)
Modul:
x = 10; // ERROR: x is not definedCode language: JavaScript (javascript)
Kesimpulan: strict mode otomatis menjadikan modul lebih aman, lebih konsisten, dan lebih mudah di-debug.
Keunggulan ES Modules Dibanding Sistem Modul Lama
ESM bukan hanya “modul versi baru”, tetapi solusi modern yang mengatasi berbagai kekurangan sistem modul sebelumnya.
Berikut keunggulan utamanya:
1. Native dan Standar Global
- Tidak perlu library tambahan seperti RequireJS atau Browserify.
- Didukung langsung oleh browser modern dan Node.js.
- Syntax resmi dan konsisten.
2. Mendukung Static Analysis
ESM memiliki struktur statis, artinya:
importdanexportdiketahui pada waktu kompilasi (bukan runtime).- Bundler (Webpack, Rollup, Vite) dapat melakukan tree-shaking.
- Aplikasi yang dihasilkan lebih kecil dan lebih efisien.
Contoh:
import { helper } from "./utils.js";Code language: JavaScript (javascript)
Bundler tahu bahwa hanya helper yang digunakan → yang lain bisa dihapus.
3. Mendukung Loading Asinkron di Browser
Browser dapat melakukan:
- Preloading
- Lazy loading / dynamic import
- Dependency analysis otomatis
Ini membuat performa aplikasi lebih cepat.
4. Tidak Ada Global Namespace Pollution
Berbeda dengan CJS atau IIFE yang bisa bocor ke global, ESM benar-benar terpisah.
5. Lebih Mudah Dibaca dan Dipelajari
Syntax ESM lebih dekat dengan bahasa modern lainnya (Python, PHP, Java, dll):
export function run() {}
import { run } from "./module.js";Code language: JavaScript (javascript)
Sangat natural bagi developer pemula maupun berpengalaman.
6. Mendukung Top-Level await
ESM mendukung await langsung tanpa async function:
const data = await fetch("/api");Code language: JavaScript (javascript)
Ini tidak tersedia di CommonJS.
7. Optimasi Lebih Baik di Browser dan Tooling Modern
- Caching modul lebih efisien
- Modul tidak dieksekusi dua kali
- Bisa dibagi kecil-kecil tanpa kehilangan performa
Ringkasan Keunggulan
| Fitur | ES Modules | CommonJS | AMD/IIFE |
|---|---|---|---|
| Native browser | Ya | Tidak | Tidak |
| Static analysis (tree shaking) | Ya | Tidak | Tidak |
| Syntax modern | Ya | Tidak | Tidak |
| Caching otomatis | Ya | Ya | Tidak |
| Top-level await | Ya | Tidak | Tidak |
| Scope terisolasi | Ya | Tidak | Tidak |
Export dalam JavaScript
Dalam sistem ES Modules (ESM), konsep export adalah salah satu fondasi utama yang memungkinkan sebuah modul berbagi fungsi, variabel, object, atau class ke modul lain. Tanpa export, modul akan menjadi file yang berdiri sendiri tanpa bisa berinteraksi.
Pada bagian ini, kita akan membahas apa itu export, jenis-jenisnya, cara penggunaannya, hingga kesalahan umum yang sering terjadi.
Pengertian Export
Export adalah mekanisme untuk “mengirim” bagian tertentu dari sebuah modul agar dapat digunakan oleh modul lain. Tanpa export, isi modul hanya hidup di dalam file tersebut (module scope).
Sederhananya:
Export = membuat sesuatu dalam modul bisa di-import oleh modul lain.
Yang dapat diexport:
- fungsi
- variabel / konstanta
- class
- object
- seluruh isi modul (melalui export * aggregation*)
Export bisa dilakukan dalam dua kategori utama:
- Named export
- Default export
Dan developer bisa menulis export secara inline atau di akhir baris.
Named Export
Named export memungkinkan kita mengekspor lebih dari satu item dari satu modul. Setiap item harus memiliki nama yang digunakan saat proses import.
Ciri-ciri named export:
- Bisa memiliki banyak export dalam satu file
- Nama harus sama antara export dan import (kecuali di-rename)
- Lebih cocok untuk modul yang berisi beberapa utilitas atau fungsi kecil
Contoh Named Export (Inline)
// file: utils.js
export const PI = 3.14;
export function tambah(a, b) {
return a + b;
}
export function kurang(a, b) {
return a - b;
}Code language: JavaScript (javascript)
Cara Meng-import
import { PI, tambah, kurang } from './utils.js';Code language: JavaScript (javascript)
Named Export dengan Rename
export function multiply(a, b) {
return a * b;
}Code language: JavaScript (javascript)
Import dengan nama berbeda:
import { multiply as kali } from './utils.js';Code language: JavaScript (javascript)
Named Export dengan Ekspor di Akhir Baris
Kadang developer ingin menulis fungsi dulu, baru mengekspornya:
function bagi(a, b) {
return a / b;
}
function mod(a, b) {
return a % b;
}
export { bagi, mod };Code language: JavaScript (javascript)
Ini membuat file tampak lebih rapi jika eksportannya banyak.
Default Export
Default export digunakan jika sebuah modul ingin mengekspor satu nilai utama. Hanya boleh ada satu default export per file.
Perbedaan utamanya:
- Nama import bebas, tidak harus sama dengan nama function/variabel
- Hanya satu default export dalam satu modul
- Lebih cocok untuk modul tunggal yang hanya memiliki satu “fitur utama”
Contoh Default Export (Inline)
// file: hitung.js
export default function hitung(a, b) {
return a + b;
}Code language: JavaScript (javascript)
Import:
import hitung from './hitung.js';Code language: JavaScript (javascript)
Nama hitung pada import bisa bebas:
import calc from './hitung.js';Code language: JavaScript (javascript)
Default Export dengan Ekspor di Akhir Baris
function foo() {
console.log("Ini default export");
}
export default foo;Code language: JavaScript (javascript)
Default Export Menggunakan Class
export default class User {
constructor(name) {
this.name = name;
}
}Code language: JavaScript (javascript)
Import:
import User from './User.js';
const u = new User("Aris");Code language: JavaScript (javascript)
Export Inline vs Export di Akhir Baris
Export Inline
Ditempatkan langsung pada deklarasi:
export function hello() {}
export const PI = 3.14;Code language: JavaScript (javascript)
Kelebihan:
- Praktis
- Terlihat jelas mana yang diekspor
Kekurangan:
- Kurang cocok bila jumlah export banyak
Export di Akhir Baris
Deklarasi ditulis dulu, export kemudian:
function login() {}
function logout() {}
const MAX = 100;
export { login, logout, MAX };Code language: JavaScript (javascript)
Kelebihan:
- Rapi dan terstruktur
- Mudah mengelola ekspor banyak item
Kekurangan:
- Membutuhkan dua langkah (tulis → ekspor)
Menggabungkan Named Export & Default Export
Anda bisa menggunakan default export + named export dalam satu modul.
Contoh:
// file: angka.js
export const PI = 3.14;
export const E = 2.71;
export default function kelilingLingkaran(r) {
return 2 * PI * r;
}Code language: JavaScript (javascript)
Import:
import keliling, { PI, E } from './angka.js';
console.log(keliling(10));Code language: JavaScript (javascript)
Ini cukup umum pada modul besar dan library modern.
Kesalahan Umum dalam Export
1. Menggunakan Lebih dari Satu Default Export
export default function A() {}
export default function B() {} // ❌ ERRORCode language: JavaScript (javascript)
Solusi:
export default function A() {}
export function B() {}Code language: JavaScript (javascript)
2. Mengimpor Named Export sebagai Default atau Sebaliknya
Misal:
// file utils.js
export function halo() {}Code language: JavaScript (javascript)
Kemudian:
import halo from './utils.js'; // ❌ salahCode language: JavaScript (javascript)
Seharusnya:
import { halo } from './utils.js';Code language: JavaScript (javascript)
3. Lupa Menambahkan Kurung Kurawal untuk Named Import
import tadi from './utils.js'; // ❌ kalau bukan default exportCode language: JavaScript (javascript)
4. Salah Menulis Path (Wajib Ekstensi di Browser)
Browser mewajibkan:
import { foo } from './file.js'; // ✔Code language: JavaScript (javascript)
Bukan:
import { foo } from './file'; // ❌ di browserCode language: JavaScript (javascript)
5. Ekspor Variabel Tanpa Deklarasi
export x = 10; // ❌ salahCode language: JavaScript (javascript)
Seharusnya:
export const x = 10;Code language: JavaScript (javascript)
6. Export Object Literals dengan Syntax Salah
Tidak bisa:
export { nama: "Aris" }; // ❌ errorCode language: JavaScript (javascript)
Harus:
const data = { nama: "Aris" };
export { data };Code language: JavaScript (javascript)
7. Error Karena Circular Dependency
Dua modul saling mengimport satu sama lain:
A import B
B import ACode language: JavaScript (javascript)
Ini biasanya menyebabkan nilai undefined.
Import dalam JavaScript
Setelah memahami bagaimana modul bekerja dan bagaimana mengekspor nilai dari sebuah file, kini kita masuk ke bagian paling penting dalam modular JavaScript: mengimpor. Konsep import memungkinkan suatu file mengambil variabel, fungsi, class, atau objek dari modul lain.
Dengan mekanisme ini, developer dapat menyusun aplikasi menjadi potongan kecil yang terorganisir, mudah dikelola, dan mudah diuji.
Cara Kerja Import
Keyword import digunakan untuk mengambil bagian tertentu dari file JavaScript lain yang telah melakukan export. Proses ini berjalan secara statis, artinya JavaScript mengetahui semua import dan export sebelum kode dijalankan.
Contoh dasar:
import { sayHello } from './utils.js';
sayHello();Code language: JavaScript (javascript)
Cara kerja import:
- Browser (atau bundler) membaca file yang sedang diproses.
- Ia melihat semua deklarasi import pada top level.
- Ia memuat modul sumber (utils.js) sebelum eksekusi berjalan.
- Modul di-resolve, lalu nilai yang diexport disediakan ke file pemanggil.
Properti penting:
- Hoisted: import dieksekusi sebelum code lainnya.
- Static: Tidak dapat ditempatkan di dalam fungsi (kecuali dynamic import).
- Read-only binding: Nilai import tidak boleh di-reassign.
Menggunakan Import Default
Default import digunakan ketika modul mengekspor satu nilai utama menggunakan export default.
Contoh utils.js:
export default function greet() {
console.log("Hello!");
}Code language: JavaScript (javascript)
Mengimpor:
import greet from './utils.js';
greet();Code language: JavaScript (javascript)
Ciri-ciri default import:
- Bebas memberi nama saat import.
- Hanya satu default export per file.
import bebasApaSaja from './utils.js';Code language: JavaScript (javascript)
Menggunakan Import Named
Named import mengambil variabel atau fungsi yang diekspor secara bernama.
Contoh math.js:
export const PI = 3.14;
export function add(a, b) {
return a + b;
}Code language: JavaScript (javascript)
Mengimpor:
import { PI, add } from './math.js';
console.log(PI);
console.log(add(2, 3));Code language: JavaScript (javascript)
Catatan:
- Nama harus sama dengan yang diekspor.
- Tidak bisa mengganti nama tanpa alias.
Import Alias (Rename Imports)
Jika terjadi konflik nama, atau ingin penamaan yang lebih jelas, kita bisa menggunakan alias dengan keyword as.
Contoh:
import { add as tambah, PI as phi } from './math.js';
console.log(tambah(5, 5));
console.log(phi);Code language: JavaScript (javascript)
Manfaat alias:
- Menghindari tabrakan nama.
- Membuat kode lebih deskriptif.
Import Seluruh Modul (as)
Anda dapat mengimpor seluruh isi modul sebagai sebuah objek.
Contoh:
import * as MathUtils from './math.js';
console.log(MathUtils.PI);
console.log(MathUtils.add(4, 2));Code language: JavaScript (javascript)
Kapan menggunakan * as:
- Modul memiliki banyak export.
- Anda ingin mengelompokkan fungsionalitas dalam sebuah namespace.
- Cocok untuk utilitas besar.
Import Dinamis (import())
Dynamic import memungkinkan memuat modul secara asinkron menggunakan fungsi import(). Berbeda dengan import biasa yang statis, ini dieksekusi saat runtime.
Contoh:
button.addEventListener('click', async () => {
const module = await import('./feature.js');
module.runFeature();
});Code language: JavaScript (javascript)
Keuntungan dynamic import:
- Lazy loading: mengurangi beban awal halaman.
- Memuat modul hanya ketika diperlukan.
- Mendukung path dinamis.
Contoh dengan path variabel:
const page = 'dashboard';
import(`./pages/${page}.js`).then(module => {
module.init();
});Code language: JavaScript (javascript)
Kesalahan Umum pada Import
Berikut beberapa error umum yang sering terjadi:
1. Salah path relatif
import { add } from 'math.js'; // salah
import { add } from './math.js'; // benarCode language: JavaScript (javascript)
2. Mengimpor default padahal modul tidak punya default export
import math from './math.js';
// error jika math.js hanya punya named exportCode language: JavaScript (javascript)
3. Memindahkan import ke dalam fungsi
Ini tidak diperbolehkan:
function test() {
import { add } from './math.js'; // ❌ error
}Code language: JavaScript (javascript)
4. Salah penamaan pada named import
import { Add } from './math.js'; // ❌ case-sensitiveCode language: JavaScript (javascript)
5. Lupa menambahkan ekstensi file
Browser modern wajib menggunakan ekstensi, kecuali bundler seperti Vite/Webpack.
Struktur Folder dan Modul
Modularisasi JavaScript bukan hanya tentang penggunaan import dan export, tetapi juga tentang bagaimana Anda mengatur file dan folder agar proyek tetap bersih, mudah dirawat, dan scalable. Struktur folder yang rapi membantu developer baru cepat memahami arsitektur, memudahkan debugging, dan menjaga kode tetap terorganisir.
Pada bagian ini kita membahas struktur folder yang direkomendasikan, jenis-jenis modul berdasarkan perannya, serta best practice penamaan file untuk modul JavaScript modern.
Organisasi Folder yang Direkomendasikan
Tidak ada struktur mutlak dalam proyek JavaScript, karena bisa berbeda bergantung pada framework (React, Vue, Svelte, Node.js), tetapi ada pola umum yang dianggap standar industri untuk aplikasi modern.
Contoh struktur modular yang baik:
src/
│
├── assets/ # gambar, font, ikon
├── components/ # modul UI
├── utils/ # helper & function kecil
├── services/ # modul API / data fetcher
├── pages/ # halaman (khusus SPA/MPA)
├── hooks/ # custom hooks (jika React)
├── store/ # state management
└── config/ # konfigurasi umumCode language: PHP (php)
Prinsip utamanya:
- Setiap folder mewakili satu kategori fungsional.
- File modul dalam folder tersebut fokus pada satu tanggung jawab.
- Hindari file besar yang mencampur banyak logic tidak terkait.
Keuntungan:
- Arsitektur lebih scalable.
- Navigasi file lebih mudah.
- Memudahkan kolaborasi tim.
File Modul untuk Komponen UI
Dalam proyek frontend, setiap komponen UI sebaiknya diperlakukan sebagai modul. Komponen harus mandiri dan menyimpan logic, style, dan struktur yang relevan.
Contoh folder components/:
components/
│
├── Button.js
├── Navbar.js
├── Sidebar.js
└── Modal.js
Contoh Button.js:
export default function Button({ label, onClick }) {
const btn = document.createElement("button");
btn.textContent = label;
btn.addEventListener("click", onClick);
return btn;
}Code language: JavaScript (javascript)
Tip penting:
- Selalu gunakan default export untuk komponen UI tunggal.
- Kelompokkan komponen besar ke subfolder:
components/
├── form/
│ ├── Input.js
│ └── Checkbox.js
└── layout/
├── Header.js
└── Footer.js
Pastikan setiap komponen hanya memiliki satu tanggung jawab visual.
File Modul untuk Utilities dan Helper
Koleksi fungsi kecil yang sering digunakan di berbagai bagian aplikasi sebaiknya ditaruh dalam folder utils atau helpers.
Contoh:
utils/
│
├── formatDate.js
├── generateID.js
└── validateEmail.js
Contoh isi modul:
export function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}Code language: JavaScript (javascript)
Best practice:
- Gunakan named export pada utils agar mudah di-tree-shake saat build.
- Jangan mencampur utilities yang tidak berkaitan dalam satu file.
- Beri nama fungsi sesuai tugasnya (misal:
debounce(),capitalize(),randomInt()).
Modul untuk API Services
Untuk aplikasi yang berkomunikasi dengan server, buat folder khusus untuk API, biasanya dinamakan services.
Contoh struktur:
services/
│
├── authService.js
├── userService.js
└── productService.js
Contoh isi userService.js:
const BASE_URL = "https://api.example.com/users";
export async function getUsers() {
const response = await fetch(BASE_URL);
return response.json();
}
export async function getUserById(id) {
const response = await fetch(`${BASE_URL}/${id}`);
return response.json();
}Code language: JavaScript (javascript)
Aturan dasarnya:
- Satu file service = satu domain data.
- Selalu gunakan named export agar mudah diketahui fungsi mana yang tersedia.
- Tidak ada manipulasi DOM di dalam service — hanya logic data & komunikasi API.
Untuk aplikasi besar, struktur bisa seperti:
services/
├── http/
│ ├── axiosInstance.js
│ └── interceptors.js
├── auth/
│ └── authService.js
└── product/
└── productService.js
Best Practice Penamaan File Modul
Penamaan file penting agar mudah dipahami, cepat dicari, dan konsisten di seluruh proyek.
Berikut best practice yang direkomendasikan:
1. Gunakan lowercase atau kebab-case
Contoh benar:
format-date.js
string-utils.js
user-service.jsCode language: CSS (css)
Contoh buruk:
FormatDate.js
stringUtils.js
UserService.jsCode language: CSS (css)
Kebab-case sangat umum digunakan di proyek modern, kecuali komponen React (yang pakai PascalCase).
2. Untuk komponen UI gunakan PascalCase
Button.js
Navbar.js
UserCard.jsCode language: CSS (css)
Alasan:
- Mengikuti standar UI modern seperti React, Vue (SFC), dan Svelte.
3. Nama file harus deskriptif dan jelas
Hindari:
helper.js
index2.js
test.jsCode language: CSS (css)
Gunakan:
date-helper.js
api-client.js
dom-utils.jsCode language: CSS (css)
4. Sesuaikan penamaan dengan isi file
Jika modul hanya berisi satu fungsi utama → namai berdasarkan fungsinya:
debounce.js
formatCurrency.js
calculateAge.jsCode language: CSS (css)
5. Jangan buat file terlalu besar (monolit)
Jika file > 300–400 baris, pertimbangkan pecah menjadi modul baru.
6. Gunakan folder + index.js untuk grouping
Contoh:
services/user/
│
├── index.js
├── getUser.js
└── updateUser.js
index.js:
export * from './getUser.js';
export * from './updateUser.js';Code language: JavaScript (javascript)
Keuntungan: import menjadi lebih sederhana:
import { getUser } from '../services/user';Code language: JavaScript (javascript)
Menggunakan Modules di HTML
Agar JavaScript modular dapat bekerja di browser tanpa bundler seperti Webpack, Vite, atau Rollup, kita perlu memanfaatkan fitur bawaan HTML yaitu <script type="module">. Fitur ini memungkinkan browser modern memuat file JavaScript sebagai modul ES (ESM), sehingga kita dapat menggunakan import dan export secara langsung.
Pada bagian ini kita membahas cara menggunakan modul di HTML, keuntungan script module, perbedaan saat menjalankan file lokal vs server, kompatibilitas browser, hingga contoh implementasi sederhana.
Penggunaan: <script type="module">
Untuk mengaktifkan modul di browser, kita cukup menambahkan atribut:
<script type="module" src="app.js"></script>Code language: HTML, XML (xml)
Dengan cara ini:
- Browser akan menganggap
app.jssebagai ES Module - Anda dapat memakai
importdanexport - Modul yang di-import akan dimuat secara otomatis
Contoh isi app.js:
import { greet } from './utils.js';
greet("Aris");Code language: JavaScript (javascript)
Isi utils.js:
export function greet(name) {
console.log(`Halo, ${name}!`);
}Code language: JavaScript (javascript)
Browser akan memuat kedua file tersebut sebagai modul.
Keuntungan script module (async, defer, caching)
Ketika menggunakan:
<script type="module" src="app.js"></script>Code language: HTML, XML (xml)
Browser secara otomatis memberi keuntungan performa:
1. Module bersifat otomatis defer
Artinya:
- Tidak memblokir rendering HTML
- Dijalankan setelah DOM selesai diparsing
- Urutan script tetap terjaga
Mirip seperti:
<script defer src="..."></script>Code language: HTML, XML (xml)
2. Module menggunakan strict mode otomatis
Tidak perlu menulis “use strict”.
3. Module mendukung import antar file
Ini fitur utama ESM.
4. Module dieksekusi hanya sekali (per origin)
Jika modul di-import berkali-kali dari berbagai file, browser tidak memuat ulang—ini menghemat performa & bandwidth.
5. Mendukung top-level import & export
Tidak perlu fungsi pembungkus.
Menggunakan module di local file vs server
Fitur ESM memiliki perbedaan besar antara:
- Dibuka langsung sebagai file lokal (file://)
- Diakses melalui HTTP server (http:// atau https://)
❌ Masalah umum saat menjalankan file langsung (file://)
Anda mungkin mendapatkan error seperti:
Access to script at 'file:///...' from origin 'null' has been blockedCode language: JavaScript (javascript)
Atau:
Uncaught TypeError: Failed to resolve module specifierCode language: JavaScript (javascript)
Browser membatasi import relatif pada file lokal demi keamanan.
Solusi: Gunakan server lokal
Rekomendasi server lokal ringan untuk development:
npx servenpx http-server- Live Server (VSCode extension)
- Vite dev server
- PHP server →
php -S localhost:8000 - Node server sederhana
Contoh dengan http-server:
npx http-server
Lalu buka di browser:
http://localhost:8080Code language: JavaScript (javascript)
Setelah memakai server, import akan berjalan normal.
Browser yang mendukung ES Modules
ES Modules didukung hampir semua browser modern.
| Browser | Support |
|---|---|
| Chrome | ✔ |
| Firefox | ✔ |
| Safari | ✔ |
| Edge | ✔ |
| Opera | ✔ |
| Android Chrome | ✔ |
| iOS Safari | ✔ |
Internet Explorer → ❌ tidak mendukung ES Modules.
Anda bisa menambahkan fallback jika butuh mendukung browser jadul:
<script nomodule src="legacy.js"></script>Code language: HTML, XML (xml)
Contoh setup sederhana di HTML
Ini contoh paling dasar project modular di browser.
Struktur folder:
project/
│
├── index.html
├── app.js
└── utils.js
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Modular JavaScript Example</title>
</head>
<body>
<h1>Contoh Modular JavaScript</h1>
<script type="module" src="app.js"></script>
</body>
</html>Code language: HTML, XML (xml)
app.js:
import { sayHello } from './utils.js';
sayHello('Dunia');Code language: JavaScript (javascript)
utils.js:
export function sayHello(name) {
console.log(`Halo, ${name}!`);
}Code language: JavaScript (javascript)
Hasil di console browser:
Halo, Dunia!
Dengan setup ini, Anda sudah menggunakan ES Module murni tanpa bundler.
Perbedaan require() dan import
JavaScript memiliki dua sistem modul yang paling dikenal: CommonJS (CJS) dengan fungsi require() dan ES Modules (ESM) dengan keyword import. Keduanya sering membingungkan developer pemula karena terlihat “mirip”, tetapi sebenarnya punya perbedaan konsep, cara kerja, dan tujuan penggunaan.
Pada bagian ini, kita akan membahas perbedaan mendasar antara require() vs import, bagaimana masing-masing bekerja, hingga kapan memilih salah satunya.
require di Node.js CommonJS
require() adalah sistem modul CommonJS (CJS) yang:
- Digunakan secara default oleh Node.js sejak awal
- Berjalan menggunakan format file .js atau .cjs
- Memuat modul secara synchronous
- Tidak mendukung static analysis seperti ESM
- Masih digunakan pada banyak proyek Node lawas
Contoh require():
const fs = require('fs');
const math = require('./math.js');
console.log(math.add(1, 2));Code language: JavaScript (javascript)
Contoh export CJS:
// math.js
module.exports = {
add(a, b) {
return a + b;
}
};Code language: JavaScript (javascript)
CJS populer karena:
- Sederhana
- Mudah digunakan
- Dukungan luas di Node.js versi lama
- Ecosystem NPM lama dibangun di atasnya
Namun sekarang CJS mulai ditinggalkan pada proyek modern.
import di ES Modules
import adalah sistem modul ES Modules (ESM) yang merupakan standar resmi ECMAScript.
Kelebihan utama:
- Mendukung static analysis
- Mendukung tree-shaking (menghapus kode tidak digunakan)
- Bisa berjalan di browser tanpa bundler
- Future-proof (standar masa depan JavaScript)
- Dieksekusi secara asynchronous (mirip defer)
Contoh import:
import { add } from './math.js';
console.log(add(5, 3));Code language: JavaScript (javascript)
Contoh export:
export function add(a, b) {
return a + b;
}Code language: JavaScript (javascript)
Sistem ESM sekarang merupakan format resmi JavaScript modern.
Perbedaan cara load
CommonJS (require)
- Dijalankan secara runtime
- Modul dimuat ketika kode dieksekusi
- Setiap
require()mengembalikan value darimodule.exports - Module caching dilakukan setelah load pertama
Urutan eksekusi:
- Jalankan file utama
- Ketika interpreter menemukan
require(), Node berhenti sejenak - Membuka file modul
- Menjalankan modul tersebut
- Mengembalikan hasil export
ES Modules (import)
- Dianalisis lebih dulu (static analysis) sebelum eksekusi
- Browser/Node membaca semua import saat parsing
- Dependency graph dibuat terlebih dahulu
- Modul dimuat secara asynchronous
- Dieksekusi setelah tree siap
Urutan eksekusi:
- Parsing file → scan semua import
- Load semua modul yang dibutuhkan
- Membangun dependency graph
- Menjalankan modul setelah semua resolusi selesai
Ini membuat ESM lebih aman, lebih cepat, dan lebih optimal untuk bundling.
Perbedaan synchronous vs asynchronous
require() — synchronous
const data = require('./file'); // Blok eksekusi
console.log("Lanjut");Code language: JavaScript (javascript)
Node akan berhenti sementara sampai require() selesai memuat file.
Kelemahan:
- Tidak cocok untuk environment non-blocking
- Memperlambat startup aplikasi besar
import — asynchronous
Modul di-load secara non-blocking:
import data from './file.js'; // Tidak blok eksekusi
console.log("Lanjut");Code language: JavaScript (javascript)
Sifat asynchronous ini:
- Lebih cepat
- Performa lebih baik untuk aplikasi besar
- Lebih cocok untuk browser
Kapan menggunakan require atau import?
Gunakan require() jika:
- Memakai Node.js versi lama (sebelum ESM stabil)
- Butuh load modul secara conditional:
if (debug) {
const logger = require('./debugger.js');
}Code language: JavaScript (javascript)
- Menggunakan paket NPM lama yang masih pakai CJS
- Project sudah terlanjur besar dan full CJS (legacy)
Gunakan import jika:
- Ingin membuat project modern
- Aplikasi berjalan di browser
- Menggunakan framework modern (React, Vue, Svelte, Next.js)
- Membuat library JavaScript masa depan
- Ingin manfaatkan fitur seperti:
- Tree-shaking
- Top-level await
- Static analysis
- Kompatibilitas ES6+
Contoh import conditional (dinamis):
if (needLogger) {
const { log } = await import('./logger.js');
}Code language: JavaScript (javascript)
Kesimpulan Singkat
| Fitur | require (CJS) | import (ESM) |
|---|---|---|
| Lingkungan | Node.js lama | Node.js modern + browser |
| Cara load | synchronous | asynchronous |
| Waktu analisa | runtime | parsing / compile time |
| Tree-shaking | ❌ tidak bisa | ✔ bisa |
| Static analysis | ❌ | ✔ |
| future-proof | ❌ | ✔ |
Untuk proyek modern: selalu gunakan import.
Tree-Shaking & Bundling Modules
Dalam pengembangan aplikasi JavaScript modern, modul adalah fondasi utama. Namun ketika aplikasi semakin besar, jumlah file modul yang tersebar bisa mencapai ratusan atau ribuan. Untuk produksi (production build), ukuran file harus tetap kecil agar aplikasi cepat dimuat.
Di sinilah peran tree-shaking dan bundling menjadi sangat penting. Dua mekanisme ini memastikan hanya kode yang benar-benar diperlukan yang dikirim ke browser.
Bagian ini membahas:
- Apa itu tree-shaking
- Bagaimana bundler seperti Vite, Webpack, dan Rollup menghapus kode tidak terpakai
- Pentingnya tree-shaking
- Cara bundling modul untuk production
Apa itu tree-shaking?
Tree-shaking adalah teknik untuk menghapus kode JavaScript yang tidak digunakan (dead code) dari hasil build.
Nama “tree-shaking” diambil dari analogi “mengguncang pohon”: hanya daun yang diperlukan yang jatuh ke hasil akhir; sisanya hilang.
Contoh sederhana:
// utils.js
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }Code language: JavaScript (javascript)
Jika di aplikasi Anda hanya menggunakan:
import { add } from './utils.js';Code language: JavaScript (javascript)
maka fungsi multiply dapat dihapus dari bundle akhir.
Tanpa tree-shaking: browser memuat semua fungsi, meskipun tidak digunakan.
Dengan tree-shaking: hanya fungsi add yang dikirim ke browser.
Cara bundler (Vite, Webpack, Rollup) menghapus kode tidak terpakai
Tree-shaking hanya bisa dilakukan dengan sistem modul ES Modules (ESM), karena ESM:
- Bersifat static analysis
- Mengetahui import/export pada waktu parsing
- Tidak bisa dimanipulasi secara runtime seperti CommonJS
Bundler seperti Rollup, Webpack, dan Vite menggunakan algoritma untuk:
Scan dependency graph
- Mencari semua file yang saling terhubung
Analisis import/export
- Modul mana yang dipakai?
- Fungsi/variabel apa yang dipanggil?
Tandai kode yang tidak dipakai (dead code marking)
Hilangkan kode yang tidak relevan
- Melalui optimisasi build
Minify hasil akhir
- Menggunakan Terser atau esbuild untuk mengecilkan kode
Cara kerja masing-masing bundler:
Rollup
- Paling “ketat” dan efisien dalam tree-shaking
- Dirancang khusus untuk library & ESM
- Logika tree-shaking terbaik di antara bundler lainnya
Webpack
- Mendukung tree-shaking sejak Webpack 2
- Memerlukan mode: “production”
- Memerlukan tanda “sideEffects”: false di package.json
Vite
- Menggunakan esbuild untuk dev mode (cepat sekali)
- Menggunakan Rollup untuk production build (tree-shaking optimal)
Vite = best of both worlds (cepat + optimal).
Manfaat tree-shaking pada performa
Tree-shaking sangat penting terutama dalam aplikasi SPA, PWA, atau dashboard besar.
Manfaatnya:
1. Ukuran bundle lebih kecil
Hanya kode yang digunakan yang masuk ke build final → halaman memuat lebih cepat.
2. Performansi runtime lebih cepat
Kode yang dihapus = waktu eksekusi lebih sedikit.
3. Lebih efisien untuk perangkat mobile
Loading dan parsing JavaScript adalah proses mahal, terutama di:
- smartphone mid-end
- koneksi lambat (3G/4G)
- aplikasi offline
4. Mengurangi konsumsi memori
Lebih sedikit modul yang harus di-cache.
5. SEO & Core Web Vitals meningkat
Bundle lebih kecil → LCP lebih cepat.
Parsing JS lebih sedikit → TBT lebih rendah.
Singkatnya, tree-shaking = performa & UX lebih baik.
Bundling modul untuk production
Untuk produksi, modul biasanya digabung (bundling) menjadi 1 atau beberapa file:
Tanpa bundling:
- Browser harus memuat ratusan file .js
- Banyak request HTTP
- Lambat pada jaringan
Dengan bundling:
- Ratusan file digabung jadi 1–5 file besar
- HTTP request jauh lebih sedikit
- Cocok untuk deployment
Fase bundling:
- Analisis graph modul
- Tree-shaking
- Bundling
- Minifying
- Code splitting
- Preload/prefetch optimization
Bundler modern seperti Vite & Rollup mengoptimalkan semua tahap ini otomatis.
Contoh bundling sederhana
Berikut contoh menggunakan Rollup sebagai bundler ESM.
1. Struktur folder
project/
│── src/
│ ├── main.js
│ └── utils.js
│
└── rollup.config.js
2. File modul
utils.js
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }Code language: JavaScript (javascript)
main.js
import { add } from './utils.js';
console.log(add(10, 20));Code language: JavaScript (javascript)
Perhatikan bahwa multiply tidak digunakan.
Bundler akan menghapusnya.
3. Rollup config
// rollup.config.js
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
}
};Code language: JavaScript (javascript)
4. Jalankan bundling
Install Rollup:
npm install rollup --save-dev
Jalankan:
npx rollup -c
5. Hasil bundling (dist/bundle.js)
Rollup akan menghasilkan file seperti:
function add(a, b) {
return a + b;
}
console.log(add(10, 20));Code language: JavaScript (javascript)
Perhatikan:
- Hanya fungsi add yang masuk
- Fungsi multiply hilang
- Struktur sudah dipadatkan
Inilah tree-shaking.
Kesimpulan
Dalam artikel ini, kita telah membahas konsep Modular JavaScript secara menyeluruh — mulai dari dasar apa itu modul, kenapa modularisasi penting, bagaimana struktur folder disusun, hingga cara menggunakan import/export dalam berbagai skenario nyata. Modular JavaScript membantu aplikasi tetap rapi, terorganisir, mudah dirawat, dan lebih skalabel seiring bertambahnya fitur.
Semakin kamu memahami modularisasi, semakin mudah kamu membangun aplikasi web modern yang cepat, efisien, dan mudah dikembangkan di masa depan.