以下の公式ドキュメントを和訳しました。
Clojure Luminus - ClojureScript
ClojureScriptはクライアントサイドのアプリケーションロジックを記述するための、JavaScripの代わりとなる素晴らしい言語だ。
ClojureScriptを使うことで以下のような利点が得られる。
- クライアントとサーバーの両方で同じ言語を使うことができる。
- フロントエンドとバックエンドで共通するコードを共有できる。
- より簡潔で構造化された言語を使用できる。
- Leiningenによる依存関係の管理
- 不変なデータ構造
- 強力な標準ライブラリ
ClojureScriptサポートの追加
ClojureScriptのサポートを追加する上で最も簡単な方法は新しいプロジェクトを作成する際に+cljsフラグを使用することだ。
しかしながら、すでに存在するプロジェクトに追加することも同じくらいにきわめて簡単だ。
project.clj
ファイルに以下のセクションを追加するだけだ。
:plugins [... [lein-cljsbuild "1.0.3"]]
:hooks [... leiningen.cljsbuild]
:cljsbuild
{:builds
[{:id "dev"
:source-paths ["src-cljs"]
:compiler
{:optimizations :none
:output-to "resources/public/js/app.js"
:output-dir "resources/public/js/"
:pretty-print true
:source-map true}}
{:id "release"
:source-paths ["src-cljs"]
:compiler
{:output-to "resources/public/js/app.js"
:optimizations :advanced
:pretty-print false
:output-wrapper false
:closure-warnings {:non-standard-jsdoc :off}}}]}
上記の設定はlein-cjsbuildプラグインを追加し、フックするようにビルドの設定をする。
すべてのClojureScriptの名前空間はプロジェクトのルートディレクトリ以下のsrc-cljs
ディレクトリ内に配置する必要がある。
ClojureScriptのファイルは拡張子が.cljsになっている必要がある。もし、拡張子が.cljだったとしてもコンパイルされるが、jsの名前空間にアクセスすることできなくなる。
ライブラリを使う
ClojureScriptを使う利点の一つはクライアントサイドのライブラリをLeiningenを使用して管理することができることである。ClojureScriptのライブラリは他のライブラリと同じようにproject.clj
の依存関係に含まれる。
コンパイラを走らせる
ClojureScriptのアプリケーションを開発するもっとも簡単な方法はコンパイラをautoモードで動かすことである。
この方法では名前空間内で変更が加えられると自動的に再コンパイルされ、Webページに対して即座に反映される。
コンパイラをこのモードで起動するには単純に以下コマンドを実行する。
lein cljsbuild auto dev
プロダクション用にアプリケーションをコンパイルするにはonceオプションを使用する。これはプロジェクトに含まれるすべてのスクリプトをコンパイルして単一のJSファイルを生成する。
lein cljsbuild once release
高度なコンパイルとエクスポート
高度なコンパイルをする際、変数名はよりコードを短くするために、コンパイラによって書き換えられます。
JavaScriptの関数の名前を保護したい場合があるかもしれない。これは^:export
アノテーションを使用することで可能となる。
(ns main)
(defn ^:export init []
(js/alert "hello world"))
こうして他のページからこの関数を他のものと同じように呼び出すことができる。
<script>
main.init();
</script>
もし、何かしらのJSのライブラリを使用する場合、使用したいすべての関数名を保護しなくてはいけない。例えば、AlbumColorsというライブラリを使用したい場合、以下のように書くことができる。
(defn ^:export init []
(.getColors (js/AlbumColors. "/img/foo.jpg")
(fn [[background]]
(.log js/console background))))
しかしながら、スクリプトが:advancedフラグを使用してコンパイルされた場合、AlbumColorsとgetColorsは書き換えられてしまうだろう。
これらを保護するためにはこれらに対する参照を行うJSファイルを作る必要がある。
var AlbumColors = {};
AlbumColors.getColors = function() {};
もし、上記のコードをresourcesディレクトリ内のexterns.js
というファイルに記述した場合、cljsbuildセクションで以下のようにして参照することができるだろう。
{:id "release"
:source-paths ["src-cljs"]
:compiler
{:output-to "resources/public/js/app.js"
:optimizations :advanced
:pretty-print false
:output-wrapper false
;;specify the externs file to protect function names
:externs ["resources/externs.js"]
:closure-warnings {:non-standard-jsdoc :off}}}
JavaScriptとの相互作用
すべてのJavaScriptのグローバル関数とグローバル変数はjs
名前空間を通して使用可能である。
メソッドの呼び出し
(.method object params)
(.log js/console "hello world!")
プロパティに対するアクセス
(.-property object)
(.-style div)
プロパティの設定
(set! (.-property object))
(set! (.-color (.-style div) "#234567"))
ClojureScriptにおけるJavaScriptの標準的な操作の例はHimera documentationを見ることで分かる。
Reagentを使う
ReagentはLuminusによってClojureScriptアプリケーションを構築する上で推奨されるアプローチである。
Luminusにおいて+cljsプロファイルを使用することは、アプリケーションをそのように設定された状態で生成することになる。
ReagentはDOM表記のためのHiccupスタイルの構文を使用してUIコンポーネントを定義するための標準的な方法を提供する。
それぞれのUIコンポーネントは特定のDOM要素を表すデータ構造になっている。
UIのDOMを中心としたビューを採用することで、ReagentはUIコンポーネントの構成をシンプルかつ直感的に書くことができるようにする。
クライアントサイドのルーティング
SecretaryはおすすめでできるClojureScriptのルーティングライブラリである。これはルーティング定義についてCompojureに影響を受けた構文を使用している。 このライブラリを使用するためには、プロジェクトに依存関係を追加する。+cljsテンプレートを使用してプロジェクトを作成した場合は、それはデフォルトで含まれる。
[secretary "1.2.0"]
次はライブラリを使うためにClojureScriptの名前空間で参照できるようにする。
(ns app
(:require [secretary.core :as secretary
:include-macros true
:refer [defroute]]
[goog.events :as events])
(:import goog.History
goog.history.EventType))
ライブラリがインポートできたら、トリガされたら指定されたDOM要素の内容を設定するルーティング定義をつくることができる。
(defn home []
[:div [:h1 "Home"]])
(defn info []
[:div [:h1 "About this app"]])
(defn not-found []
[:div [:h1 "404: Page doesn't exist"]])
(defn page [page-component]
(reagent/render-component
[page-component]
(.getElementById js/document "appContainer")))
(defroute home-path "/" [] (page home))
(defroute home-path "/about" [] (page info))
(defroute "*" [] (page not-found))
より詳しい内容は公式ドキュメントを参照しなさい。
DOMを直接操作する
注意
Reagentは、仮想DOMを使用して必要に応じてコンポーネントをレンダリングするので、DOMを直接操作しないことを強く推奨する。Reagentコンポーネントの外部のDOM要素を更新した場合、予測できない動作が発生することがある。
DOM要素に対してアクセスし、変更することを可能にするライブラリはいくつかある。具体的にはDominaやDommyといったものを見てみるかもしれない。DominaはDOM要素を選択して操作するだけでなく、イベントハンドリングも行う軽量ライブラリである。DommyはHiccupに似たテンプレートライブラリである。
Ajax
LuminusはAjax操作をハンドリングするためにcljs-ajaxを使用する。このライブラリはajax-request、GET、POST関数を使用して簡単にAjaxリクエストをサーバーに送信するための方法を提供する。
ajax-request
ajax-requestは以下のようなパラメータを受け入れる基本的なリクエスト関数である。
- uri - リクエストのURI
- method - PUT、DELETEなどのようなHTTPリクエストの種類を表す文字列
- format - レスポンスのフォーマットを指定するキーワードで
:json
、:edn
のどちらかを指定できる。デフォルトは:edn
。 - handler - 成功時のハンドラ。レスポンスを単一の引数で受け取る関数。
- error-handler - エラー時のハンドラ。エラーとキーを表すマップを受け取る関数。
- params - サーバーに送信するパラメータのマップ。
GET/POSTヘルパー
GETとPOSTのヘルパーはURIに続いてオプションのマップを受け取る。
-
:handler
- 成功時のハンドラ。デシリアライズされた単一のパラメータを受け取る関数。 -
:error-handler
- エラー時のハンドラ。:status
、:status-text
のキーを含むマップを受け取る関数。 -
:format
- レスポンスのフォーマットを指定するキーワードで:json
、:edn
のどちらかを指定できる。デフォルトは:edn
。 -
:params
- リクエストとともに送信されるパラメータのマップ。
(ns foo
(:require [ajax.core :refer [GET POST]]))
(defn handler [response]
(.log js/console (str response)))
(defn error-handler [{:keys [status status-text]}]
(.log js/console
(str "something bad happened: " status " " status-text)))
(GET "/hello")
(GET "/hello" {:handler handler})
(POST "/hello")
(POST "/send-message"
{:params {:message "Hello World"
:user "Bob"}
:handler handler
:error-handler error-handler})