Pendahuluan
Halo rekan-rekan developer! Tidak terasa kita sudah sampai di artikel ke-10 dalam seri pengembangan Aplikasi Kuis Interaktif ini. Pada artikel sebelumnya, kita telah berhasil belajar cara mengelola data secara persisten menggunakan localStorage dan sessionStorage. Dengan fitur tersebut, aplikasi kuis kita sekarang sudah bisa menyimpan skor tertinggi (high score) pengguna secara lokal di browser.
Namun, coba perhatikan file script.js Anda. Seiring bertambahnya fitur—seperti penambahan timer, sistem level, hingga integrasi API—file JavaScript kita akan menjadi sangat panjang, penuh dengan variabel global, dan sulit untuk dikelola. Jika kita terus membiarkan semua logika menumpuk dalam satu file raksasa, kita akan terjebak dalam apa yang sering kita sebut sebagai “Spaghetti Code”. Kode menjadi sulit dibaca, sulit diuji (testing), dan sangat berisiko saat kita ingin melakukan perubahan kecil namun berakibat fatal pada fitur lainnya.
Di artikel kali ini, kita akan mempelajari salah satu fitur paling krusial dalam JavaScript modern: ES6 Modules. Kita akan belajar cara memecah kode besar kita menjadi potongan-potongan kecil yang mandiri (modular) menggunakan perintah import dan export. Dengan teknik ini, aplikasi kuis kita akan memiliki struktur yang jauh lebih profesional, rapi, dan mudah dikelola, layaknya standar pengembangan aplikasi di perusahaan teknologi besar seperti Gojek atau Tokopedia.
Mengenal Konsep ES6 Modules
ES6 Modules (atau sering disebut ECMAScript Modules) adalah sistem bawaan JavaScript untuk membagi kode ke dalam file-file terpisah. Setiap file dalam sistem modul dianggap sebagai “scope” tersendiri. Artinya, variabel yang Anda buat di dalam satu file tidak akan mengotori atau bertabrakan dengan variabel di file lain, kecuali jika Anda secara eksplisit mengekspornya.
Ada dua cara utama untuk membagikan kode antar file: Named Exports dan Default Exports.
1. Named Exports
Named exports digunakan ketika Anda ingin mengekspor beberapa fungsi, variabel, atau kelas dari satu file yang sama. Keunggulan utamanya adalah Anda harus menyebutkan nama yang tepat saat mengimpornya, yang sangat membantu dalam menjaga kejelasan kode.
Mari kita buat sebuah file bernama utils.js yang berisi fungsi-fungsi bantuan untuk aplikasi kuis kita.
// utils.js
// Kita menggunakan kata kunci 'export' sebelum deklarasi variabel/fungsi
export const formatRupiah = (angka) => {
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
}).format(angka);
// Contoh: 5000 menjadi Rp 5.000,00
};
export function checkAnswer(userAnswer, correctAnswer) {
// Mengembalikan true jika jawaban benar, false jika salah
return userAnswer === correctAnswer;
}
export const APP_VERSION = "1.0.2";
2. Default Exports
Default exports digunakan ketika sebuah file hanya memiliki satu tanggung jawab utama. Misalnya, sebuah file yang isinya hanya berupa kumpulan data pertanyaan kuis. Dalam satu file, Anda hanya boleh memiliki satu export default.
Mari kita buat file questions.js yang hanya berisi data pertanyaan.
// questions.js
const quizQuestions = [
{
id: 1,
question: "Siapakah pendiri Gojek?",
options: ["Nadiem Makarim", "William Tanuwijaya", "Ferry Unardi", "Achmad Zaky"],
answer: "Nadiem Makarim"
},
{
id: 2,
question: "Apa nama mata uang resmi Indonesia?",
options: ["Ringgit", "Dollar", "Rupiah", "Peso"],
answer: "Rupiah"
}
];
// Menggunakan export default karena file ini hanya fokus pada data pertanyaan
export default quizQuestions;
3. Menggunakan Import
Setelah kita mengekspor kode, langkah terakhir adalah mengambilnya kembali menggunakan perintah import. Perhatikan perbedaan sintaks antara mengimpor named export (menggunakan kurung kurim {}) dan default export (tanpa kurung kurim).
// main.js
// Mengimpor named exports menggunakan kurung kurawal
// Kita bisa juga mengganti namanya menggunakan kata kunci 'as'
import { formatRupiah, checkAnswer } from './utils.js';
// Mengimpor default export (nama bebas, tidak perlu kurung kurawal)
import quizData from './questions.js';
console.log("Versi Aplikasi:", formatRupiah(1000)); // Output: Rp 1.000,00
console.log("Data Pertanyaan:", quizData);
// Contoh penggunaan fungsi checkAnswer
const isCorrect = checkAnswer("Rupiah", "Rupiah");
console.log("Apakah jawaban benar?", isCorrect); // Output: true
Implementasi dalam Proyek
Sekarang, mari kita terapkan konsep ini untuk merestrukturisasi Aplikasi Kuis Interaktif kita. Kita akan membagi proyek menjadi beberapa modul agar lebih terorganisir.
Struktur Folder Proyek
project-kuis/
├── index.html
├── src/
│ ├── main.js (Logika utama/Entry point)
│ ├── questions.js (Data pertanyaan)
│ └── utils.js (Fungsi pembantu)
└── style.css
Langkah 1: Konfigurasi HTML
Hal terpenting saat menggunakan ES6 Modules di browser adalah menambahkan atribut type="module" pada tag <script>. Tanpa ini, browser akan menganggap file Anda sebagai skrip biasa dan akan error saat menemui kata kunci import.
<!-- index.html -->
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Aplikasi Kuis Interaktif</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="quiz-container">
<!-- Konten kuis Anda -->
</div>
<!-- WAJIB: Tambahkan type="module" -->
<script typetype="module" src="./src/main.js"></script>
</body>
</html>
Langkah 2: Refactoring Kode
Kita akan memindahkan logika pemrosesan skor ke utils.js dan data pertanyaan ke questions.js, lalu menggabungkannya di main.js.
File: src/utils.js
// Fungsi untuk menghitung sisa waktu (logic timer)
export function startTimer(duration, displayElement) {
let timer = duration, minutes, seconds;
const interval = setInterval(() => {
minutes = parseInt(timer / 60, 10);
seconds = parseInt(timer % 60, 10);
displayElement.textContent = `${minutes}:${seconds < 10 ? '0' + seconds : seconds}`;
if (--timer < 0) {
clearInterval(interval);
displayElement.textContent = "Waktu Habis!";
}
}, 1000);
return interval;
}
// Fungsi untuk merapikan skor ke format Rupiah jika ada hadiah
export function formatPrize(amount) {
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR'
}).format(amount);
}
File: src/main.js
import quizData from './questions.js';
import { startTimer, formatPrize } from './utils.js';
const quizContainer = document.getElementById('quiz-container');
const timerDisplay = document.getElementById('timer');
// Inisialisasi kuis
function initQuiz() {
console.log("Memulai kuis dengan", quizData.length, "pertanyaan.");
// Mulai timer selama 60 detik
startTimer(60, timerDisplay);
// Logika menampilkan pertanyaan...
renderQuestion(quizData[0]);
}
function renderQuestion(question) {
// Logika rendering HTML...
console.log("Menampilkan pertanyaan:", question);
}
// Jalankan aplikasi
initQuiz();
Dengan struktur seperti ini, jika suatu saat Anda ingin mengganti sumber data (misalnya dari file lokal ke API), Anda hanya perlu mengubah file questions.js tanpa perlu menyentuh logika utama di main.js. Inilah kekuatan dari modularitas.
Kesimpulan
Menggunakan ES Modules (ESM) adalah standar modern dalam pengembangan JavaScript. Dengan membagi kode menjadi modul-modul kecil, kita mendapatkan keuntungan berupa:
- Maintainability: Kode lebih mudah dirawat karena terbagi dalam bagian-bagian kecil.
- Reusability: Fungsi yang sama dapat digunakan di berbagai proyek berbeda.
- Namespace Isolation: Menghindari tabrakan nama variabel antar file.
Ringkasan Tips
- Gunakan Named Export (
export const ...) jika Anda memiliki banyak fungsi kecil dalam satu file. - Gunakan Default Export (
export default ...) jika file tersebut hanya bertanggunguk satu entitas utama (seperti class atau konfigurasi). - Selalu sertakan ekstensi file
.jssaat melakukanimportdi lingkungan browser asli (kecuali jika Anda menggunakan bundler seperti Webpack atau Vite). - Jangan lupa menambahkan
type="module"pada tag<script>di HTML Anda.