프론트엔드 개발을 위한 ClojureScript 발굴

게시 됨: 2022-03-11

이 소개를 읽는 동안 머리에 두 가지 주요 생각이 있을 것입니다.

  1. ClojureScript 란 무엇입니까?
  2. 이것은 내 관심사와 관련이 없습니다.

하지만 기다려! 그것이 당신이 틀렸다는 것입니다. 그리고 나는 당신에게 그것을 증명할 것입니다. 10분의 시간을 투자할 의향이 있다면 ClojureScript가 프론트엔드 및 React-y 애플리케이션 작성을 재미있고 빠르며 가장 중요하게는 기능적 으로 만드는 방법을 보여 드리겠습니다.

일부 ClojureScript 튜토리얼 전제 조건

  • Lisp 지식은 필요하지 않습니다. 이 블로그 게시물에 흩어져 있는 코드 샘플을 설명하기 위해 최선을 다할 것입니다!
  • 그러나 사전 읽기를 조금 하고 싶다면 Clojure(및 확장하여 ClojureScript)를 시작하기 위한 원스톱 상점인 https://www.braveclojure.com/을 적극 권장합니다.
  • Clojure와 ClojureScript는 공통 언어를 공유합니다. 저는 종종 이것을 Clojure[Script] 라고 동시에 언급할 것입니다.
  • React에 대한 지식과 일반적인 프론트엔드 노하우 가 있다고 가정합니다.

ClojureScript를 배우는 방법: 짧은 버전

따라서 ClojureScript를 배울 시간이 많지 않고 이 모든 것이 어디로 가는지 보고 싶을 뿐입니다. 먼저 ClojureScript가 무엇입니까?

ClojureScript 웹사이트에서: ClojureScript는 JavaScript를 대상으로 하는 Clojure용 컴파일러입니다. Google Closure 최적화 컴파일러의 고급 컴파일 모드와 호환되는 JavaScript 코드를 내보냅니다.

무엇보다도 ClojureScript에는 다음과 같은 많은 기능이 있습니다.

  • 함수형 프로그래밍이 적은 다중 패러다임 프로그래밍 언어입니다. 함수형 프로그래밍은 코드 가독성을 향상시키고 더 적은 코드로 더 많은 것을 작성 하는 데 도움이 되는 것으로 알려져 있습니다.
  • 기본적으로 불변성을 지원합니다. 전체 런타임 문제에 작별을 고하십시오!
  • 데이터 지향적입니다. 코드는 ClojureScript의 데이터 입니다. 대부분의 Clojure[Script] 응용 프로그램은 일부 기본 데이터 구조에서 작동하는 함수 집합으로 축소될 수 있으므로 디버깅이 간단하고 코드를 매우 읽기 쉽게 만듭니다.
  • 간단 해! ClojureScript를 시작하는 것은 쉽습니다. 멋진 키워드가 없고 마법이 거의 없습니다.
  • 환상적인 표준 라이브러리가 있습니다. 이 모든 것이 있습니다.

그걸 막고, 이 웜 캔을 예로 들어 보겠습니다.

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

Lisp 방언이나 ClojureScript에 익숙하지 않은 사람들을 위한 참고 사항: 이 예제의 가장 중요한 부분은 :div , []() 입니다. :div<div> 요소를 나타내는 키워드입니다. [] 는 Java의 ArrayList 와 매우 유사한 벡터이고 ()LinkedList 와 매우 유사한 시퀀스입니다. 이 포스트의 뒷부분에서 더 자세히 다루겠습니다!

이것은 ClojureScript에서 React 컴포넌트의 가장 기본적인 형태입니다. 그게 전부입니다. 키워드, 문자열 및 전체 목록입니다.

피! JSX 또는 TSX의 "hello world"와 크게 다르지 않습니다.

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

그러나 이 기본 예에서도 확인할 수 있는 몇 가지 중요한 차이점이 있습니다.

  • 포함된 언어가 없습니다. ClojureScript 예제 내의 모든 것은 문자열, 키워드 또는 목록입니다.
  • 간결 하다 ; 목록은 HTML 닫는 태그의 중복 없이 필요한 모든 표현을 제공합니다.

이 두 가지 작은 차이점은 코드 작성 방법뿐만 아니라 자신을 표현하는 방법에도 큰 영향을 미칩니다!

어때요? 싸움에 뛰어들어 ClojureScript가 우리를 위해 무엇을 더 준비했는지 봅시다…

관련된:
  • Elm 프로그래밍 언어 시작하기
  • Elixir 프로그래밍 언어 시작하기

빌딩 블록

이 ClojureScript 튜토리얼 전반에 걸쳐 나는 Clojure[Script]를 훌륭하게 만드는 것이 무엇인지 너무 깊이 파고들지 않으려고 노력할 것입니다. 그럼에도 불구하고 여기에서 우리가 할 수 있는 일의 폭을 파악할 수 있도록 몇 가지 기본 개념을 다루는 것이 유용할 것입니다.

경험이 풍부한 Clojuristas 및 Lispians를 위해 자유롭게 다음 섹션으로 건너뛰십시오!

먼저 다루어야 할 세 가지 주요 개념이 있습니다.

키워드

Clojure[Script]에는 Keyword라는 개념이 있습니다. 상수 문자열(예: Java)과 키 사이 어딘가에 있습니다. 그것들은 스스로를 평가하는 상징적 식별자입니다 .

예를 들어, 키워드 :cat 은 항상 :cat cat을 참조하고 다른 어떤 것도 참조하지 않습니다. Java에서와 같이 다음과 같이 말할 수 있습니다.

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

… Clojure에서는 다음과 같이 간단하게 수행할 수 있습니다.

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

또한 참고: Clojure에서 맵은 Java HashMap 과 같은 값에 대한 키의 모음이자 해당 내용에 액세스하기 위한 함수입니다. 정돈 된!

기울기

Clojure[Script]가 Lisp 방언이라는 것은 목록을 많이 강조한다는 의미입니다. 앞서 언급한 것처럼 두 가지 주요 사항을 알고 있어야 합니다.

  1. []ArrayList 와 매우 유사한 벡터입니다.
  2. ()LinkedList 와 매우 유사한 시퀀스입니다.

Clojure[Script]에서 목록을 작성하려면 다음을 수행합니다.

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

시퀀스의 경우 약간 다릅니다.

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

앞에 붙는 ' 는 다음 섹션에서 설명합니다.

기능

마지막으로 기능이 있습니다. Clojure[Script]의 함수 는 앞에 ' 가 붙지 않고 입력되는 시퀀스 입니다. 해당 목록의 첫 번째 요소는 함수 자체이고 다음 요소는 모두 인수 가 됩니다. 예를 들어:

 (+ 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`

이 동작의 결과는 실제로 실행하지 않고도 함수의 정의를 구축할 수 있다는 것입니다! 프로그램이 평가될 때 '네이키드' 시퀀스만 실행됩니다.

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

이것은 나중에 관련이 될 것입니다!

함수는 몇 가지 방법으로 정의할 수 있습니다.

 ; 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)

면밀한 검토

이제 기본 사항을 다루었으므로 여기에서 무슨 일이 일어나고 있는지 좀 더 자세히 살펴보겠습니다.

ClojureScript에서 React는 일반적으로 Reagent라는 라이브러리를 사용하여 수행됩니다. 시약은 Hiccup 및 해당 구문을 사용하여 HTML을 나타냅니다. Hiccup repo의 위키에서:

"Hiccup은 Clojure 데이터 구조를 다음과 같이 변환합니다."

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

"다음과 같은 HTML 문자열로:"

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

간단히 말해서 목록의 첫 번째 요소 는 HTML 요소 유형이 되고 나머지는 해당 요소의 내용이 됩니다. 선택적으로 해당 요소에 첨부될 속성 맵을 제공할 수 있습니다.

요소는 단순히 부모 목록에 중첩함으로써 서로 중첩될 수 있습니다! 이것은 예에서 가장 쉽게 볼 수 있습니다.

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

임베딩 메서드를 명시적으로 선언하지 않고도 구조 내에 오래된 함수나 일반적인 Clojure 구문을 넣을 수 있는 방법에 주목하세요. 결국 목록일 뿐입니다!

그리고 더 좋은 점은 이것이 런타임에 무엇으로 평가됩니까?

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

키워드 목록과 내용은 물론! 재미있는 유형이나 마법의 숨겨진 방법이 없습니다. 그저 오래된 목록일 뿐입니다. 원하는 만큼 이 목록을 연결하고 재생할 수 있습니다.

Hiccup이 레이아웃을 수행하고 Reagent가 논리 및 이벤트 처리를 수행하면 완전한 기능의 React 환경이 완성됩니다.

더 복잡한 예

좋습니다. 이것을 일부 구성 요소와 함께 좀 더 연결해 보겠습니다. React(및 Reagent)의 마법 같은 점 중 하나는 보기 및 레이아웃 논리를 모듈로 구획화한 다음 애플리케이션 전체에서 재사용할 수 있다는 것입니다.

버튼과 몇 가지 간단한 논리를 보여주는 간단한 구성 요소를 만든다고 가정해 보겠습니다.

 ; 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?")}]])

이름 지정에 대한 빠른 참고 사항: Clojure의 모듈은 일반적으로 네임스페이스가 지정되므로 위젯 widget.cljs 를 네임스페이스 widget 아래에서 가져올 수 있습니다. 이는 최상위 component 기능이 widget/component 로 액세스됨을 의미합니다. 모듈당 하나의 최상위 구성 요소만 갖고 싶지만 이것은 스타일 기본 설정입니다. 구성 요소 기능의 이름을 polite-component 또는 widget-component 와 같은 이름으로 지정하는 것이 좋습니다.

이 간단한 구성 요소는 선택적으로 폴라이트 위젯을 제공합니다. (when polite? ", please.") 로 평가합니다 ", please." polite? == true polite? == true 이고 false 이면 nil 입니다.

이제 이것을 app.cljs 에 포함시켜 보겠습니다.

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

여기에서는 HTML 키워드와 마찬가지로 목록의 첫 번째 항목으로 호출하여 위젯을 앱 구성 요소에 포함합니다! 그런 다음 동일한 목록의 다른 요소로 제공하여 구성 요소에 모든 자식 또는 매개 변수를 전달할 수 있습니다. 여기서 우리는 단순히 true 를 전달하므로 우리 위젯에서 polite? == true polite? == true , 따라서 우리는 폴라이트 버전을 얻습니다.

지금 앱 기능을 평가한다면 다음을 얻을 수 있습니다.

 [: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 가 어떻게 평가되지 않았는지 주목하십시오! (혼란스러운 경우 기능 섹션을 참조하십시오.)

DOM 트리 내의 구성 요소는 업데이트된 경우에만 평가되며(따라서 장면 뒤에서 실제 React 객체로 변환됩니다), 이는 상황을 훌륭하고 빠르게 유지하고 어느 시점에서든 처리해야 하는 복잡성의 양을 줄여줍니다.

관심 있는 사람들을 위해 이 주제에 대한 자세한 내용은 시약 문서에서 얻을 수 있습니다.

모든 방법을 나열

또한 DOM이 목록의 목록일 뿐이며 구성 요소는 목록의 목록을 반환하는 함수일 뿐입니다. ClojureScript를 배울 때 이것이 왜 그렇게 중요합니까?

함수나 목록에 대해 수행할 수 있는 모든 작업을 구성 요소에 대해 수행할 수 있습니다.

여기에서 ClojureScript와 같은 Lisp 언어를 사용하여 복합 수익을 얻기 시작합니다. 구성 요소와 HTML 요소는 다른 일반 데이터처럼 조작할 수 있는 일급 객체가 됩니다! 다시 한 번 말씀드리겠습니다.

구성 요소와 HTML 요소는 Clojure 언어 내에서 일급 지원 개체입니다!

맞아요, 당신은 내 말을 들었습니다. Lisp가 목록을 처리하도록 설계된 것과 거의 같습니다(힌트: 그랬습니다.)

여기에는 다음이 포함됩니다.

  • 번호가 매겨진 목록의 요소에 대한 매핑:
 (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"]]
  • 포장 구성 요소:
 ; 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!
  • 속성 주입:
 (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"))]])

목록 및 지도와 같은 평범한 오래된 데이터 유형을 처리하는 것은 클래스를 닮은 어떤 것보다 훨씬 간단하고 장기적으로 훨씬 더 강력해집니다!

패턴이 나타납니다

자, 요약해 보겠습니다. 지금까지 ClojureScript 튜토리얼은 무엇을 보여주었습니까?

  • 모든 것이 가장 단순한 기능으로 축소되었습니다. 요소는 목록일 뿐이고 구성 요소는 요소를 반환하는 함수일 뿐입니다.
  • 구성 요소와 요소는 일급 객체이기 때문에 적은 비용 으로 더 많은 것을 작성할 수 있습니다.

이 두 가지 점은 Clojure 및 함수형 프로그래밍 정신에 꼭 맞습니다. 코드는 조작할 데이터 이고 복잡성은 덜 복잡한 부분을 연결하여 구축됩니다. 우리는 프로그램(이 예에서는 웹페이지)을 데이터(목록, 기능, 지도)로 표시하고 Reagent가 인수하여 React 코드로 변환하는 마지막 순간까지 그대로 유지합니다. 이것은 우리의 코드를 재사용할 수 있게 하고 가장 중요한 것은 아주 약간의 마법으로 읽고 이해하기가 매우 쉽습니다.

스타일리시해지기

이제 몇 가지 기본 기능으로 응용 프로그램을 만드는 방법을 알았으므로 보기 좋게 만드는 방법으로 넘어갑니다. 이에 접근하는 몇 가지 방법이 있습니다. 가장 간단한 방법은 스타일 시트를 사용하고 구성 요소에서 해당 클래스를 참조하는 것입니다.

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

이렇게 하면 예상한 대로 정확하게 수행되며 아름다운 빨간색 "Hello, world!"가 표시됩니다. 텍스트.

그러나 뷰와 논리 코드를 구성 요소에 같은 위치에 배치하고 스타일 시트로 스타일 시트를 분리하는 이 모든 문제를 해결해야 하는 이유는 무엇입니까? 언어도!

CSS를 구성 요소의 코드 로 작성하지 않는 이유는 무엇입니까(여기서 테마 참조). 이것은 우리에게 많은 이점을 줄 것입니다:

  • 구성 요소를 정의하는 모든 것은 같은 위치에 있습니다.
  • 클래스 이름은 영리한 생성을 통해 고유함을 보장할 수 있습니다.
  • CSS는 동적일 수 있으며 데이터가 변경되면 변경됩니다.

내가 개인적으로 좋아하는 CSS-in-code는 Clojure Style Sheets(cljss)를 사용하는 것입니다. 임베디드 CSS는 다음과 같습니다.

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

defstyles 는 고유한 클래스 이름을 생성하는 함수를 생성합니다(구성 요소를 가져오는 모든 사람에게 유용합니다).

cljs가 사용자를 위해 수행할 수 있는 다른 많은 작업(스타일, 애니메이션, 요소 재정의 등)이 있지만 여기서는 자세히 다루지 않겠습니다. 직접 확인해보시길 추천합니다!

ClojureScript 앱 조각 모으기

마지막으로 이 모든 것을 하나로 묶는 데 필요한 접착제가 있습니다. 다행스럽게도 프로젝트 파일과 index.html 외에 상용구는 최소한 여기에 있습니다.

다음이 필요합니다.

  • 프로젝트 정의 파일 project.clj . 모든 Clojure 프로젝트의 필수 요소이며 GitHub에서 직접적으로도 종속성을 정의하고 기타 빌드 속성( build.gradle 또는 package.json 과 유사)을 정의합니다.
  • 시약 응용 프로그램의 바인딩 지점 역할을 하는 index.html 입니다.
  • 개발 환경 및 마지막으로 시약 응용 프로그램 시작을 위한 일부 설정 코드입니다.

GitHub에서 사용할 수 있는 이 ClojureScript 자습서의 전체 코드 예제를 찾을 수 있습니다.

그게 다야(지금은). Lisp 사투리(Clojure[Script] 또는 기타)를 확인하거나 심지어 자신의 Reagent 응용 프로그램을 직접 만들어 보기 위해서라도 여러분의 궁금증을 조금이나마 해소할 수 있기를 바랍니다! 나는 당신이 그것을 후회하지 않을 것이라고 약속합니다.

Re-frame을 사용한 상태 관리에 대해 이야기하는 이 문서 상태에 들어가기에 참여하세요.