Ön Uç Geliştirme için ClojureScript'i Ortaya Çıkarma

Yayınlanan: 2022-03-11

Bu tanıtımı okurken muhtemelen kafanızda iki ana düşünce vardır:

  1. ClojureScript nedir?
  2. Bu benim ilgi alanlarımla alakalı değil.

Fakat bekle! İşte bu noktada yanılıyorsunuz - ve bunu size kanıtlayacağım. 10 dakikanızı ayırmaya hazırsanız, ClojureScript'in ön uç ve React-y uygulamalarını nasıl eğlenceli, hızlı ve en önemlisi işlevsel hale getirebileceğini göstereceğim.

Bazı ClojureScript Eğitimi Ön Koşulları

  • Lisp bilgisi gerekli değildir. Bu blog gönderisine dağılmış olan tüm kod örneklerini açıklamak için elimden gelenin en iyisini yapacağım!
  • Ancak, biraz ön okuma yapmak istiyorsanız, Clojure'u (ve buna bağlı olarak ClojureScript) kullanmaya başlamak için tek adres olan https://www.braveclojure.com/'u şiddetle tavsiye ederim.
  • Clojure ve ClojureScript ortak bir dili paylaşırlar—bunlara genellikle Clojure[Script] ile aynı anda değineceğim.
  • React ve genel ön uç bilgi birikimine sahip olduğunuzu varsayıyorum.

ClojureScript Nasıl Öğrenilir: Kısa Versiyon

Yani ClojureScript öğrenmek için fazla zamanınız yok ve sadece tüm bunların nereye gittiğini görmek istiyorsunuz. İlk önce ilk şeyler, ClojureScript nedir?

ClojureScript web sitesinden: ClojureScript, JavaScript'i hedefleyen Clojure için bir derleyicidir. Google Closure optimizasyon derleyicisinin gelişmiş derleme moduyla uyumlu JavaScript kodu yayar.

Diğer şeylerin yanı sıra ClojureScript'in sunabileceği çok şey var:

  • İşlevsel programlamaya sahip çok paradigmalı bir programlama dilidir - işlevsel programlamanın kod okunabilirliğini iyileştirdiği ve daha az kodla daha fazla yazmanıza yardımcı olduğu bilinmektedir.
  • Varsayılan olarak değişmezliği destekler; tüm çalışma zamanı sorunlarına elveda deyin!
  • Veri odaklıdır: kod ClojureScript'teki verilerdir. Çoğu Clojure[Script] uygulaması, bazı temel veri yapıları üzerinde çalışan bir dizi fonksiyona indirgenebilir, bu da hata ayıklamayı basitleştirir ve kodu süper okunaklı hale getirir.
  • Basit! ClojureScript'i kullanmaya başlamak kolaydır; süslü anahtar kelimeler yoktur ve çok az sihir vardır.
  • Harika bir standart kütüphaneye sahiptir. Bu şeyde her şey var.

Bu arada, bu solucan kutusunu bir örnekle açalım:

 (defn component [] [:div "Hello, world!"])

Lisp lehçelerine veya ClojureScript'e aşina olmayanlar için not: Bu örneğin en önemli kısımları :div , [] ve () dir. :div , <div> öğesini temsil eden bir anahtar sözcüktür. [] , Java'daki ArrayList çok benzeyen bir vektördür ve () , LinkedList çok benzeyen bir dizidir. Bu yazıya daha sonra daha ayrıntılı olarak değineceğim!

Bu, ClojureScript'teki bir React bileşeninin en temel biçimidir. İşte bu - sadece bir anahtar kelime, bir dizi ve bir sürü liste.

Peşa! bunun JSX veya TSX'teki “merhaba dünya”dan önemli ölçüde farklı olmadığını söylüyorsunuz:

 function component() { return ( <div> "Hello, world!" </div> ); }

Ancak, bu temel örnekten bile görebileceğimiz bazı önemli farklılıklar var:

  • Gömülü dil yoktur; ClojureScript örneğindeki her şey bir dize, bir anahtar kelime veya bir listedir.
  • Kısa ve öz ; listeler, HTML kapanış etiketlerinin fazlalığı olmadan ihtiyacımız olan tüm ifadeyi sağlar.

Bu iki küçük farkın, sadece nasıl kod yazdığınız değil, aynı zamanda kendinizi nasıl ifade ettiğiniz konusunda da büyük sonuçları var!

Bu nasıl, soruyorsun? Haydi savaşa atlayalım ve ClojureScript'in bizim için başka neler hazırladığını görelim…

İlişkili:
  • Elm Programlama Dilini Kullanmaya Başlarken
  • Elixir Programlama Dili ile Başlarken

Yapı taşları

Bu ClojureScript öğreticisi boyunca, Clojure[Script]'i harika yapan şeyin ne olduğunu çok fazla araştırmamaya çalışacağım (ki bu pek çok şeydir, ama konuyu dalıyorum). Bununla birlikte, burada yapabileceklerimizin genişliğini kavramak için bazı temel kavramların ele alınması faydalı olacaktır.

Deneyimli Clojurista'lar ve Lisp'liler için, bir sonraki bölüme atlamaktan çekinmeyin!

İlk önce ele almam gereken üç ana kavram var:

anahtar kelimeler

Clojure[Script], Anahtar Kelime adında bir konsepte sahiptir. Sabit bir dize (örneğin, Java'da) ile bir anahtar arasında bir yerde bulunur. Kendilerini değerlendiren sembolik tanımlayıcılardır .

Örnek olarak, :cat anahtar kelimesi her zaman :cat atıfta bulunur ve asla başka bir şeye atıfta bulunmaz. Tıpkı Java'da olduğu gibi şunları söyleyebilirsiniz:

 private static const String MY_KEY = "my_key"; // ... myMap.put(MY_KEY, thing); // ... myMap.get(MY_KEY);

… Clojure'da basitçe:

 (assoc my-map :my-key thing) (my-map :my-key) ; equivalent to (:my-key my-map) ...nice and flexible!

Ayrıca not edin: Clojure'da bir harita hem bir koleksiyondur (tıpkı Java HashMap gibi değer anahtarlarının) hem de içeriğine erişmek için bir işlevdir. Düzenli!

Listeler

Clojure[Script]'in bir Lisp lehçesi olması, listelere çok fazla önem verdiği anlamına gelir. Daha önce de belirttiğim gibi, bilinmesi gereken iki ana şey var:

  1. [] bir vektördür, ArrayList çok benzer.
  2. () bir LinkedList gibi bir dizidir.

Clojure[Script]'te bir şeyler listesi oluşturmak için aşağıdakileri yaparsınız:

 [1 2 3 4] ["hello" "world"] ["my" "list" "contains" 10 "things"] ; you can mix and match types ; in Clojure lists!

Diziler için biraz farklıdır:

 '(1 2 3 4) '("hello" "world")

Başlayan ' sonraki bölümde açıklanmıştır.

Fonksiyonlar

Son olarak, fonksiyonlarımız var. Clojure[Script] içindeki bir işlev , başına ' eklenmeden yazılan bir dizidir . Bu listenin ilk öğesi işlevin kendisidir ve aşağıdaki öğelerin tümü argümanlar olacaktır. Örneğin:

 (+ 1 2 3 4) ; -> 10 (str "hello" " " "world") ; -> "hello world" (println "hi!") ; prints "hi!" to the console (run-my-function) ; runs the function named `run-my-function`

Bu davranışın bir sonucu, bir fonksiyonun tanımını, onu gerçekten çalıştırmadan oluşturabilmenizdir! Program değerlendirilirken yalnızca 'çıplak' bir dizi yürütülecektir.

 (+ 1 1) ; -> 2 '(+ 1 1); -> a list of a function and two numbers

Bu daha sonra alakalı hale gelecektir!

Fonksiyonlar birkaç şekilde tanımlanabilir:

 ; A normal function definition, assigning the function ; to the symbol `my-function` (defn my-function [arg1 arg2] (+ arg1 arg2)) ; An anonymous function that does the same thing as the above (fn [arg1 arg2] (+ arg1 arg2)) ; Another, more concise variation of the above #(+ %1 %2)

Daha Yakın Bir Muayene

Şimdi temelleri ele aldığımıza göre, burada neler olduğunu görmek için biraz daha ayrıntıya girelim.

ClojureScript'te React, genellikle Reagent adlı bir kitaplık kullanılarak yapılır. Reagent, HTML'yi temsil etmek için Hıçkırık ve sözdizimini kullanır. Hiccup deposunun wiki'sinden:

"Hıçkırık, Clojure veri yapılarını şu şekilde çevirir:"

 [:a {:href "http://github.com"} "GitHub"]

"Bunun gibi HTML dizelerine:"

 <a href="http://github.com">GitHub</a>

Basitçe söylemek gerekirse, listenin ilk öğesi HTML öğesi türü olur ve geri kalanlar o öğenin içeriği olur. İsteğe bağlı olarak, daha sonra o öğeye eklenecek olan bir nitelik haritası sağlayabilirsiniz.

Öğeler, yalnızca üst öğelerinin listesinin içine yerleştirilerek iç içe yerleştirilebilir! Bu en kolay bir örnekle görülür:

 [:div [:h1 "This is a header"] [:p "And in the next element we have 1 + 1"] [:p (+ 1 1)]]

Herhangi bir eski işlevi veya genel Clojure sözdizimini, açıkça bir gömme yöntemi bildirmek zorunda kalmadan yapımıza nasıl yerleştirebileceğimize dikkat edin. Sonuçta bu sadece bir liste!

Ve daha da iyisi, bu çalışma zamanında ne olarak değerlendirilir?

 [:div [:h1 "This is a header"] [:p "And in the next element we have 1 + 1"] [:p 2]]

Elbette anahtar kelimelerin ve içeriğin bir listesi! Komik tipler yok, sihirli gizli yöntemler yok. Bu sadece eski bir şeyler listesi. Bu listeyi istediğiniz kadar ekleyebilir ve oynayabilirsiniz - ne görüyorsanız onu elde edersiniz.

Düzeni Hiccup ve mantık ve olay işlemeyi Reagent yaparken, tamamen işlevsel bir React ortamı elde ederiz.

Daha Karmaşık Bir Örnek

Pekala, bunu bazı bileşenlerle biraz daha birleştirelim. React (ve Reagent) ile ilgili büyülü şeylerden biri, görünüm ve yerleşim mantığınızı modüllere ayırmanızdır ve daha sonra uygulamanız boyunca yeniden kullanabilirsiniz.

Bir düğme ve bazı basit mantık gösteren basit bir bileşen oluşturduğumuzu varsayalım:

 ; widget.cljs (defn component [polite?] [:div [:p (str "Do not press the button" (when polite? ", please."))] [:input {:type "button" :value "PUSH ME" :on-click #(js/alert "What did I tell you?")}]])

Adlandırmayla ilgili kısa not: Clojure'daki modüller normalde ad alanlıdır, bu nedenle widget.cljs ad alanı widget altında içe aktarılabilir. Bu, üst düzey component işlevine widget/component olarak erişileceği anlamına gelir. Modül başına yalnızca bir üst düzey bileşen olmasını seviyorum, ancak bu bir stil tercihidir; bileşen işlevinize polite-component veya widget-component gibi bir ad vermeyi tercih edebilirsiniz.

Bu basit bileşen bize isteğe bağlı olarak kibar bir pencere öğesi sunar. (when polite? ", please.") ", lütfen" olarak değerlendirilir ", please." ne zaman polite? == true polite? == true ve false olduğunda nil olur.

Şimdi bunu app.cljs yerleştirelim:

 (defn app [] [:div [:h1 "Welcome to my app"] [widget/component true]])

Burada, widget'ımızı bir listenin ilk öğesi olarak adlandırarak uygulama bileşenimize yerleştirdik - tıpkı HTML anahtar kelimeleri gibi! Daha sonra herhangi bir alt öğeyi veya parametreyi aynı listenin diğer öğeleri olarak sağlayarak bileşene iletebiliriz. Burada sadece true 'yi geçiyoruz, yani widget'ımızda polite? == true polite? == true ve böylece kibar sürümü elde ederiz.

Uygulama fonksiyonumuzu şimdi değerlendirecek olsaydık, aşağıdakileri alırdık:

 [:div [:h1 "Welcome to my app"] [widget/component true]] ; <- widget/component would look more like a ; function reference, but I have kept it ; clean for legibility.

widget/component nasıl değerlendirilmediğine dikkat edin! (Kafanız karıştıysa, İşlevler bölümüne bakın.)

DOM ağacınızdaki bileşenler yalnızca güncellendiyse değerlendirilir (ve böylece sahne arkasında gerçek React nesnelerine dönüştürülür), bu da işleri güzel ve hızlı tutar ve herhangi bir zamanda uğraşmanız gereken karmaşıklık miktarını azaltır.

İlgilenenler için bu konuyla ilgili daha fazla ayrıntı Reaktif belgelerinde bulunabilir.

Tüm Yolu Aşağı Listeler

Ayrıca, DOM'nin nasıl yalnızca bir liste listesi olduğuna ve bileşenlerin yalnızca liste listelerini döndüren işlevler olduğuna dikkat edin. ClojureScript'i öğrenirken bu neden bu kadar önemli?

Çünkü işlevlere veya listelere yapabileceğiniz her şeyi bileşenlere de yapabilirsiniz.

ClojureScript gibi bir Lisp lehçesi kullanarak bileşik getiriler almaya başladığınız yer burasıdır: Bileşenleriniz ve HTML öğeleriniz, diğer normal veriler gibi işleyebileceğiniz birinci sınıf nesneler haline gelir! Şunu tekrar söylememe izin verin:

Bileşenler ve HTML öğeleri, Clojure dilinde birinci sınıf desteklenen nesnelerdir!

Bu doğru, beni duydun. Neredeyse Lisps, listeleri işlemek için tasarlanmış gibi (ipucu: öyleydi.)

Bu, aşağıdakileri içerir:

  • Numaralandırılmış bir listenin öğeleri üzerinde eşleme:
 (def words ["green" "eggs" "and" "ham"]) (defn li-shout [x] [:li (string/uppercase x)) (concat [:ol] (map li-shout words) ; becomes [:ol [:li "GREEN"] [:li "EGGS"] [:li "AND"] [:li "HAM"]]
  • Sarma bileşenleri:
 ; in widget.cljs (defn greeting-component [name] [:div [:p (str "Hiya " name "!")]]) ; ... (def shouty-greeting-component #(widget/greeting-component (string/uppercase %))) (defn app [] [:div [:h1 "My App"] [shouty-greeting-component "Luke"]]) ; <- will show Hiya LUKE!
  • Enjekte etme özellikleri:
 (def default-btn-attrs {:type "button" :value "I am a button" :class "my-button-class"}) (defn two-button-component [] [:div [:input (assoc default-btn-attrs :on-click #(println "I do one thing"))] [:input (assoc default-btn-attrs :on-click #(println "I do a different thing"))]])

Listeler ve haritalar gibi düz eski veri türleriyle uğraşmak, bir sınıfa benzeyen her şeyden çok daha basittir ve uzun vadede çok daha güçlü olur!

Bir Model Ortaya Çıkıyor

Tamam, özetleyelim. ClojureScript eğitimimiz şu ana kadar ne gösterdi?

  • Her şey en basit işlevselliğe indirgenmiştir; öğeler yalnızca listelerdir ve bileşenler yalnızca öğeleri döndüren işlevlerdir.
  • Bileşenler ve öğeler birinci sınıf nesneler olduğundan, daha azıyla daha fazlasını yazabiliyoruz.

Bu iki nokta, Clojure ve işlevsel programlama değerlerine tam olarak uyar; kod, manipüle edilecek verilerdir ve karmaşıklık, daha az karmaşık parçaların bağlanmasıyla oluşturulur. Programımızı (bu örnekteki web sayfamız) veri (listeler, fonksiyonlar, haritalar) olarak sunuyoruz ve Reagent'ın devraldığı ve React koduna dönüştürdüğü son ana kadar bu şekilde tutuyoruz. Bu, kodumuzu yeniden kullanılabilir hale getirir ve en önemlisi, çok az sihirle okunması ve anlaşılması çok kolaydır.

Şıklaşmak

Artık bazı temel işlevlere sahip bir uygulama yapmayı biliyoruz, bu yüzden onu nasıl iyi gösterebileceğimize geçelim. Buna yaklaşmanın birkaç yolu vardır, en basiti stil sayfalarıyla ve bileşenlerindeki sınıflarına atıfta bulunmaktır:

 .my-class { color: red; }
 [:div {:class "my-class"} "Hello, world!"]

Bu tam olarak beklediğiniz gibi olacak ve bize güzel, kırmızı bir "Merhaba dünya!" Metin.

Ancak, görünüm ve mantık kodunu bileşenlerinize yerleştirmek ve sonra stilinizi bir stil sayfasına ayırmak neden tüm bu soruna katlanıyorsunuz? diller de!

Neden CSS'imizi bileşenlerimizde kod olarak yazmıyorsunuz (burada bir temaya bakın?). Bu bize bir takım avantajlar sağlayacaktır:

  • Bir bileşeni tanımlayan her şey aynı yerdedir.
  • Akıllı nesil sayesinde sınıf adlarının benzersiz olması garanti edilebilir.
  • CSS dinamik olabilir, verilerimiz değiştikçe değişir.

Kod içinde CSS'nin en sevdiğim kişisel çeşidi Clojure Stil Sayfaları (cljss). Gömülü CSS aşağıdaki gibi görünür:

 ;; -- STYLES ------------------------------------------------------------ (defstyles component-style [] {:color "red" :width "100%"}) ;; -- VIEW -------------------------------------------------------------- (defn component [] [:div {:class (component-style)} "Hello, world!"])

defstyles , bizim için benzersiz bir sınıf adı oluşturacak bir işlev yaratır (bileşenlerimizi içe aktaran herkes için harikadır.)

cljss'in sizin için yapabileceği pek çok başka şey var (besteleme stilleri, animasyonlar, öğe geçersiz kılmaları vb.), burada ayrıntılarına girmeyeceğim. Kendiniz kontrol etmenizi tavsiye ederim!

Bir ClojureScript Uygulamasının Parçalarını Birleştirme

Son olarak, hepsini birbirine yapıştırmak için gereken yapıştırıcı var. Neyse ki, bir proje dosyasının ve bir index.html yanı sıra, burada ortak metin minimumda.

Şunlara ihtiyacınız var:

  • Proje tanım dosyanız, project.clj . Herhangi bir Clojure projesinin temeli olan bu, doğrudan GitHub'dan bile olsa bağımlılıklarınızı ve diğer derleme özelliklerini ( build.gradle veya package.json benzer) tanımlar.
  • Reaktif uygulaması için bağlayıcı bir nokta görevi gören bir index.html .
  • Bir geliştirme ortamı ve son olarak Reagent uygulamanızı başlatmak için bazı kurulum kodları.

Bu ClojureScript öğreticisinin tam kod örneğini GitHub'da bulabilirsiniz.

Yani bu kadar (şimdilik). Umarım, ister bir Lisp lehçesini (Clojure[Script] veya başka bir şekilde) kontrol etmek için, ister kendi Reagent uygulamanızı yaparken elinizi denemek için olsun, en azından merakınızı biraz olsun giderebilmişimdir! Sana söz veriyorum pişman olmayacaksın.

Re-frame kullanarak durum yönetimi hakkında konuştuğum bu Başlarken başlıklı makalenin devamında bana katılın - ClojureScript'te Redux'a merhaba deyin!