javascript 初心者が react に触ってみる。その 2(React 成分 1 割)
前回までやったこと
- react のチュートリアルをやってみる(途中)。
- javascript のライブラリ管理?モジュール管理?のツールの整理
前回何も考えず文章書いていたら、完全に自分用のメモになっちゃってますね。
今回の範囲
今回はそうならないように気をつけて行ったら、半分以上 React ではなくなってしまいました。
- React の開発用にエディタ (Atom) を整備
- react のチュートリアルを JSX → JS の変換を Production 環境さながらにやってみよう!
- そしたらいろいろ調べないと理解できないぞっていう。ES6 (EcmaScript) とか、CommonJS とか。
- あとは uglifyjs で圧縮かけて、gulp で自動で JSX -> JS の変換をがんばるぞ。
ここまでいけば、他人に渡すときには package.json と gulpfile.js、.babelrc を渡してセッティングしてもらうだけですね、という状態になる。まー boilerplate の作成はまだ早いかなー。
で、予想以上に長くなってしまった。javascript 覚えることおおい。
エディター整備
atom を使っているので、javascript 関連の Atom のパッケージを入れていきます。だいたい次の記事に書いてます。
あと、時代は JSLint ではなく ESLint らしい。
開発したコードをブラウザに読み込ませるまで
まえがき
注意:長いです。はい。ここまで長くなるとは思わなかった。
Babel を使う
Babel って何者さ
React で JSX(JavaScript Xml)を使っているので、それを babel というツールで javascript のコードにする。Facebook が最近 Reactify というツールではなく babel を使うように推奨しているそうです。
Babel はもともと 6to5 (ECMAScript 6 -> 5 に変換する?)という名前だったらしく、そのプラグイとして JSX から javascript への変換も提供されているそうです。
ECMAScript の話(脱線)
ところで、React はんどーなってんのーっていうのが javascript 初心者にはよーくわからんかった。ただ、おじさんがIE8でReact.jsを使うために頑張った話 - ugon105の日記 を読む限り、es5-shim や html5-shiv というものを利用して IE8 対応させているので、おそらく React は HTML5 と ES5 で成り立っているのだろう、と邪推。本当は正攻法で調べるべきなんだろうけれども、まぁ一旦ここで切りあげ。
いろいろ調べると、ES6 の文法のほうが python プログラマ中の下の人にとっては書きやすそうだったので、一応参考となるリンクは調べた。
- どうやってECMAScript 6を学び始めるか
- ECMAScriptの仕様策定に関するカンニングペーパー | Web Scratch
- Babelで始める!モダンJavaScript開発 | HTML5Experts.jp
- babelify で ES6 を試す | アカベコマイリ
Babel の使い方
Babel はデフォルトでは(最近かわって)何もしない設定なので、どのような変換を行うかを指定する必要がある。
Plugins · Babel に変換の一覧があるので、npm でインストールして、/some/git/repos/.babelrc
というファイルを作成して、そこでどのような変換をかますかを記述する。
- Babel 自体のインストール
npm install -g babel
- React の JSX -> JS 用変換プラグインのインストール
npm install babel-plugin-transform-react-jsx --save-dev
.babelrc
ファイルの中身
{ "plugins": ["transform-react-jsx"] }
- JSX -> JS に変換
babel ./project/static/scripts/jsx/example.jsx | diff ./project/static/scripts/jsx/example.jsx -
差分を抜粋
< render: function() { < return ( < <div className="commentBox"> < <h1>Comments</h1> < <CommentList /> < <CommentForm /> < </div> --- > render: function () { > return React.createElement( > 'div', > { className: 'commentBox' }, > React.createElement( > 'h1', > null, > 'Comments' > ), > React.createElement(CommentList, null), > React.createElement(CommentForm, null)
確かに JSX のほうが書きやすそう(小並感)
browserify を使う
こいつなにもの?
Browserify lets you require('modules') in the browser by bundling up all of your dependencies.
javascript はほとんどブラウザの中でしか動かないものでしたが、Node.js 登場以降はサーバサイドで動くようになったそうです(もちろんNode.js 以前にもサーバで機能する javascript は存在はしていた)。で、サーバサイド開発の中で CommonJS という javascript の方言みたいなものが登場(Lisp のなかの clojure みたいな感じでしょうか)。それがモジュール管理など便利になったんだけれどもブラウザさんは「おまえのゆーとることはよーわからん」と。ブラウザさんに解釈可能なように変換するために、browserify
(ブラウザ(Browser)-化(ify))ということなんでしょうか。その 1 では単語だけ出しましたが、ようやく理解しました。
このへんの詳しい説明は JavaScriptのモジュール管理(CommonJSとかAMDとかBrowserifyとかwebpack) | tsuchikazu blog などなど、調べたらいっぱい出てくるので割愛。
使い方
まずは browserify を入手します。
npm install browserify --save-dev
先ほど JSX から JS に変換したコードを browserify に渡してあげます。
babel /path/to/example.jsx | $(npm bin)/browserify - > /path/to/example.js
これでブラウザがやっと認識して、ちゃんと文字も表示できるようになりました。
ちなみに、browserify には、 -t
オプションで変換をかました後に browserify を行うという機能もあり、
他のブログの多くはこっちで行っている。でもこっちほうが最初はわかりやすいかなー(個人的に)。
休憩
いったんここで HTML に上述の example.js
を読み込ませます。すると、
[BABEL] Note: The code generator has deoptimised the styling of "http://localhost:5000/static/scripts/js/example.js" as it exceeds the max of "100KB".
というエラーがでてきました。ファイルおっきすぎるでーっていう警告ですね。ファイルを小さくしろと怒られています。
uglifyjs で圧縮かけるよ
uglify は「醜くする」という意味(つまり、難読化をするようなライブラリに見えるの)ですが、圧縮オプションがあり、ファイルを小さくすることも可能のようです。React 界隈ではこれが使われているみたい?。早速インストール。
npm install uglify-js --save-dev
で、圧縮をかけます。-c
オプション(Compression) で圧縮されます。パイプラインをつなぎこむと読みにくいので、先ほど出力をしたファイルを使います。
$(npm bin)/uglifyjs /path/to/example.js -c > /path/to/example_uglified.js
比較をすると、半分ぐらいになっていることがわかります。
drwxr-xr-x 5 nya nya 170 11 22 16:28 . drwxr-xr-x 5 nya nya 170 11 11 19:26 .. -rw-r--r-- 1 nya nya 668115 11 22 15:53 example.js -rw-r--r-- 1 nya nya 335178 11 22 16:28 example_uglified.js
ちなみに、これでも先ほどの「ファイルおっきすぎやで」という警告はでます。React がおっきいのかね。
gulp を使うよ
ばっくりと gulp の説明
そろそろ、このあたりから一回一回コマンドうって変換するの面倒やわーってなります。 一連の面倒なコマンドを一度にびゃびゃっと実行してくれるツールが有ります。grunt というものもありますが、GitHub でのスター数が gulp (17,624) > grunt (10,205) *1 ですし、Google Trend みても Grunt は縮小傾向なので、gulp にします。
設定と使い方
まずはインストールから。私はグローバル(-g
)でも入れましたが、($(npm bin)/gulp
で使えますし)別に入れなくてもいい気がします。もちろんローカル側には必要です。
npm install -g gulp npm install gulp --save-dev
その後、gulp の設定ファイル(gulpfile.js
)を作ります。
var gulp = require('gulp'); var browserify = require('browserify'); var babelify = require('babelify'); var source = require("vinyl-source-stream"); var uglify = require('gulp-uglify'); var buffer = require('vinyl-buffer'); gulp.task('transform', function () { browserify('./path/to/example.jsx', {transform: ['babelify']}) .bundle() .on("error", function (err) { console.log("Error : " + err.message); }) .pipe(source('example.js')) .pipe(buffer()) .pipe(uglify({ preserveComments: 'license', compress: true})) .pipe(gulp.dest('./path/to/output/')); }); gulp.task('default', ['transform']);
これに必要なモジュールも一式インストールします。
npm install --save-dev gulp-uglify vinyl-buffer vinyl-source-stream
ちなみにこの gulpfile.js
もすっげぇ苦労しましたが、次のリンクが参考になりました。
- The Ultimate Flask Front-End - part 2 - Real Python
- JavaScript - React.js + Babel + Browserify + gulp の環境を作ってみた - Qiita
- gulp で browserify - 新しい日記
- gulp-uglify
とりあえず、イケてない感がすごいです。初心者にパイプライン的な実行で型を意識しろってのは無茶では。
で、gulp
コマンドだけで実行できるようになります。
じゃあ開発するぞ!
……jsx 書き換えるたびに gulp
コマンドを叩くのもなーというめんどくさがりが世の中にいるようで、ソースコードの差分が出てきたたびに、gulp
の一連のタスクを実行するようにした人がいます。ここまでくれば、なんか gulp 使っててよかった、という感じがする(LaTeX にも同じようなものないのかな、と思うぐらい)。参考はこちら。
* gulp-watch
* これからはじめるGulp(3):gulp.watchでファイルの変更を監視しタスクを実行する | Webデザイン、フロントエンド系の技術に関する備忘録 - whiskers
変更内容は至って簡単。
gulp.task('watch', function(){ gulp.watch('./path/to/*.jsx', ['transform']); });
以上。あとは、gulp watch
というコマンドを打つだけ。チョー簡単。ただし、変更してもエラーが出た時しか、変更ファイルが出ない(ただ、エラーしても終了しないところは良い)。
$ gulp watch [18:14:06] Using gulpfile ~/devel/python/react-tutorial/react-offcial-tutorial/gulpfile.js [18:14:06] Starting 'watch'... [18:14:06] Finished 'watch' after 14 ms [18:14:09] Starting 'transform'... [18:14:09] Finished 'transform' after 30 ms [18:15:30] Starting 'transform'... [18:15:30] Finished 'transform' after 20 ms Error : /path/to/example.jsx: Unexpected token (51:2) while parsing file: /path/to/example.jsx
これもなんとかしたい場合は、watch の部分をもうちょっと追記して、最終的に次のようにする。
var gulp = require('gulp'); var browserify = require('browserify'); var babelify = require('babelify'); var source = require("vinyl-source-stream"); var uglify = require('gulp-uglify'); var buffer = require('vinyl-buffer'); gulp.task('transform', function () { browserify('./path/to/example.jsx', {transform: ['babelify']}) .bundle() .on("error", function (err) { console.log("Error : " + err.message); }) .pipe(source('example.js')) .pipe(buffer()) .pipe(uglify({ preserveComments: 'license', compress: true})) .pipe(gulp.dest('./path/to/output/')); }); gulp.task('watch', ['transform'], function(){ var watcher = gulp.watch('./path/to/*.jsx', ['transform']); watcher.on('change', function(event) { console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); }); }); gulp.task('default', ['transform']);
ここまで準備できて、さー React 触るぞー!となりました((差分ビルドなるものが watchfy
でできるらしいが、コンパイルがすぐ終わるし、そこまでしなくていいなかなーと、今回は無視しました。LaTeX のビルドで 1 分かかるとかありますし。))。CommonJS とか ES6 とかいろいろ javascript さんいろいろありすぎやん。