Umi Uyuraのブログ

プログラミング関連の作業ログ

React(JSX)開発にBrowserifyとReactifyを導入

そろそろ <script> タグではなく、Node.jsらしく require でモジュール読み込みしたくなってきたので、Browserifyを試してみることにしました。

ベースとしたプロジェクト

以前React on NW.jsの取っ掛かりとして作ったサンプル があったので、違いを理解するためにも、それをベースにして組み込んでみることにしました。

umi-uyura.hatenablog.com

主要なソースだけ抜き出すと、 index.htmlapp.jsx の2つで、以下のような単純なものです。

index.html

<html>
  <head>
    <title>React on NW.js</title>
    <script src="node_modules/react/dist/react.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script src="build/app.js"></script>
  </body>
</html>

app.jsx

'use strict';

(function() {
  var Hello = React.createClass({
    render: function() {
      return (
          <h1>Hello, {this.props.name} !</h1>
      );
    }
  });

  React.render(
    <Hello name='React Component' />,
    document.getElementById('app')
  );
})();

Browserifyを使ってみる

CommonJS形式のNode.js向けパッケージをブラウザ向けのJSでも読み込めるようにする、つまり require を使えるようにするためのツール

概要を把握するのは、こちら( browserify をはじめてみる - Please Sleep )の記事がわかりやすかったです。

本来なら上記記事でも紹介されているように、GruntやGulpなどのタスクランナーと組み合わせていくのが良いと思われますが、まずは動作を理解するために単独で使うようにしてみました。

インストール

npmからインストール。

依存関係をpackage.jsonに残せるように、 --save-dev を付けています。

$ npm install browserify --save-dev

コードを変更

サンプルをベースに、 require を使ったものに書き換えてみました。

まずは app.jsx からHelloコンポーネント部分を切り出し、単独の Hello.jsx とします。

require で読み込めるように、 module.exports を追加しました。

Hello.jsx

'use strict';

var Hello = React.createClass({
  render: function() {
    return (
      <h1>Hello, {this.props.name} World !</h1>
    );
  }
});

module.exports = Hello;

切り出し元の app.jsx には、切り出したHelloコンポーネントを読み込む require を追加します。

app.jsx

'use strict';

(function() {
  var Hello = require('./Hello');

  React.render(
    <Hello name='React Component' />,
    document.getElementById('app')
  );
})();

index.html で、JSXから変換した app.js を読み込んでいた <script> タグを、 Browserifyで変換後のJavaScriptとする bundle.js へ書き換えます。

index.html

<html>
  <head>
    <title>React on NW.js</title>
    <script src="node_modules/react/dist/react.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script src="build/bundle.js"></script>
  </body>
</html>

Browserifyを実行

Browserify単独ではJSXの変換はできないため、まずはこれまで同様react-toolsでJSXをビルド後、Browserifyを実行します。

$ npm run build     # `jsx ./src ./build --extension jsx`
$ $(npm bin)/browserify build/app.js -o build/bundle.js

生成された bundle.js は、app.js および、 require で読み込まれる先となる Hello.js が結合されたようなものになっていました。

先の index.html の修正で、この bundle.js を読み込むようにしていたため、 npm start (nw) で実行すれば、動作が確認できました。

余談:Browserifyで指定するファイル

Browserifyで入力するファイルは、エントリポイントとなる app.js のみで良いのですが、最初それがわからずに、必要な.jsをすべて指定するのだと思って、

$ $(npm bin)/browserify build/*.js -o build/bundle.js

のように、 build/*.js と実行していたところ、Helloコンポーネントが読み込まれないという状態になっていました。。。

Reactifyを組み合わせてみる

Browserifyの動作としては、複数JavaScriptを1つのJavaScriptファイルへまとめるというもののようですが、Reactで開発をする場合、元のソースはJSX形式になります。

つまり、

JSX -> JavaScript -> Browserify

のように、JSXをJavaScriptへ変換後、変換したJavaScriptをBrowserifyでまとめる、というような流れになります。

先のように、react-toolsの jsx コマンドを実行後、Browserifyを実行するようなワンライナー的なものを書いても良いのですが、ReactifyというBrowserify用のJSX変換プラグインがあるようなので、これを使うことにします。

BrowserifyのGitHubリポジトリには、変換プラグインの一覧( list of transforms · substack/node-browserify Wiki )が掲載されていました。

インストール

こちらも --save-dev を付けて、npmからインストール。

$ npm install reactify --save-dev

コードを修正

このまま、よくあるサンプルのようにBrowserifyのオプションにReactifyを指定すれば動くと思ったのですが、少し修正が必要でした。

Helloコンポーネントを読み込んでいる require の指定に、 .jsx をつけておく必要があり、これがないと、コンポーネントが見つからないというエラーが発生します。

'use strict';

(function() {
  var Hello = require('./Hello.jsx');       // .jsxが必要

  React.render(
    <Hello name='React Component' />,
    document.getElementById('app')
  );
})();

実行してみる

-t オプションで、変換用のプラグインとなるReactifyを指定します。

$ $(npm bin)/browserify -t reactify src/app.jsx -o build/bundle.js

生成されたbundle.jsで実行したところ、問題なく動いてくれました。

まとめ

Browserify + Reactifyが動けば、これまで使っていたreact-toolsは不要ですね。

Web系の開発に便利なプラグインが色々とあるようなので、Browserify自体ももう少し研究してたいと思います。

参考

実践Node.js プログラミング (Programmer's SELECTION)

実践Node.js プログラミング (Programmer's SELECTION)