Elaboration in, Garbage out

Twitt*r ではメモできない何かそれ的なモノ・コトを

javascript 初心者が react に触ってみる。その 2(React 成分 1 割)

前回までやったこと

gre.hatenablog.com

前回何も考えず文章書いていたら、完全に自分用のメモになっちゃってますね。

今回の範囲

今回はそうならないように気をつけて行ったら、半分以上 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 プログラマ中の下の人にとっては書きやすそうだったので、一応参考となるリンクは調べた。

Babel の使い方

Babel はデフォルトでは(最近かわって)何もしない設定なので、どのような変換を行うかを指定する必要がある。 Plugins · Babel に変換の一覧があるので、npm でインストールして、/some/git/repos/.babelrc というファイルを作成して、そこでどのような変換をかますかを記述する。

  • Babel 自体のインストール
npm install -g babel
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 にします。

f:id:huacha:20151122170026p:plain

設定と使い方

まずはインストールから。私はグローバル(-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 もすっげぇ苦労しましたが、次のリンクが参考になりました。

とりあえず、イケてない感がすごいです。初心者にパイプライン的な実行で型を意識しろってのは無茶では。

で、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 さんいろいろありすぎやん。

*1:2015/11/22 17:00 JST 時点