LoginSignup
12

More than 5 years have passed since last update.

spa react study

Last updated at Posted at 2016-01-12

React を用いて SPA を作る初期段階の勉強メモ

Github

$ git clone https://github.com/hiyuzawa/spa_react_study.git
$ cd spa_react_study/
$ npm install
$ ./node_modules/.bin/gulp   # http://localhost:8000/ で確認可能

# ドキュメント内の CHECK POINT N の区切りでGit Commitし Tag v0.N を切ってあります

$ git checkout refs/tags/v0.N  # これで CHECK POINT N の状態になる
$ git checkout master # これで最終系に戻る

JavaScript

  • オブジェクト指向スクリプト言語
  • ECMAScriptとして標準化
  • Node.jsのようにサーバサイドでも利用が活発
  • GoogleのV8 はJITコンパイラとして有名

ECMAScript - JavaScript

Hello World

そのまえに開発環境をちょっと整備

nodebrew

nodebrew

  • node.jsを自分のマシン内でversion管理するためのtool.
  • nvm, nbenv っていうのもあるらしい

koko

サーバ不要のローカルHTMLを確認したい時に簡易に立ち上げることができるウエブサーバ

$ npm -g install koko

$ koko
document root   : /Users/hiyuzawa/...
php     : off
md      : off
[listen 65524]

kokoが起動されたディレクトリをDocumentRootとしてウエブサーバが立ち上がる
(Portは毎回空きポートをランダムに利用し異なる)

CHECK POINT 1

Google Hosted Libraries

Google Hosted Libraries

Googleが気前よくJavaScriptで有名なライブラリをCDN経由で配信をホスティングしてくれてる.
軽く利用するときには使わせてもらうと便利

CHECK POINT 2

Node

  • サーバサイドJavaScript
  • イベント駆動型プログラミング (callback)
  • require で各種ライブラリ(自作を読み込む)

npm

  • Node Package Manager
  • package管理は package.json に記述
$ npm init
$ npm install -save [package name]
$ npm install -save-dev [package name]  # 開発用ライブラリ
$ npm install # package.json からライブラリをインストール

$ npm install -g [package name] #グローバル環境にライブラリをインストール

example

$ npm install lodash --save-dev

# インストールされたライブラリは node_modules/ 以下にインストール
$ ls -l node_modules/lodash/

# .gitignore はこんな感じにしとく
$ cat .gitignore
node_modules/

lodashUnderscore.js と並ぶJavascriptの言語機能を拡張する人気ライブラリ

自作ライブラリ

  • module.exports = exports
  • require("my_module")

CHECK POINT 3

browserify

browserify

require がブラウザ(非node環境)でも使える.
起点となるJSから呼びだされているrequireを追跡して一つのJavaScriptファイルにマージしてくれる

$ npm install -g browserify  # グローバル環境に入れる
$ which browserify # <-- browserify 実行ファイル
/Users/hiyuzawa/.nodebrew/current/bin/browserify
  • Hello worldを修正 (git 参照)
$ browserify js/app.js --outfile bundle.js
$ ls -l bundle.js
-rw-r--r--  1 hiyuzawa  Users  412708  1 11 11:47 bundle.js

※ bundle.js も .gitignoreに追加

CHECK POINT 4

gulp

browserifyは便利だけどjsを修正するためにコマンドを実行して bundle.js を作るのは面倒。nodeで書かれたビルドシステムのヘルパーである gulp を利用すると便利

gulp

$ npm install gulp --save-dev
$ ls -l node_modules/.bin/gulp  # -g を付けずにインストールした実行ファイルはここに保存される

# 以下のパッケージも必要なのでインストールしとく
$ npm install browserify --save-dev 
$ npm install gulp-webserver --save-dev   # ファイルの更新を監視するgulpモジュール
$ npm install vinyl-source-stream --save-dev # ファイルに書き出すために必要?なgulpモジュール

gulpfile.js

gulpタスクと定義するファイル. 以下の定義では

  • 3つタスクが定義されている (browserify, watch, webserver)
  • gulpを起動すると 3つのタスクとも起動される
  • browserifyタスクは上述のbrowserifyを実行するタスク (js/app.js -> bundle.js)
  • watchタスクは ./js/*.js を監視して更新されたら browserify タスクを自動で行う
  • webserver はローカルにウエブサーバを起動するタスク. 関連ファイルの更新で自動リロード
var gulp = require('gulp');
var browserify = require('browserify')
var webserver = require('gulp-webserver');
var source = require('vinyl-source-stream');

gulp.task('browserify', function() {
    browserify('./js/app.js', {debug: true})
        .bundle()
        .pipe(source('bundle.js'))
        .pipe(gulp.dest('./'))
});

gulp.task('watch', function(){
    gulp.watch('./js/*.js', ['browserify'])
});

gulp.task('webserver', function(){
    gulp.src('./')
        .pipe(webserver({
                host: '127.0.0.1',
                livereload: true
        }));
});

gulp.task('default', ['browserify', 'watch', 'webserver']);

実行

$ ./node_modules/.bin/gulp
[12:15:44] Using gulpfile ~/../gulpfile.js
[12:15:44] Starting 'browserify'...
[12:15:44] Finished 'browserify' after 19 ms
[12:15:44] Starting 'watch'...
[12:15:44] Finished 'watch' after 14 ms
[12:15:44] Starting 'webserver'...
[12:15:44] Webserver started at http://127.0.0.1:8000
[12:15:44] Finished 'webserver' after 15 ms
[12:15:44] Starting 'default'...
[12:15:44] Finished 'default' after 3.17 μs

http://127.0.0.1:8000 でブラウザ確認できる. jsを編集すると自動でビルド&ブラウザリロードされる

CHECK POINT 5

ES2015(ES6) & Babel

Babel は JavaScriptコンパイラ.
ECMAScript2015 (ES6)やECMAScript7などで書かれたソースコードを一般的なブラウザがサポートしているECMAScript5の形式に出力する.
(ES2015のJavaScript文法を先取りして利用することが出来る)

React(後述)のJSX文法も同様にコンパイルすることができる.

準備 (gulpに適応)

$ npm install babelify --save-dev # babel 本体
$ npm install babel-preset-es2015 --save-dev  # babelはコンパイル方法によってplugin形式で追加インストールする

gulpfile.jsの編集 (babelify のタスクの編集)

...
var babelify = require('babelify'); # モジュールをrequire

gulp.task('browserify', function() {
    browserify('./js/app.js', {debug: true})
        .transform(babelify, {presets: ["es2015"]})  # この部分を追加
        .bundle()
        .pipe(source('bundle.js'))
        .pipe(gulp.dest('./'))
});
...

ES2015のclass構文

export default class MyModule2 {
    constructor(name) {
        this.name =  name
    }
    sayHello() {
        return `Hello My Name is ${this.name}`;   // template構文 (ES2015)
    }
}

require も import 構文が使える

import MyModule2 from './my_module2';
const my_module2 = new MyModule2("hoge");   // 変数宣言const, let (ES2015)

CHECK POINT 6

React による SPA

JavaScriptライブラリの遷移

各ライブラリの特徴はこの辺を参照

SPA (Single Page Application)

  • Flashに変わるHTML5やWebAPIを用いたクライアントサイドにおけるアプリケーション表現技法
  • ディスクトップアプリと同等の表現力(UX)

特徴

  • 単一ページのWebアプリ
  • DOMの操作でページ(UI)切り替え
  • サーバ(データ)のやり取りはWebAPI

React

  • Facebook謹製
  • MVCのVに特化したライブラリ (not Framework)
  • VirtualDOMを内部でサポートしてSPAのデメリットの一つ(DOMの再描画のパフォーマンス)を克服
  • MVCのMの部分は 同じくFacebookが提唱する Flux という仕組みで対応(してもよい)

Hello world of React

先に構築した browserify + babel の環境に React も使えるようにする

ReactはJSX(JavaScript syntax extension)という拡張文法で記載することを推奨されている。
さらにES2015に準拠してもよいがそこは必須ではない

$ npm install react --save-dev       # react 本体
$ npm install react-dom --save-dev   # reactのdom操作部分(最近より別パッケージ化されてる)
$ npm install babel-preset-react --save-dev   # babel の react(jsx) コンパイラプラグイン

gulpfile.js は以下のように babelコンパイル時にreact適応すればok

 gulp.task('browserify', function() {
     browserify('./js/app.js', {debug: true})
-        .transform(babelify, {presets: ["es2015"]})
+        .transform(babelify, {presets: ["es2015", "react"]})
  • Hello コンポーネントを定義して id="content" にアタッチして render する
  • React.createClass でコンポーネント作成 render 関数を定義. return の中身が xml (JSX)
  • 親から渡された変数はコンポーネント内で this.props で取得
    var Hello = React.createClass({
        render: function() {
            return (
                <div className="Hello">
                    Hello React World!! by {this.props.name}
                </div>
            );
        }
    })

    ReactDOM.render(
        <Hello name="hiyuzawa"/>,
        document.getElementById('content')
    );

CHECK POINT 7

Reactで作るアプリケーションはコンポーネントを定義してルートコンポーネントにスタックしていくイメージで作成します。
イメージ的には以下な感じ。

  • define of Root Component
require(Component-A)
require(Component-B)
<div>
    <Component-A />
    <Component-B />
</div>
ReactDOM.render(ROOT-DOM-ELEMENT)
  • define of Component-A
require(Component-C)
<div>
    <Component-C />
</div>

コンポーネントを作成する上で重要なのは

  • 親から渡された変数は this.props で渡される.
  • コンポーネントは状態を持たない(ステートレス) (与えられた変数に応じて一意に定まる)
  • アプリケーション全体の変数はrootコンポーネントで一元管理するのが通例
  • 親から渡されるpropsが変化すると自動的にコンポーネントのDOMがリレンダリングされる
  • リレンダリング時にVirtualDOMの仕組みで変化のあったDOMのみが更新される
  • コンポーネント側で管理される変数は this.state が用意されている。stateを操作する関数として以下が提供されている(以下の関数を操作して始めてDOMが変化する)
    • getInitialState()
    • this.setState(...)

CHECK POINT 8

すこしAdvanceなReactの利用法

  • React内の変数はrootコンポーネントのstateで管理する
  • イベントを処理する(グローバルな変数を扱う)関数はrootコンポーネントで定義しそれを処理を行うコンポーネントまでpropsで輸送する
  • rootのstateが変化すると応じてDOMが再描画される

まあ、実際に動かしてソース見た方が早い。

CHECK POINT 9

refs

コンポーネント内のDOM要素はref属性をつけるとコンポーネント側から参照可能(this.refs)になる

<input type="text" ref="hoge" />
↓
ReactDOM.findDOMNode(this.refs.hoge)

CHECK POINT 10

Redux (flux)

ReactはあくまでもMVCで言うのところのVのライブラリにすぎない。FacebookはReactにおけるデータの取り扱いについてflux という設計手法を提案(推奨している).
fluxはあくまでも手法.実装は各種ある。一番有名でFacebookも推しているのは Redux である

Reduxの紹介を始めるとそうとうややこしくなるので今回は割愛する. 興味あれば以下を参照.

material-ui

material-ui は GoogleのマテリアルデザインをReactベースで利用できるコンポーネント集.
Reactのコンポーネントはステートレスなので、自分のアプリケーションに簡単に適応できる

install

$ npm install material-ui --save-dev
$ npm install react-tap-event-plugin --save-dev  # material-ui@1.0 まではコレが必要

Topレベルで以下を実行しておく (これをしないとタップイベントが反応しなくハマる)

let injectTapEventPlugin = require("react-tap-event-plugin");
injectTapEventPlugin();

app-bar - material-ui

import AppBar from 'material-ui/lib/app-bar';

render: function() {
    return (
        <div className="Hello">
            <AppBar
                title="spa react study"
                iconClassNameRight="muidocs-icon-navigation-expand-more"
                onLeftIconButtonTouchTap={this.menuLeftIconTapped}
            />
            ...

Buttons - material-ui

import RaisedButton from 'material-ui/lib/raised-button';

render() {
    return (
        <div className="sample_es2015">
            <RaisedButton label="count up!" onTouchTap={this.clickCounter} />
            ...

CHECK POINT 11

おわり.

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12