シンクロ・フード エンジニアブログ

飲食店.COMを運営する、株式会社シンクロ・フードの技術ブログです

既に稼働しているWebサービスに対してgulp+webpackでReactをビルドする

こんにちは。エンジニアの大久保です。年の瀬ギリギリにブログを書いてみました。

シンクロ・フードでは、半年くらい前からReactの導入を少しずつやっているのですが、今回はgulpが動作している非SPAサイトに対するReactビルド環境を紹介したいと思います。

「Reactを使ってみたいけれど、ゼロからプロジェクトを作る機会は少ない。だから稼働中のWebサイトに対してReactを使いたい。でもどのような環境にしているのだろう」という方向けに、一つの事例として参考にしていただければと思います。
結論から言うと、gulp+webpack+babelの組み合わせでReactのJSXをビルドし、JavaScriptは複数のエンドポイントに出力する、という方法を取っています。
尚、RailsでReactを使う場合は違う方法をとっているため、今回はあくまでもgulpでのビルドプロセスがある環境、且つSPAではないサイトにReactを導入する場合の事例です。

gulp+webpackでのビルド

弊社では、すでにgulpでのSassビルドを行なっていたため、gulp経由でReactをビルドしたいと思っていました。
gulp経由でReactをビルドする構成としては、以下の2つが候補に挙がるのではないでしょうか。

  • gulp+browserify+babelify
  • gulp+webpack+babel

弊社では両方とも設定してみた結果、設定としてスッキリする、という理由からwebpackを使ったほうに決めました。当初は、gulp→webpack、という、両方とも似たような役割のツールを重ねることが気持ち悪かったのですが、結果としては気にならなくなりました。
ただし、これは既にgulpを使った環境があるからこのような構成にしたのであって、gulpなどのタスクランナーを使っていないならば、webpackを直接使ってビルドすることをおすすめします。

具体的な導入手順

ディレクトリ構成

ディレクトリ構成は以下のような感じ。わかりやすいように単純化しています。

project  
├── src  
│   └── main  
│       └── webapp  
│           ├── es2015-src  
│           ├── es2015-dest  
│           └── 省略...  
└── gulpfile.js  

なんとなくJavaでのWebサイトを作った場合によくあるディレクトリ構成にしてみました。
上記例では、es2015-srcディレクトリにおいたjsファイルが、es2015-destに出力されることになります。
では、手順を紹介してみます。

パッケージのインストール

まずgulp-webpackのインストール。

npm install --save-dev gulp-webpack  

引き続き、babel関連のインストール。

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react  

更に、vinyl-namedを入れます。これは後ほど説明する、複数エンドポイントにjsを出力するために必要なパッケージです。

npm install --save-dev vinyl-named  

これでビルドに必要なパッケージのインストールは完了です。

gulpfile.js

続いてgulpfile.jsを記載します。

var gulp = require('gulp');  
var webpack = require('gulp-webpack');  
var named = require('vinyl-named');  
  
gulp.task('webpack', function () {  
    return gulp.src('src/main/webapp/es2015-src/**/index.js')  
        .pipe(named(function(file) {  
            return file.relative.replace(/\.[^\.]+$/, '');  
        }))  
        .pipe(webpack({  
            module: {  
                loaders: [  
                    {  
                        test: /\.js?$/,  
                        exclude: /(node_modules)/,  
                        loader: 'babel-loader',  
                        query: {  
                            presets: ['es2015', 'react']  
                        }  
                    }  
                ]  
            }  
        }))  
        .pipe(gulp.dest('src/main/webapp/es2015-dest/'));  
});  

// watchタスク  
gulp.task('watch', function () {  
    gulp.watch('src/main/webapp/es2015-src/**/*.js', ['webpack']);  
});
  
gulp.task('default', ['webpack', 'watch']);  

webpackという関数の中で渡しているオブジェクトがwebpackのオプションです。ここでbabelによるビルドと、presetsを指定することで、JSXをビルドすることができます。
次の項目で説明しますが、上記設定はindex.js、というファイル名のjsファイルだけをビルドするようにしています。それと、複数jsを書き出すための設定が加わっています。

以上で、Reactをビルドするための環境構築は完了です。

gulpfileに関する捕捉

複数jsの書き出しについて

SPAサイトでない場合、ページ毎に読み込むJSが違う、ということはよくあると思います。
弊社もそういったWebサイトを運営しており、webpackでもsrcに入れたjs毎に別々にビルドしたい、というニーズがありました。
jsファイルを追加するたびに、webpackのオプションを修正していけば個別出力は可能なのですが、毎回gulpfileをいじくるのも面倒だったので、src側においたjsの構造をそのままdest側に出力させる、ということをやっています。
そのために、vinyl-namedというパッケージを使って、gulpfileを都度修正することなく、複数jsを出力するようにしています。

ここについては、以下の方のエントリーが非常に参考になりました。
http://2no.hatenablog.com/entry/2015/06/08/170511

なぜindex.jsのみをビルドするか

Reactではimport/exportsを使ったJSを書くことが普通だと思うのですが、*.jsでビルド対象のjsファイルを指定すると、本来ビルド不要な、importされる側のjsも個別にビルドしてしまうため、src側でindex.jsのみをビルドするようにしています。
index.jsの場合はビルド対象、それ以外のファイル名はビルド除外、というルールでやっています。
これは運用でカバーという状態なので、改善が必要だと思っています。

まとめ

以上が弊社のgulpビルドをしている非SPAサイト向けReactビルド環境のご紹介でした。

シンクロ・フードではこのようなフロントエンドに対して興味を持つエンジニアを募集しています(フロントだけではありませんが!)。
興味を持った方がいらっしゃれば、お気軽にご応募ください!それでは!
http://www.synchro-food.co.jp/recruit/