Clojure adalah bahasa pemrograman fungsional yang berjalan secara native di JVM. Pustaka ClojureScript memungkinkan kita untuk mengkompilasi Clojure ke dalam JavaScript dan menjalankannya di browser. Dalam artikel ini, saya akan memperkenalkan Anda kepada Clojure dan ClojureScript, mendemonstrasikan bagaimana aplikasi web Anda berikutnya dapat menjadi langkah pertama Anda ke dunia yang menyenangkan sebagai pengembang Clojure.
Kita akan membahas beberapa alasan mengapa ClojureScript sangat cocok untuk pengembangan web, terutama dengan React melalui library yang disebut Reagent. Kemudian kita akan menggunakan alat bantu pembuatan yang disebut shadow-cljs untuk menulis aplikasi web dasar dan interaktif.
Mengapa ClojureScript?
Ada beberapa alasan kuat untuk mempertimbangkan Clojure untuk segala jenis pengembangan, baik di server maupun frontend. Pertama, bahasa Clojure itu sendiri adalah bahasa yang fungsional, dan keabadian adalah idiom default. Selain itu, pustaka inti Clojure, struktur data, dan literal sangat ringkas dan kuat.
Di server, Clojure akan berjalan di JVM, dan interop dengan Java memungkinkannya untuk mendapatkan keuntungan dari ekosistem Java. Dengan menggunakan ClojureScript, Clojure juga dikompilasi ke JavaScript, dan dapat bekerja sama dengan pustaka JavaScript untuk memberikan keuntungan yang sama. Biasanya, kita menyebut file sumber Clojure yang dimaksudkan untuk dikompilasi ke JavaScript sebagai “file ClojureScript”, meskipun masih ditulis dalam bahasa Clojure.
Salah satu fitur favorit saya di Clojure adalah REPL (Read, Eval, Print, Loop), sebuah lingkungan interaktif di mana Anda dapat mengeksekusi kode Anda, sangat mengurangi umpan balik untuk pengembangan berulang dan mendorong eksplorasi dan eksperimen.
Clojure sendiri memiliki komunitas yang aktif dan menyenangkan, sehingga Anda dapat memecahkan masalah apa pun yang mungkin Anda temui. Ada banyak sumber daya luar biasa yang tersedia untuk mempelajari Clojure, tetapi untuk saat ini, kita akan mulai dengan kursus kilat.
- :span: Sebuah kata kunci
- [:div :span]: Sebuah vektor dengan kata kunci di dalamnya
- {:width 100}: Sebuah peta dengan kunci :width dan nilai 100
- (defn double-me [n]: Sebuah fungsi bernama double-me yang mengambil satu argumen
- (* 2 n)): Mengembalikan 2 * n
- (double-me 4): Contoh pemanggilan fungsi double-me dengan 4
Selamat, Anda baru saja mempelajari hampir seluruh sintaks yang digunakan oleh Clojure!
Tool Membuat ClojureScript
Ketika harus memilih alat yang tepat untuk tugas tertentu, Anda biasanya memiliki beberapa opsi yang dapat diandalkan untuk dipilih. Demikian juga halnya dengan alat bantu pembuatan ClojureScript.
Kompiler ClojureScript itu sendiri dianggap cukup rendah, yang berarti kompiler ini akan mengubah Clojure menjadi JavaScript tetapi tidak lebih dari itu. Selama sepuluh tahun terakhir, beberapa alat bantu build telah muncul yang mendukung kompilasi tambahan saat Anda mengembangkan, meluncurkan REPL, menjalankan tes, dan memuat ulang browser diantaranya:
Dalam artikel ini, kita akan menggunakan shadow-cljs, yang memiliki perkembangan terbaru pada saat penulisan dan memiliki dukungan terbaik untuk menggunakan pustaka dari npm. Dengan kata lain, ini adalah pilihan yang bagus saat ini.
Cara Menggunakan shadow-cljs
Alasan lain yang sangat baik untuk memilih shadow-cljs adalah dokumentasinya yang komprehensif, termasuk peramban cepat, yang dapat menghasilkan penyiapan yang sedang berjalan pada proyek kerangka hanya dengan beberapa perintah.
Pertama, kloning proyek dan instal dependensi:
git clone https://github.com/shadow-cljs/quickstart-browser.git quickstart cd quickstart npm install
Sekarang, Anda siap untuk memulai shadow-cljs, yang akan mengkompilasi kode Anda, menyajikannya ke peramban, dan mengawasi sistem berkas untuk setiap perubahan yang Anda buat:
npx shadow-cljs watch app
Untuk melihat kerangka kerja, Anda dapat membuka http://localhost:8020 di browser Anda. Diagram berikut ini akan membantu Anda memahami apa yang terjadi di sini:
Pengembangan berulang ClojureScript
Kode sumber yang dihasilkan dari mulai cepat berada di bawah quickstart/src/main/starter/browser.cljs. Kode ini berisi beberapa fungsi yang merupakan pengait untuk membantu Anda memulai siklus pengembangan:
- init: Dipanggil sekali ketika halaman dimuat, memanggil start juga
- start: Dipanggil setelah kode baru dimuat
- stop: Dipanggil sebelum kode baru dimuat
Mari kita coba mendorong elemen DOM ke dalam halaman. Tambahkan kode berikut ini ke fungsi start sehingga terlihat seperti berikut ini:
(defn ^:dev/after-load start [] (.appendChild (js/document.getElementById "app") (js/document.createTextNode "Hello, world")))
Ketika Anda menyimpan berkas sumber ini dengan perubahan yang Anda buat, shadow-cljs akan mengkompilasi dan mendorong kode yang telah diperbarui ke dalam peramban untuk dievaluasi. Anda akan melihat “Hello, World!” muncul. Bersama dengan kecepatan kompilasi yang cepat dan fungsionalitas hot reload, REPL benar-benar dapat meningkatkan kecepatan pengembangan Anda.
Reagent: Kerangka kerja rendering untuk ClojureScript
React adalah library yang populer untuk merender halaman. Pendekatannya yang fungsional dan tidak dapat diubah sangat cocok untuk Clojure, dan ada beberapa pembungkus Clojure yang menyediakan API yang lebih idiomatis. Reagent adalah pilihan yang aman bagi kita di sini.
Meskipun hanya sebagai pembungkus, Reagent dapat bekerja lebih cepat daripada React biasa. Struktur data Clojure yang tidak dapat diubah dapat dibandingkan dengan objek JavaScript secara lebih efisien, sehingga menghasilkan keputusan yang lebih cepat mengenai kapan harus me-render ulang sebuah komponen.
Tambahkan Reagent ke :dependencies key di dalam shadow-cljs.edn:
:dependencies ` [[reagent "1.1.1"]]
Untuk mendapatkan dependensi baru, kita harus memulai ulang dengan npx shadow-cljs watch app. Sekarang, kita dapat menulis komponen Reagent pertama kita. Komponen ini berbentuk fungsi Clojure sederhana yang mengembalikan struktur data Clojure yang merepresentasikan HTML, yang dikenal dengan nama Hiccup, sesuai dengan nama pustaka yang mempopulerkan format tersebut.
Hiccup jauh lebih ringkas daripada HTML dan dapat digunakan dengan baik dengan editor kode struktural:
(defn- hello-world [] [:ul [:li "Hello"] [:li {:style {:color "red"}} "World!"]])
Kode di atas mewakili HTML berikut ini:
<ul> <li>Hello</li> <li style="color: red;">World!</li> </ul>
Selanjutnya, kita perlu memerlukan reagent.dom namespace:
(ns starter.browser (:require [reagent.dom :as rd]))
Kemudian, kita menggunakannya untuk memasang komponen ke dalam DOM dengan mengubah fungsi start menjadi seperti berikut:
(defn ^:dev/after-load start [] (rd/render [hello-world] (js/document.getElementById "app")))
Setelah Anda menyimpan berkas, daftar tersebut akan di-render di browser Anda, dan kita sekarang memiliki dasar-dasar rendering di aplikasi kita.
Interaktivitas dengan ClojureScript
Hal-hal mulai menjadi menarik ketika kita menambahkan interaktivitas. Lagi pula, jika halaman Anda statis, maka Anda tidak memerlukan JavaScript sama sekali. Namun, sebelum kita mengimplementasikan sesuatu, saya ingin memperkenalkan Anda pada salah satu kekuatan super Clojure, yaitu REPL.
REPL
REPL adalah sebuah perintah untuk mengevaluasi ekspresi Clojure. Ini adalah alat yang ampuh yang membantu Anda mengeksplorasi dan mencoba solusi, mengurangi lingkaran umpan balik dan membuat Anda lebih produktif:
cljs.user=> (+ 1 1) 2
Karena kita sedang membangun sebuah halaman web, akan sangat membantu jika kita menggunakan peramban untuk melakukan evaluasi di REPL. Sangat mudah untuk memulainya. Pada terminal baru, jalankan perintah berikut:
npx shadow-cljs cljs-repl app
Ketika Anda membuka http://localhost:8020 di peramban Anda, maka akan ada beberapa kode yang diinjeksikan oleh shadow-cljs untuk membuka WebSocket kembali ke server.
Ketika Anda mengetikkan perintah ke dalam REPL ini, perintah tersebut akan dikirim melalui WebSocket ini untuk dievaluasi di dalam peramban, dan hasilnya akan dikembalikan ke REPL Anda.
Cobalah contoh penambahan di atas. Bagaimana kita tahu bahwa itu dievaluasi di browser? Coba yang berikut ini:
(js/alert "Hello, world")
Anda akan melihat dialog peringatan pada browser Anda, seolah-olah Anda telah mengetik alert (“Hello, world”) pada konsol pengembang browser. Untuk keluar dari REPL, tekan Ctrl+D. Mari kita gunakan alat ini untuk membantu mengimplementasikan state interaktif dalam aplikasi kita.
Atom
Anda mungkin bertanya-tanya bagaimana kita dapat mengimplementasikan state yang dapat berubah ketika struktur data Clojure tidak dapat diubah. Clojure adalah bahasa pragmatis, jadi sebenarnya ada konstruksi yang dapat berubah di dalamnya, tetapi mereka ditandai dengan hati-hati, sehingga jelas di mana Anda berurusan dengan sesuatu yang istimewa.
Atom adalah referensi yang dapat diubah ke nilai yang tidak dapat diubah. Anda bisa membuatnya dengan (atom nil). Referensi dapat dimutasi menggunakan swap! atau reset!, dengan ! adalah notasi konvensional untuk mutasi. Anda bisa mendereferensi atom untuk mendapatkan nilainya dengan menggunakan @ atau deref.
Cobalah kode di bawah ini di REPL Anda:
(def counter (atom 0)) @counter ;; => 0 (swap! counter inc) @counter ;; => 1
Kita akan menggunakan atom dalam aplikasi kita untuk menyimpan beberapa state, tetapi kita akan menggunakan atom versi Reagent. Atom memiliki antarmuka yang sama dengan atom Clojure, tetapi memiliki kekuatan super rahasia: ketika berubah, atom dapat memberi tahu React untuk menggambar ulang DOM.
Kita membutuhkan namespace inti Reagent:
(ns starter.browser (:require [reagent.core :as r] [reagent.dom :as rd]))
Kemudian, kita dapat membuat status awal:
(defonce state (r/atom {:items ["Hello" "World!"]}))
Kita dapat menulis komponen baru dengan sebuah kotak input untuk menambahkan item ke dalam state:
(defn- new-item [] [:input {:type "text" :placeholder "Enter a new item" :on-key-down (fn [e] (when (= "Enter" (.-key e)) (swap! state update :items conj (.. e -target -value))))}])
Terakhir, ubah komponen “Halo, Dunia!” Anda untuk membuat daftar item dari negara bagian:
(defn- hello-world [] [:div [new-item] [:ul (map (fn [item] [:li {:key item} item]) (:items @state))]])
Anda akan melihat bahwa “Halo, Dunia!” juga menyertakan komponen new-item sebagai anak. Sekarang, kode di atas dirender sebagai berikut, dan ketika kita mengetikkan item baru ke dalam input dan menekan enter, item baru tersebut akan bergabung ke dalam daftar.
Anda juga dapat menggunakan REPL untuk memeriksa atom ini:
`(in-ns 'starter.browser) @state ;; => {:items ["Hello", "World!"]}
Anda juga dapat mengubahnya, dengan status baru yang langsung dirender di browser Anda:
(swap! state update :items conj "Hello REPL!")
Ini hanyalah sekilas tentang kegunaan REPL saat mengembangkan aplikasi. REPL adalah pendamping konstan yang memadukan kode, data, dan state, sehingga Anda dapat memeriksa semuanya dengan kekuatan penuh dari bahasa ini.