フロントエンド開発のためのClojureScriptの発掘
公開: 2022-03-11この紹介を読んでいる間、おそらく頭の中に2つの主な考えがあります。
- ClojureScriptとは何ですか?
- これは私の興味とは関係ありません。
ちょっと待って! それはあなたが間違っているところです—そして私はあなたにそれを証明します。 10分間の時間を費やしても構わないと思っているなら、ClojureScriptがフロントエンドとReact-yアプリケーションの作成を楽しく、速く、そして最も重要なことに機能的にする方法を紹介します。
いくつかのClojureScriptチュートリアルの前提条件
- Lispの知識は必要ありません。 このブログ投稿内に散在しているコードサンプルを説明するために最善を尽くします!
- ただし、少し先読みしたい場合は、https://www.braveclojure.com/を強くお勧めします。これは、Clojure(および拡張機能としてClojureScript)を開始するためのワンストップショップです。
- ClojureとClojureScriptは共通の言語を共有しています。私は、これらをClojure[Script]と同時に参照することがよくあります。
- 私はあなたがReactと一般的なフロントエンドのノウハウの知識を持っていることを前提としています。
ClojureScriptを学ぶ方法:ショートバージョン
したがって、ClojureScriptを学ぶ時間はあまりなく、このすべてがどこに向かっているのかを知りたいだけです。 まず最初に、ClojureScriptとは何ですか?
ClojureScript Webサイトから: ClojureScriptは、JavaScriptを対象とするClojure用のコンパイラーです。 これは、GoogleClosure最適化コンパイラの高度なコンパイルモードと互換性のあるJavaScriptコードを出力します。
とりわけ、ClojureScriptには提供できるものがたくさんあります。
- これは、関数型プログラミングを無駄にしないマルチパラダイムプログラミング言語です。関数型プログラミングは、コードの読みやすさを向上させるだけでなく、少ないコードでより多くのことを書くのに役立つことが知られています。
- デフォルトで不変性をサポートしています。実行時の問題のスイート全体に別れを告げてください。
- それはデータ指向です:コードはClojureScriptのデータです。 ほとんどのClojure[Script]アプリケーションは、基礎となるデータ構造を操作する一連の関数にまとめることができます。これにより、デバッグが簡単になり、コードが非常に読みやすくなります。
- 簡単だ! ClojureScriptの使用を開始するのは簡単です。派手なキーワードはなく、魔法もほとんどありません。
- 素晴らしい標準ライブラリがあります。 これにはすべてがあります。
それが邪魔にならないように、例を使ってこのワームの缶を開けましょう。
(defn component [] [:div "Hello, world!"])
Lisp方言やClojureScriptに慣れていない人への注意:この例の最も重要な部分は:div
、 []
、および()
です。 :div
は、 <div>
要素を表すキーワードです。 []
はJavaのArrayList
によく似たベクトルであり、 ()
はLinkedList
によく似たシーケンスです。 これについては、この投稿の後半で詳しく説明します。
これは、ClojureScriptのReactコンポーネントの最も基本的な形式です。 それだけです—キーワード、文字列、そしてたくさんのリストだけです。
Pshaw! あなたが言うには、それはJSXやTSXの「helloworld」と大差ありません。
function component() { return ( <div> "Hello, world!" </div> ); }
ただし、この基本的な例からでも、いくつかの重要な違いを見つけることができます。
- 埋め込まれた言語はありません。 ClojureScriptの例内のすべては、文字列、キーワード、またはリストのいずれかです。
- 簡潔です; リストは、HTMLの終了タグの冗長性なしに、必要なすべての表現度を提供します。
これらの2つの小さな違いは、コードの記述方法だけでなく、自分自身の表現方法にも大きな影響を及ぼします。
どうですか、あなたは尋ねますか? 争いに飛び込んで、ClojureScriptが私たちのために他に何を用意しているかを見てみましょう…
- Elmプログラミング言語入門
- Elixirプログラミング言語入門
ビルディングブロック
このClojureScriptチュートリアル全体を通して、Clojure [Script]を優れたものにしているものを深く掘り下げないように努めます(これは多くのことですが、私は逸脱します)。 それでも、ここでできることの幅を把握できるように、いくつかの基本的な概念をカバーしておくと便利です。
経験豊富なClojuristasとLispiansの方は、次のセクションに進んでください。
最初に取り上げる必要がある3つの主要な概念があります。
キーワード
Clojure[スクリプト]にはキーワードと呼ばれる概念があります。 これは、定数文字列(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方言であることは、リストに多くの重点を置いていることを意味します。 先に述べたように、注意すべき2つの主なことがあります。
-
[]
は、ArrayList
によく似たベクトルです。 -
()
は、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と呼ばれるライブラリを使用して実行されます。 Reagentは、Hiccupとその構文を使用してHTMLを表します。 Hiccupレポのwikiから:
「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)の魔法の1つは、ビューとレイアウトロジックをモジュールに区分し、アプリケーション全体で再利用できることです。
ボタンといくつかの単純なロジックを示す単純なコンポーネントを作成するとします。
; 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
としてアクセスされることを意味します。 モジュールごとにトップレベルのコンポーネントを1つだけ持つのが好きですが、これはスタイル設定です。コンポーネント関数に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チュートリアルは何を示していますか?
- すべてが最も単純な機能に還元されます。要素は単なるリストであり、コンポーネントは要素を返す単なる関数です。
- コンポーネントと要素はファーストクラスのオブジェクトであるため、より少ないでより多くを書くことができます。
これらの2つのポイントは、Clojureと関数型プログラミングの精神にぴったりと適合しています。コードは操作されるデータであり、複雑さの少ない部分を接続することで複雑さが増します。 プログラム(この例ではWebページ)をデータ(リスト、関数、マップ)として提示し、Reagentが引き継いでReactコードに変換する最後の瞬間までその状態を維持します。 これにより、コードが再利用可能になり、最も重要なこととして、ほとんど魔法をかけずに非常に読みやすく、理解しやすくなります。
スタイリッシュに
これで、いくつかの基本的な機能を備えたアプリケーションを作成する方法がわかったので、見栄えを良くする方法に移りましょう。 これにアプローチする方法はいくつかあります。最も簡単な方法は、スタイルシートを使用し、コンポーネントでそれらのクラスを参照することです。
.my-class { color: red; }
[:div {:class "my-class"} "Hello, world!"]
これは、期待どおりに機能し、美しい赤い「Hello、world!」を表示します。 文章。
ただし、ビューとロジックコードをコンポーネントに配置する際に、この問題のすべてに行き、スタイルをスタイルシートに分離するのはなぜですか。2つの異なる場所を調べる必要があるだけでなく、2つの異なる場所を処理しているのです。言語も!
CSSをコンポーネントのコードとして記述してみませんか(ここのテーマを参照してください)。 これにより、多くの利点が得られます。
- コンポーネントを定義するものはすべて同じ場所にあります。
- クラス名は巧妙な生成を通じて一意であることが保証されます。
- CSSは動的であり、データの変化に応じて変化します。
私が個人的に気に入っているCSS-in-codeのフレーバーは、Clojureスタイルシート(cljss)です。 埋め込まれたCSSは次のようになります。
;; -- STYLES ------------------------------------------------------------ (defstyles component-style [] {:color "red" :width "100%"}) ;; -- VIEW -------------------------------------------------------------- (defn component [] [:div {:class (component-style)} "Hello, world!"])
defstyles
は、一意のクラス名を生成する関数を作成します(これは、コンポーネントをインポートするすべての人に最適です)。
cljssがあなたのためにできることは他にもたくさんあります(スタイル、アニメーション、要素のオーバーライドなどの作成)。ここでは詳しく説明しません。 自分でチェックすることをお勧めします!
ClojureScriptアプリのピースを組み立てる
最後に、これらすべてをくっつけるために必要な接着剤があります。 幸い、プロジェクトファイルとindex.html
に加えて、ボイラープレートは少なくともここにあります。
必要なもの:
- プロジェクト定義ファイル、
project.clj
。 Clojureプロジェクトの定番であり、GitHubから直接でも、依存関係とその他のビルドプロパティ(build.gradle
やpackage.json
と同様)を定義します。 - Reagentアプリケーションのバインディングポイントとして機能する
index.html
。 - 開発環境用のセットアップコードと、最後にReagentアプリケーションを起動するためのセットアップコード。
このClojureScriptチュートリアルの完全なコード例は、GitHubで入手できます。
以上です(今のところ)。 うまくいけば、Lisp方言(Clojure [Script]など)をチェックする場合でも、独自の試薬アプリケーションを作成する場合でも、少なくともあなたの好奇心を少しだけ刺激しました。 後悔しないことをお約束します。
この記事「GettingIntoaState」のフォローアップに参加してください。ここでは、リフレームを使用した状態管理について説明しています。ClojureScriptでReduxに挨拶してください。