Menu

CommonJS vs ES Modules: require vs import di JavaScript

Dua sistem module bawaan JavaScript, mengapa keduanya ada, dan cara memilih antara require dan import di proyek Node.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Dua Sistem Module, Satu Bahasa

Awalnya JavaScript sama sekali tidak punya sistem module. Node mengisi kekosongan itu pada 2009 dengan CommonJS (require, module.exports), dan selama bertahun-tahun seperti itulah tampilan kode Node. Lalu pada 2015, bahasanya sendiri mendapat sistem module standar, yaitu ES Modules (import, export), yang sekarang didukung browser dan Node.

Jadi kamu akan melihat keduanya di lapangan. Berikut module kecil yang sama ditulis dengan masing-masing cara:

Fungsi yang sama, dua amplop yang berbeda. Sisa halaman ini membahas kapan masing-masing amplop itu penting dan mana yang sebaiknya kamu pilih.

Perbedaan Sintaks

Perbedaan sehari-harinya cukup ditulis di selembar kartu pos:

// CommonJS
const fs = require("fs");
const { readFile } = require("fs/promises");

module.exports = something;
module.exports.name = value;
exports.name = value;
// ES Modules
import fs from "fs";
import { readFile } from "fs/promises";

export default something;
export const name = value;
export { name };

require adalah pemanggilan fungsi biasa. import adalah sebuah pernyataan: ia hanya boleh muncul di tingkat atas module, dan path-nya harus berupa string literal. Batasan itu tidak sembarangan; justru itulah yang memungkinkan ESM melakukan hal-hal yang tidak bisa dilakukan CommonJS.

Perbedaan Sebenarnya: Statis vs Dinamis

CommonJS mengevaluasi require() saat barisnya berjalan. Kamu bisa menaruhnya di dalam if, menghitung path-nya saat runtime, dan memuat module secara kondisional:

ES Modules bersifat statis. Engine mem-parsing semua pernyataan import sebelum menjalankan kode apa pun, membangun graf dependensi, dan menyelesaikan semuanya di awal. Itulah sebabnya path harus berupa string literal dan import hanya muncul di tingkat atas.

Keuntungannya: tools bisa melihat seluruh graf module tanpa menjalankan apa pun. Begitulah bundler melakukan tree-shaking (membuang export yang tidak terpakai), editor memberimu autocomplete yang akurat, dan browser bisa mengambil module secara paralel.

Saat kamu benar-benar butuh pemuatan dinamis di ESM, pakai import(), sebuah ekspresi mirip fungsi yang mengembalikan Promise:

Cara Node Menentukan Sistem yang Dipakai Sebuah File

Satu proyek Node bisa berisi kedua jenis file. Node menentukan sistem yang dipakai setiap file dengan melihat dua hal:

  • Ekstensi file: .mjs selalu ESM, .cjs selalu CommonJS.
  • Field "type" di package.json terdekat: "module" berarti file .js adalah ESM, "commonjs" (default kalau tidak ada) berarti file itu CJS.
// package.json
{
    "name": "my-app",
    "type": "module"
}

Dengan "type": "module", file hello.js biasa di package yang sama memakai import/export. Taruh hello.cjs di sebelahnya, maka satu file itu memakai require. Dengan cara inilah sebuah proyek bisa bermigrasi bertahap, atau sebuah library bisa merilis kedua versi berdampingan.

Jebakan untuk pemula: di file ESM, require dan module.exports sama sekali tidak ada. Kalau kamu memakainya karena kebiasaan, kamu akan mendapat ReferenceError.

Interop: Mencampur Keduanya

Kamu sering perlu membuat file ESM memakai package CommonJS, atau sebaliknya. Aturannya tidak simetris.

ESM yang meng-import CommonJS langsung berfungsi. Objek module.exports milik CJS menjadi default export:

// app.mjs
import greet from "./greet.cjs";
console.log(greet("Rosa"));

Named import dari CommonJS kadang berfungsi, karena Node mencoba mendeteksi named export secara statis, tetapi agar andal, ambil default-nya lalu lakukan destructuring:

import pkg from "./utils.cjs";
const { parse, stringify } = pkg;

CommonJS yang meng-import ESM adalah arah yang merepotkan. Kamu tidak bisa melakukan require() pada ES module, karena akan memunculkan ERR_REQUIRE_ESM. Jalan keluarnya adalah import() dinamis, yang juga berfungsi di CJS dan mengembalikan Promise:

Node modern (22+) menambahkan require() synchronous untuk ESM dalam kondisi tertentu, tetapi import() dinamis adalah jawaban yang paling portabel.

Perbedaan Perilaku Lain yang Perlu Diketahui

Selain sintaks, kedua sistem berbeda dalam beberapa detail yang sesekali bikin repot:

Beberapa lagi:

  • this di tingkat atas. Di CJS, this adalah module.exports. Di ESM, nilainya undefined. ESM selalu berjalan dalam strict mode.
  • __dirname dan __filename. CJS memberikannya secara gratis. Di ESM, kamu menurunkannya dari import.meta.url:
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
  • Ekstensi file dalam import. ESM mewajibkan ekstensi ("./utils.js", bukan "./utils") untuk path relatif. CJS lebih toleran.
  • Live binding vs salinan. Import ESM adalah referensi hidup ke variabel milik module yang meng-export-nya. CJS memberimu salinan dari apa pun yang disimpan ke module.exports saat dimuat. Kebanyakan kode tidak pernah menyadarinya, tetapi ini penting untuk dependensi melingkar.

Mana yang Sebaiknya Kamu Pakai?

Untuk proyek baru: ES Modules. Atur "type": "module" di package.json dan jangan menoleh lagi. ESM adalah standar bahasa, bekerja dengan cara yang sama di browser dan Node, mendukung top-level await, dan tools dibuat dengan mempertimbangkannya.

Tetap pakai CommonJS kalau:

  • Kamu memelihara codebase CJS yang sudah ada dan migrasinya belum sepadan.
  • Kamu merilis library yang harus mendukung versi Node yang sangat lama atau pengguna yang tidak bisa memakai ESM.
  • Dependensi penting hanya merilis CJS dan interop-nya berantakan. (Sekarang jarang, tetapi masih terjadi.)

Bahkan dalam kasus itu, kamu akan terus membaca kode ESM, karena semua yang dirilis di npm beberapa tahun terakhir cenderung ke arah sana. Fasih di keduanya itu wajib; fasih dalam kebiasaan sistem yang sedang kamu tulis juga wajib.

Daftar Periksa Singkat

Saat membuka file baru, tanyakan:

  • Apakah file ini memakai import/export atau require/module.exports? Jangan dicampur.
  • Apa isi "type" di package.json terdekat?
  • Kalau kamu meng-import sebuah package, cek package.json-nya: apakah package itu merilis ESM, CJS, atau keduanya?
  • Kalau kamu mendapat ERR_REQUIRE_ESM, berarti kamu berada di CJS dan mencoba memuat ESM. Ganti dengan import() dinamis atau pindahkan pemanggilnya ke ESM.

Sembilan puluh persen kebingungan soal module di Node adalah salah satu dari keempat hal itu.

Berikutnya: Dasar npm

Module adalah cara memecah kode milikmu ke beberapa file. Langkah berikutnya adalah memakai kode yang ditulis orang lain, dan untuk itulah npm ada. Kita akan membahas cara menginstal package, rentang semver, dan bagian alur kerja npm yang benar-benar kamu pakai sehari-hari.

Pertanyaan yang sering diajukan

Apa bedanya require dan import di JavaScript?

require adalah cara CommonJS memuat module: synchronous, berjalan di titik ia dipanggil, dan mengembalikan apa pun yang disimpan module itu ke module.exports. import adalah sintaks ES Modules: statis, diangkat ke bagian atas file, dan dianalisis sebelum kode berjalan. Keduanya juga berbeda dalam hal nilai this, cara menyelesaikan dependensi melingkar, dan apakah top-level await diizinkan.

Sebaiknya aku memakai CommonJS atau ES Modules di proyek Node baru?

Pakai ES Modules. Atur "type": "module" di package.json dan tulis import/export. ESM adalah standar resmi, bekerja di browser dan Node, dan mendukung top-level await. CommonJS masih muncul di package dan tooling lama, jadi kamu akan tetap membacanya walaupun tidak menulisnya.

Bisakah aku mencampur require dan import dalam satu proyek?

Bisa, dengan aturan. File .mjs atau package dengan "type": "module" memakai ESM; .cjs atau "type": "commonjs" memakai CJS. ESM bisa melakukan import pada module CommonJS (module.exports muncul sebagai default export). CommonJS tidak bisa langsung melakukan require() pada module ESM: kamu harus memakai import() dinamis, yang mengembalikan Promise.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai