React(とflux)について学ぶ。
Node.jsをインストールする。
Node.js公式ページに行き、Current と書いてあるリンクをクリックするとインストーラーが落ちてくる。
あとは指示通りインストールを進めていけばよい。
nvmをおすすめする。
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash
でnvmをインストールしたあと、
nvm install node
で最新版のNode.jsをインストールする。
curl | bash
をしたくない人は公式ページに従ってインストール。
まずはいつも通り Hello, World! から。
以下の内容を適当なファイル名(react.html
とか)で保存し、ブラウザで開く。「Hello, World!」と表示されれば成功。
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@15.3.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.2/dist/react-dom.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
<script type="text/jsx">
class HelloWorld extends React.Component {
render () {
return <h1>Hello, World!</h1>;
}
}
ReactDOM.render(
<HelloWorld/>,
document.body
);
</script>
</head>
<body>
</body>
</html>
Facebook社が2013年に発表したリアクティブUIフレームワーク。
DOMとデータを切り離すための思想と導入の容易さが受け入れられ、JS界隈で流行した。
また、後ほど話すfluxと呼ばれる思想の登場によってさらに爆発的に流行。去年ぐらいまでJS界隈で最もアツいライブラリだった。
例えば文字列の配列からli要素を動的に生成する場合を考える。
生のAPIを使う場合、
const usersElement = document.createElement('ul');
usersElement.setAttribute('class', 'users');
document.body.appendChild(usersElement);
const users = ['aaa', 'bbb', 'ccc'];
users.forEach((user) => {
const userElement = document.createElement('li');
userElement.setAttribute('class', 'user');
userElement.textContent = user;
usersElement.appendChild(userElement);
});
jQeuryを使うと、
const $users = $('<ul/>', {'class': 'users'}).appendTo('body');
const users = ['aaa', 'bbb', 'ccc'];
users.forEach((user) => {
$('<li/>', {'class': 'user', text: user}).appendTo($users);
});
これらは「配列から一つづつ値を取り出してその値を持つli要素を追加する」という手続きをナイーブに記述しているだけであり、「userクラスのli要素が配列の値を表現する」ということを記述していない。そのためUIを操作するコードは規模が大きくなるにつれて肥大化し、変更に対する影響が追いにくくなっていく。
このように「データに対して行う処理を最終的な結果に至るまで順に記述していく」のではなく、「データを加工する処理をいくつかのコンポーネントに分割し、それぞれの要素が行うデータの送受信のみを定義する」ことを「リアクティブプログラミング」と呼ぶ。
Reactはこの考えをDOMに適用したものである。
で、先ほどの処理をReactで記述すると以下のようになる。
class User extends React.Component {
render() {
return <li className="user">{this.props.name}</li>
}
}
class Users extends React.Component {
constructor(props) {
super(props);
this.state = {users: []};
}
render() {
const userNodes = this.state.users.map(user => <User name={user}/>);
return <ul className="users">
{userNodes}
</ul>;
}
}
const users = ReactDOM.render(
<Users/>,
document.body
);
users.setState({users: ['aaa', 'bbb', 'ccc']});
コードは長くなっているが、データをDOMに反映するまでの処理がコンポーネントに分割され、よりリアクティブになっている。
まず特徴的なのはコード中にHTMLの断片のようなものが直接記述されていることである。これはJSXというReactのコードを簡明に記述するための記法で、JavaScriptの文法を独自に拡張したものである。実際に動かすためにはコンパイルしてJSのコードに変換する必要がある。
JSXのコンポーネント記法はHTMLと似ているが結構異なる。タグ名に当たる部分を大文字で始めるとHTML要素ではなくReactコンポーネントを呼び出すことができる。
Reactの特徴の一つはVirtualDOMを用いて軽量に操作できる点である。例えば先ほどのリストの内容を変更する場合は、
users.setState({users: ['aaa', 'bbb', 'ccc', 'ddd']});
とすればul要素に新しいli要素を追加できるが、この時Reactは一旦ul要素を空にしてから再びli要素を4個追加するのではなく、差分であるdddのli要素のみを検出して追加することができる。一般にDOMの操作は様々な副効果を引き起こしてとても重いので、実際に変更があった部分のみを変更するのはとても重要である。
fluxはReactで言うところのstateのようなものを、どのようなルールで管理するかを規定する枠組みである。言うなればクライアントサイドのMVCみたいなものである。
2014年に同じくfacebookが発表し、当初は懐疑的な意見が多かったものの後にReactとともに広く普及し、2014年から2015年のトレンドとなった。
(この直前に流行ったAngularを採用したプロジェクトは歯噛みをしたという)
ここでは詳しい説明は省くが、fluxは4種類のコンポーネントから構成され、その中をデータが一方向に流れていくモデルを想定する。Viewの部分は通常Reactで実装する。
fluxはライブラリやフレームワークではないので、複数の実装が存在する。一時は数十の実装が存在し戦国状態となっていたが、最近は以下の2つがメジャーどころとなっている。
今回はReact.jsを使ってCTF分科会で使えそうなflag送信サイトを作ってみる。
以下のリンクから2つのファイルを保存する。flag-matcher.htmlを開くとページが表示されることを確かめる。
https://gist.github.com/hakatashi/faca529e065878d537381f47406b9574
以下の機能を実装してよう。
- 問題のタイトルを正しく表示する
- flagを入力してボタンを押すと合ってるかどうかcheckし、alertで結果を表示してくれる
- 下のフォームから新しい問題を追加できるようにする
- 処理を書くべきところにTODOコメントを書いているので、参考に。
- Solversを記録し、表示する
- 問題のタイトルやフラグが空白ならエラーを出す
- checkの結果をalertではなくいい感じに表示する
- BootstrapのJSを読み込むと良いかも
- LocalStorageあたりにデータを保存するようにする
- マニアックにやるならIndexedDBとか(つらい)
- ユーザーのリストを別途保持し、そこから選択するようにする
- 問題作成者の情報を記録・表示する
- 問題に得点の情報を持っておき、ユーザーごとのランキングをつくる
- サーバーを書く(つらい)
- やるならIsomorphicにしたいね
- CSSをがんばる
- 問題のタイトルを正しく表示する
- flagを入力してボタンを押すと合ってるかどうかcheckし、alertで結果を表示してくれる
- 下のフォームから新しい問題を追加できるようにする
- Solversを記録し、表示する
まで実装したものを以下に置いておいた。
https://gist.github.com/hakatashi/319a6a9e1683f151197bfe0d53b4ac47
差分はこちら。
https://gist.github.com/hakatashi/319a6a9e1683f151197bfe0d53b4ac47/revisions
プロ