動機
ReactでSPAを作ろうとしたとき、何も工夫せずにbrowserifyを使うとコンパイル等で非常に時間がかかったり、生成物のサイズがでかくなりすぎたりしがちです。
そこで、ライブラリ等はなるべくCDN経由で取得してみてはどうかと思い、gulpfile.jsを考えてみました。
結果として、コンパイル後の生成物のサイズが抑えられ、タスクも短時間で完了するようになりました。(たとえばuglifyを使う場合などには如実です)
npmパッケージ
まず、本記事で利用するnpmパッケージは、次のとおりです。
(バージョン等は、投稿時のものです。)
{
... 省略 ...
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babelify": "^7.2.0",
"browserify": "^12.0.1",
"gulp": "^3.9.0",
"literalify": "^0.4.0",
"react": "^0.14.3",
"react-dom": "^0.14.3",
"react-router": "^1.0.0"
}
}
gulpfile.js
gulpfile.jsは下記のようにしてみました。
内容としては、browser.jsxをbrowser.jsにコンパイルするというものです。babelifyの後にliteralifyを使っているのがポイントです。
const gulp = require('gulp');
const browserify = require('browserify');
const babelify = require('babelify');
const literalify = require('literalify');
const fs = require('fs');
gulp.task('browser', () => {
return browserify({debug: true})
.transform(babelify.configure({
presets: ['es2015', 'react']
}))
.transform(literalify.configure({
'react': 'window.React',
'react-dom': 'window.ReactDOM',
'react-router': 'window.ReactRouter'
}))
.require('browser.jsx', {entry: true})
.bundle()
.on('error', (error) => {
console.log(`Error: ${error.message}`);
})
.pipe(fs.createWriteStream('browser.js'));
});
index.html
次に、SPAのレンダリング先となるindex.htmlを準備します。reactやreact-router等はcdnjsから取ってくるようにしています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>SPA</title>
</head>
<body>
<div id="application"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/1.0.0/ReactRouter.js"></script>
<script src="browser.js"></script>
</body>
</html>
JSXファイル
説明用のシンプル(すぎる)アプリケーションは次のとおりです。特筆すべき点はありません。ご参考まで。
import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route} from 'react-router';
const Header = () => (
<header>header</header>
);
const Main = () => (
<main>main</main>
);
const Footer = () => (
<footer>footer</footer>
);
const Application = React.createClass({
render() {
return (
<div>
<Header />
<Main />
<Footer />
</div>
);
}
});
ReactDOM.render((
<Router>
<Route path="/" component={Application} />
</Router>
), document.getElementById('application'));
$ gulp browser [Enter]
これで、ひとまずコマンドラインでのgulp browser
が実行できるようになります。
(その前にnpm install -g gulp
も必要かも)
できあがったjsファイルを覗いてみると、うまいこと動いていることが確認できます。
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){... 省略 ...
'use strict';
var _react = window.React;
var _react2 = _interopRequireDefault(_react);
var _reactDom = window.ReactDOM;
var _reactDom2 = _interopRequireDefault(_reactDom);
var _reactRouter = window.ReactRouter;
... 省略 ...
おわりに
名前の付け方やディレクトリ分け等については、社内文化などもあると思いますので適宜設定ください。その際は、fs.createWriteStreamのパス指定先のディレクトリがない場合にエラーがでますので、予め出力先ディレクトリを準備する必要があります。
そのほか、必要あらばgulpのwatchタスク等もご準備ください。
あと気になった点としては、requireとimportの使い分けが必要なところが、一貫性という点では問題のある方法かもしれません...