世の中にはCycle.jsという便利なものがあるらしい。Cycle.jsがどんなものかについては、たぶん公式サイトの説明が一番わかりやすいと思う。簡単に説明すると、仮想DOMとStream(Observable)をうまく組み合わせて、いい感じに動的なwebページを作れるらしい。
ということで、本記事ではCycle.jsとxstreamを使って、公式ページにある「Hello hogehoge(hogehogeの部分が動的に表示される)」というサンプルを実装しようと思う。
対象
- RxJsやxstreamの最低限の知識があり、初めてCycle.jsに触れる人。ここでいう最低限は、「streamがどんなものか」が漠然とわかる程度でいいはず(たぶん)。
注意
- 私自身、Cycle.jsの初心者で間違った表現などあるかもしれないので、もしあれば教えていただけると嬉しいです。
- javascriptを直接書きたくないので、typescriptを使います(公式でも推奨されています)。
- 本記事ではRxJsではなくxstreamを使って実装します。
参考にしたサイト
- 公式サイト:(https://cycle.js.org/)
- ここをめっちゃ参考にした:(https://tech.recruit-mp.co.jp/front-end/post-11898/)
実行環境
- OS:Mac
- エディタ:VSCode
- ブラウザ:Google Chrome
- パッケージ管理:yarn(npmでもいいと思います)
- 言語:typescriptで書いてjavascriptにコンパイルする
では、実際に実装していこう。
1.環境を整える
まずは、諸々の環境を整える。適当にディレクトリを作ってそこに移動してください。ここでは、cyclejs
ディレクトリを作ったとしよう。ここに全てのファイルをぶち込んでもいいのだが、やや見栄えが良くないので元のソースを入れるsrc
ディレクトリと、jsファイルなどを入れるcodegen
ディレクトリを作ろう。以降のファイルも全て含めると、最終的に下のようなディレクトリ構造になると思う。
cyclejs |__src //**.tsファイルを入れる | |____test.ts |__codegen //**.jsファイル、**.htmlファイルを入れる | |____test.js | |____index.html |__nodemodules | |__bs-config.js |__package.json |__tsconfig.json |__watch.sh
cyclejs
ディレクトリ内でパッケージ管理用のpackage.json
を作りたいので、次のコマンドを叩く。なんか色々聞かれますが、Enter連打で大丈夫。
$ yarn init
これでpackage.json
ができるはず。ここに色々とライブラリが追加されていき、管理してくれる(便利)。
では、必要なライブラリを入れていこう。
$ yarn add xstream @cycle/dom @cycle/run @types/node
これで必要なライブラリが一通り依存関係を解決した状態で入ってくれる。わぁい。 さて、これで準備は整ったので実際にコードを書いていこう。
2.コードを書く
上でライブラリを用意できたので、実際にコードを書いていく。公式サイトにあるやつを参考にすると、次のようになると思う。
//test.ts import {Stream} from 'xstream'; import {run} from '@cycle/run'; import {div, label, input, hr, h1, makeDOMDriver, DOMSource, VNode } from '@cycle/dom'; type Sources = { DOM: DOMSource; } type Sinks = { DOM: Stream<VNode>; } console.log("test.js readed"); function main(sources: Sources) : Sinks { const input$: Stream<Event> = sources.DOM.select('.field').events('input'); const name$: Stream<string> = Stream.from(input$) .map((ev: Event) => (ev.target as HTMLInputElement).value) .startWith(''); const vdom$: Stream<VNode> = name$.map(name => div([ label('Name:'), input('.field', {attrs: {type: 'text'}}), hr(), h1('Hello ' + name), ]) ) return { DOM: vdom$}; }; const drivers = { DOM: makeDOMDriver('#app-container') }; run(main, drivers);
公式サイトにも書いてあるが、簡単にCycle.jsの仕組みを説明する。Cycle.jsにはmain
とdriver
という2つの部分があり、main
が内部の処理を、driver
がDOMの操作などを担当している(たぶん)。で、main
とdriver
はstream(Observable)
を授受することでやりとりしており、これをmain
関数ではsource
としてdriver
から受け取り、sink
としてdriver
に渡す。上のコードでもそれをやっている。
さて、Javascriptをブラウザ上で動かしたいので、HTMLも用意してやる必要がある。これをindex.html
として、次のように書く。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Hello Cycle.js</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> </head> <body> <div id="app-container"></div> <script src="test.js"></script> </body> </html>
実際の処理はtest.js
が全てやってくれるので、ここでは特に何かを書いてやる必要はない。
これで必要なソースはできたので、あとはtest.ts
をコンパイルして、サーバーを立ててアクセスできるようにしよう。
3.ブラウザに表示させる
ここの部分は正直なところ私もよく分かっていない…が、上の参考サイト様の言う通りに色々とやればいい。
まず、サーバを利用するために以下のものをyarn
する。
$ yarn add -D typescript tsify browserify watchify browser-sync
そして、サーバを実行するためのシェルファイルwatch.sh
を作る。
#!/usr/bin/env bash # Compile TypeScript sources nohup watchify -d src/test.ts -p [ tsify ] -o codegen/test.js & browserify_pid=$! trap "kill -15 $browserify_pid $>/dev/null" 2 15 # Run Server nohup browser-sync start --config bs-config.js & browserSync_pid=$! trap "kill -15 $browserSync_pid &>/dev/null" 2 15 tail -f nohup.out
よくわからないけどこれでtypescriptのコンパイルとサーバの起動ができて、ファイル変更があったら自動でリコンパイルしてくれるらしい(めっちゃ便利)。
で、このシェルをyarn
から呼び出したいので、package.json
に次の記述を足す。
"scripts": { "watch": "bash ./watch.sh" }
これで、ターミナル上で、
$ yarn watch
と叩くと、シェルファイルが走ってくれます。
さて、これでOKだ!と思いきやそんなことはなく。typescriptをコンパイルするにはtsconfig.json
とかいうものが必要なので、こいつもディレクトリ内に作る。内容は以下の通り。
{ "compilerOptions": { "target": "es2017", "lib": ["es5", "es6", "dom"], "module": "commonjs", "sourceMap": true, "outDir": "./codegen" }, "include": [ "src/**/*" ] }
これで最適なのかはわからないけど、少なくとも動いているので間違ってはないのでしょう(誰か、tsconfigの正しい書き方教えてください)。
そして、残念なことにまだ用意しなければならないものがある。browser-sync
とかいうやつを使ってブラウザ上に表示をするわけだが、これの設定ファイルであるbs-config.js
を作らなければならないらしい。そこで、公式サイトを見ると、次のようなコマンドを叩くとbs-config.js
が自動で生成される。
$ yarn browser-sync init
これでbrowser-sync
が生成されるので、そこの、files
とserver
を次のように変更する。
"files": ["codegen/index.html", "codegen/test.js"],
"server": { baseDir: "codegen", serveStaticOptions: { extensions: ["html"] } }
これで、index.html
が読み込まれる他、"files"
に指定したファイルが変更された時に自動でリコンパイルされるようになった。
さて、以上で全ての準備は整った。あとは、
$ yarn watch
でサーバーの起動を確認後、localhost:3000
にアクセスすればサンプルと同じページが見られる。やったぜ!
最後に
コンパイルとかbrowser-sync
とかはなんも理解せずに行き当たりばったりで使っているので、もっと理解しなきゃなあというお気持ち。あと、Cycle.jsそれ自体もそんなに理解できてないので、誰か教えてください。