hidekatsu-izuno 日々の記録

プログラミング、経済政策など伊津野英克が興味あることについて適当に語ります(旧サイト:A.R.N [日記])

React 始めました。あるいは、React.js + Webpack の環境構築。

このブログの説明を読むと「プログラミング」という文字が見えるはずなのだけど、今現在に到るまでまったく書けていないということもあり、ずっと興味を持っていた React.js に手を付けてみることしてみた。

ところが着手してみると、Node.js のメジャーバージョンが上がったり、React.js のツールチェインが変わっていたりと、動かすだけでもひと苦労。結局、React.js と webpack の環境構築だけで一日が終わってしまった。昨今はツール類も多種多様で目的に到るまでに時間がかかりすぎるのが困ったもの。

バックエンドは、慣れている Java で書く予定なので以下の作業は Windows 上の eclipse から Terminal を開いて作業しているけれども、Node.js のインストールを除けば特に環境に依存する内容はないと思われる(はず)。

手順 1.Node.js のインストール

Node.jsのサイトからインストーラーをダウンロードしてインストールする。せっかくなので、最新版の v5.0.0 でやってみるかと試してみたのだけれど、(予期はしていたが)以降の作業でいくつかのモジュールがインストールできず先に進まないので、長期サポート版の v4.2.2 を入れなおし再度試してみたところ、こちらは問題ないようだ。

手順 2. package.json の作成

package.json は node.js のパッケージ管理用の定義ファイル。Maven でいうところの pom.xml みたいなもの。npm init とコマンドを打つと質問に答えながらファイルの作成ができる。

> npm init
...
name: (test) hogehoge
...

各項目の詳細はここの説明を参照のこと。最低限、名前とバージョンがあれば動作は可能なようだ。

手順 3.npm で各種ツールをインストール

React.js のサイトを見ると browserfy を使ってのビルドになっているけれども、後々、CSS の JS へのマージや画像の圧縮なども行いたいため webpack を使う方向とした。npm を使い関係するツール類をインストールする。

> npm install --save react react-dom <s>babel</s> babel-core babel-loader babel-preset-es2015 babel-preset-react webpack style-loader css-loader sass-loader

この記述だけ見るとさもどのモジュールを使えばいいのか最初からわかっているかのような感じだけれど、この構成になるまでずいぶん試行錯誤が必要だった。本家サイトの方にも、webpack の場合のインストールガイドを書いておいてほしい……

オプションの「--save」は必須ではないけれども、付けておくとインストールしたモジュールを package.json に依存関係(dependencies)として自動的に追記してくれる。「--save」の代わりに「--save-dev」を使うとビルド時のみ使う依存関係(devDependencies)として追記される。

依存関係が書かれていると、

> npm install

を実行するだけで、指定されたバージョンのモジュール一式がインストールがされる。なかなかに便利。

一応、各モジュールの説明を簡単にしておくと、

  • react: React.js のコアモジュール
  • react-dom: React Native の登場を受けブラウザに依存する部分が別のモジュールとして独立させたもの
  • babel, babel-core, babel-loader: 元々、ES5 や ES6 から ES3 への変換ツールとして開発されてきたツール。現在は、JS変換基盤として利用されており、React では JSX から JS への変換に使われている(旧来のJSXTransformer は廃止)
  • babel-preset-es2015: Babel の ES6 -> ES3 変換モジュール
  • babel-preset-react: Babel の JSX -> JS 変換モジュール
  • webpack: JS、CSS、画像などをひとつのファイルにパッケージングするためのツール
  • style-loader, css-loader: CSS をパッケージするための webpack 用ローダー機能
  • sass-loader: SASS を CSS に変換するための webpack 用ローダー機能

指定されたモジュールのインストールが完了すると React+ES6 + SASS からなるクライアント・プログラムをひとつの JS ファイルとしてパッケージ化することができる。

手順 4.webpack.config.js の作成

webpack の処理はコマンドラインから行うこともできるけれども、設定量が多くなりがちなので、通常は設定ファイルに記載する。

var webpack = require('webpack');
var path = require('path');

module.exports = {
    context: path.join(__dirname, '/src/main/js'),
    entry: {
        app: './main.jsx'
    },
    output: {
        path: path.join(__dirname, '/src/main/webapp/js'),
        filename: './[name].js'
    },
    module: {
        loaders: [
            {
                test: /\.jsx$/,
                loaders: [ 'babel?presets[]=es2015&presets[]=react' ]
            },
            {
                test: /\.js$/,
                loaders: [ 'babel?presets[]=es2015' ]
            },
            {
                test: /\.scss$/,
                loaders: [ 'style', 'css?sourceMap', 'sass?sourceMap' ]
            },
            {
                test: /\.css$/,
                loaders: [ 'style', 'css?sourceMap' ]
            }
        ]
    },
    devtool: '#source-map',
    plugins: [
        new webpack.optimize.UglifyJsPlugin() // minify
    ]
}

path モジュールを使わずに、__dirname + '/src/main/js' と書いたり loaders ではなく loader を使うと意味不明なエラーを出して異常終了するなど、とにかく webpack には何度も躓いたが、ここまで来ればようやく開発に入れる。

それにしても、Node.js 系のプロジェクトはプロジェクトのルートフォルダが設定ファイルだらけでとても萎える。.NET の設定ファイルのように package.json に統一的に設定を書けるくらいのほうがわかりやすいと思うのだけど。GulpとかBabelの設定とか始めるとさらに増えてしまうんだろうなぁ。

手順 5.npm run から実行できるようにする。

今回は、各モジュールをローカルにインストールしたので、webpack コマンドは直接使えない(パスが通っていない)。そこでnpm で依存もタスクも一元化する を参考に project.jsonスクリプトのエントリを追加する。

{
    ...
    "scripts": {
      "build:webpack": "webpack"
    }
}

これで、

> npm run build:webpack

で、webpack の処理が実行できる。ただ、今回使ってみて気付かされたのは webpack 遅すぎ。Ant は起動が遅くて……、などと言っていた時代が嘘かと思うくらいの実行時間(処理に 1分近くかかる)。進歩なのか退化なのか、はたまた歴史は繰り返すというべきか。

2015/11/12追記

babelパッケージは旧パッケージで、現在は babel-core のみインストールすればいいようだったので、該当の箇所には打ち消し線を入れた。また、webpack は相変わらず遅いが、loaders の指定に include で該当のパスを書くか、exclude で node_modules を除くと半分くらいの時間で終わるようになった。とはいえ、それでも 30 秒なので実用に耐えない……。google closure でも commonjs については対応できるので、そっちにしようかと思案中。