RailsでCoffeeScriptを共存させつつES6やJSXを使う方法
こんにちは。いるかさんです。今回は技術系の記事となります!
社内のとあるRailsプロジェクトで、「ES6を使おうよ!」という動きが高まっています。
(ES6については、よしこさんのスライドが大変分かりやすいので、ぜひご一読ください。)
主に 「Coffee && JSX な lint がない」 という点と、 「Coffeeを使っていくよりES6を使うようにした方が余分な学習コストが減るし今後に生きる」 という点で検討されていました。
しかし、現在はCoffeeScriptで開発しているため、一度にES6へ置き換えるのは難しいです。
そこで、以下の要件を満たすように環境を整えました。
- CoffeeScript+JSXは今までどおり使うことができる
- ES6を使うことができる
- ES6 で JSX を書くことができる (Reactを利用しているため)
- CoffeeScriptからES6やES6なJSXを読み込んで使うことができる
- ついでに
npm
も使えたら嬉しいな(・∀・)
候補1 sprockets-es6
結論から先に書くと、sprockets-es6
は実験用であり、実運用向けではありません。
sprockets-es6 (https://github.com/TannerRogalsky/sprockets-es6) は、
かなり手軽に導入できて、ES6を使うことができるライブラリです。
READMEに記載されているように、Gemfileに追記してbundleするだけで、ES6が使えました。
導入時に、sprockets
のバージョンが2系だと、bundle update sprockets
する必要があります。
gem 'sprockets-es6', require: 'sprockets/es6'
問題点
import/export
が使えないという噂*1を見かけました。本当かどうかは調べていません。
また、READMEにも以下のように書かれています。そのためか、Issueも投げられるようになっていません。
This plugin is primarily experimental and will never reach a stable 1.0. The purpose is to test out BabelJS features on Sprockets 3.x and include it by default in Sprockets 4.x.
(このプラグインは実験的なものなので安定版を出すつもりはないよ!このプラグインの目的は、BabelJSの機能をSprockets3系で試すためなのだ。)
よって、製品に使うのには向いていないと言えるでしょう。
候補2 browserify-rails
browserify-rails
https://github.com/browserify-rails/browserify-rails は、sprockets
を通して、
browserify
を利用して、CommonJSのモジュールの仕組み (require
とか使えるようになる) を実現してくれるライブラリです。
要するに、js でrequire
が使えるようになる、ということです。
さらにbrowserify
には、トランスパイル機能がついています。これを使って、ES6やJSXを利用しよう!という目論見です。
これを使ってあげれば、CoffeeScriptはそのままに、es6やjsxが使えそうですね!
npmも使うことができるので、一石二鳥です(*°∀°)=3
今回は、当該プロジェクトではsprockets-coffee-react
を使っていたので、CoffeeScript はbrowserify-rails
のトランスパイル設定を書かず、ES6やJSXだけ対象にしました。
導入
Gemfileにbrowserify-rails
を書いて、bundle install
します。
さらに、npm init
してnpm install browserify browserify-incremental --save-dev
します。
これだけで導入は終わりです。
トランスパイルの設定
トランスパイルの設定は、config/application.rb に書きます。
- Coffeeをトランスパイルしたい場合
npm install coffeeify --save
してから、以下の設定を追加します。
config.browserify_rails.commandline_options = "-t coffeeify --extension=\".js.coffee\""
- ES6 + JSX をトランスパイルしたい場合
npm install babelify babel-preset-es2015 babel-preset-react --save-dev
してから、以下の設定を追加します。
config.browserify_rails.commandline_options = "-t babelify"
さらに、babel
の設定を .babelrc に書きます。
{ "presets": ["es2015", "react"] }
これでトランスパイルも自在にコントロールできるようになりました。
CoffeeScriptや外部からES6で書かれたクラスなどを利用する
CoffeeScriptでは、ES6のimport
が使えません。CoffeeScriptの文法エラーになるからです。
そこで、グローバル領域に置いておく方法と、require
する方法で、
CoffeeScriptや外部からES6の資源を利用することができました。
ES6側でグローバル領域に定義しておく
ES6/JSX側でwindow
に引っ付けておくことで解決する方法です。
あまり綺麗な解決法じゃないですね(;´∀`)
react-rails
のreact_component
を使って、JSX + ES6 で書かれたReactコンポーネントを描画してみましたが、ちゃんと読み込めました。
これならどこからでも参照できるので、ライブラリ的なものはこれでいいかもしれません。
export default class Hoge extends React.Component { /* 中略 */ } window.Hoge = Hoge;
require して利用する
Coffeeのコンパイル時に、require
を用いても、ただの関数なので、エラーにはなりません。
そして、browserify-rails
によって、require
をよしなにしてくれます。
これだと、それっぽく利用することができました。
- Mofu.es6
export default class Mofu { static get hoge() { return 'bar'; } }
- MofuUser.coffee
Mofu = require('./Mofu.es6').default console.log(Mofu.hoge)
npmの利用
browserify-rails
の導入により、npm
を利用して、jsのライブラリを利用することができます。これは嬉しい!
適当にpackage.json
を用意し、npm install hogehoge --save
してあげれば、インストールされます。
node_modules
は.gitignore
に入れておきます。
- package.json
{ "name": "moffumofu", "dependencies": { "browserify": "~> 10.2.4", "browserify-incremental": "^3.0.1" } }
npm
でインストールした後は、使いたいファイルでrequire
を使って読み込んであげれば使えます。
browserify-railsの問題点 テストの時間が長い
当該プロジェクトにbrowserify-rails
を導入してCircleCI
でテストしたところ、タイムアウトしました。
これは、事前にassets:precompile
しておくことで回避*2できました。
ちなみに、ローカル環境でのテストは、そこまで時間がかかるという印象は受けませんでした。
(余談) JSX + ES6 でReactを書く時のつまづきポイント
既存の、Coffeeで書かれたReactComponentをJSX+ES6に書き直してみましたが、そのままではうまく動きませんでした。
以下の記事を参考にして、全て解決しました。感謝感謝。
getInitialState
getInitialState
は使われず、constructor
の中で直接this.state
にハッシュを入れてやることで解決しました。そういう仕様らしい。
callback
callbackのthis
が、オブジェクトのインスタンスじゃなくなってました。これは、.bind(this)
をつけたり、アロー関数にして解決しました。
終わりに
今回の方法では、css
を含むライブラリをnpm
でインストールしても、そのCSSをうまくRailsで使う方法をまだ見つけられていません。
おそらく、アセットパスの都合などもあり、一筋縄ではいかないだろうという感じです。
そこで、bower-rails
を使うと、bower:install
/ bower:resolve
でよしなにしてくれるらしいので、
次はbower-rails
を導入して、cssも含むjsライブラリ関係のバージョン管理が楽にできるようにしていこうと思っています。
実は、このES6導入、社内ではPullRequest段階でまだマージされてません。アサインされてる人、レビュー( `・∀・´)ノヨロシク
*1:http://qiita.com/cotto89/items/8f7f67bfd726de023f13 > 一番手軽そうな選択肢だがimport/exportが使えないらしい。
*2:https://github.com/browserify-rails/browserify-rails#acceptance-test-failures