はじめに
フロントエンドへスプラトゥーンを決めるために、Rails プロジェクト内で JavaScript を楽に良い感じに分離する方法を考えてみました。
先人たちが様々な方法を提示してくださっていましたが、設定が大掛かりだったり、何種類かのツールを活用したりと、自分にはどうもハードルが高かったため、より簡単な方法を模索しました。
TL;DR
Initial commit で、この記事でこれから行う設定をコミットしています。
特徴
- Sprockets の良い所はそのまま活用
- 導入ツールは実質 WebPack のみ
- 設定項目が少なく簡単
- おそらくほぼメンテナンスフリー
概要
以下のような流れで、JavaScript のコーディング、ビルド、配信を行います。
-
frontend
ディレクトリ以下で JavaScript のコーディングを行う -
frontend
以下で作成した JavaScript をapp/assets/javascripts
以下へビルドする - ビルド後にできた
app/assets/javascripts
内のファイルを結合せずにそのまま配信する
Sprockets の良い所というのは、Fingerprint を付けて配信してくれる点、つまり3
の部分だと個人的には思います。
この機能を他のフロントエンド系のビルドツールでカバーしようとすると、大工事やツールが必要になります。
つまり、ここを大人しく Sprockets に任すことで、大掛かりな設定を行うことなく、Rails から JavaScript を分離させることができるようになります。
Views が Rails に残ってしまっていますが、全てのフロントエンドを消し去るのであれば、そもそも Rails API にすれば良いという噂もあるので、これくらいの分離具合が丁度良いんじゃないかなと思います。
具体例
概要に少しだけ具体性をもたせると、以下のような感じになります。
-
frontend/src/javascripts/hoge.js
でモダンな JS 開発を行う - WebPack で
frontend/src/javascripts/hoge.js
をビルドし、assets/javascripts/hoge.js
として出力する - Sprockets で
assets/javascripts/hoge.js
に Fingerprint を付けて配信する
使用ツールとバージョン
基本的には WebPack のみが追加ツールとなるイメージです。
WebPack でのビルド時に Transpile を行いたかったので、Babel も導入しています。
- Sprockets 3.4.1 (+ Rails 4.2.5)
- 配信 (with MD5 fingerprint)
- WebPack 1.12.9 (+ Babel 6.1.18)
- ビルド
ディレクトリ構成
Rails プロジェクトのルートにfrontend
というディレクトリを追加しています。
$ ls
Gemfile README.md app/ config/ db/ lib/ spec/
Gemfile.lock Rakefile bin/ config.ru frontend/ public/ vendor/
frontend
のディレクトリ構成は以下のとおりです。
$ tree frontend -I node_modules
.
├── config
│ ├── development
│ │ └── webpack.config.js
│ └── production
│ └── webpack.config.js
├── package.json
├── src
│ └── javascripts
│ └── application.js
└── test
└── javascripts
おおまかに、
- package.json
- WebPack 設定ファイル
- ソース置き場
- テスト置き場
の4つから構成されています。
設定手順
具体的な設定手順について述べていきます。
- Sprockets で分割配信するように設定する
- WebPack でのビルドに関する設定を行う
- WebPack のビルドを Precompile にフックさせる
1. Sprockets で分割配信するように設定する
Rails のデフォルトの設定だと、application.js
に全ての JavaScript ファイルが結合されて配信されるようになっているので、この設定を変更します。
app/assets/javascripts
以下を一掃する
app/assets/javascripts
ディレクトリには、ビルド後の js ファイルが格納される場所となるため、このディレクトリは空にします。
Git 管理下からも外したいので、.gitignore
に以下のように追記しておきます。
!/app/assets/javascripts/.keep
/app/assets/javascripts
また、ついでに CSS も分割配信にしたかったので、app/assets/stylesheets/application.scss
内の*= require_tree .
という行も削除しておきました。
Sprockets の Precompile 対象を追加する
デフォルトではapplication.js
とapplication.css
しか対象になっていないため、config/initializers/assets.rb
に設定を追加し、全ての JavaScript と CSS ファイルを対象にするようにします。
Rails.application.config.assets.precompile += %w(*.js *.css)
アンダースコアから始まるファイルを除去する
Rails で Views 系の Gem を使っている場合、使用している Gem によっては Precompile でエラーが発生する場合があります。
その場合は、上記の設定の代わりに、以下のアンダースコアから始まるファイルを対象外とする設定を使用すれば良さそうです。
Rails.application.config.assets.precompile << /(^[^_\/]|\/[^_])[^\/]*(\.js|\.css)$/
補足: Rails での JavaScript と CSS の読み込み方
それぞれ Views テンプレート内で、
-
javascript_include_tag
ヘルパー -
stylesheet_link_tag
ヘルパー
を呼び出すことで使用可能になります。
2. WebPack でのビルドに関する設定を行う
WebPack と、WebPack で Transpiler として使用する Babel をインストールします。
もしfrontend
直下にpackage.json
がない場合は、先にnpm init
を実行しておきます。
$ npm init
$ npm install -D webpack babel babel-loader babel-core
例: React.js と ES2015 と Stage2 を使う
例として、React.js (0.14.3) と ES2015 と Stage2 を使えるように設定を進めていきます。
まずは、React.js をインストールします。
$ npm install --save react react-dom
次に、必要な Babel の Preset をインストールします。
$ npm install -D babel-preset-react babel-preset-es2015 babel-preset-stage-2
WebPack で Babel を使った Transpile ができるよう、設定ファイルを作成します。
module.exports = {
devtool: 'inline-source-map',
entry: {
application: './src/javascripts/application.js',
},
output: {
path: '../app/assets/javascripts',
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel?presets[]=react,presets[]=es2015,presets[]=stage-2'
}
]
}
}
本番用は devtool オブションを外せばよいと思います。たぶん。
module.exports = {
entry: {
application: './src/javascripts/application.js',
},
output: {
path: '../app/assets/javascripts',
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel?presets[]=react,presets[]=es2015,presets[]=stage-2'
}
]
}
}
これで、webpack --config config/{enviroment}/webpack.config.js
を叩けば、entry に登録された JavaScript がapp/assets/javascripts/*.js
にビルドされます。
上記の設定ですと、frontend/src/javascripts/application.js
をエントリーとして、app/assets/javascripts/application.js
にビルドされます。
作成したい JavaScript ファイルが増えても、entry に新たなエントリーを追加し、それを使いたい Views でヘルパーで読み込むだけで済みます。
npm run
で起動できるようにする
npm run build
のようなコマンドを準備しておくととても快適になるので、
- release:
webpack --config config/production/webpack.config.js"
- build:
webpack --config config/development/webpack.config.js"
- watch:
webpack --watch --config config/development/webpack.config.js"
の3つを package.jsonに登録しておきます。
{
"scripts": {
"release": "webpack --config config/production/webpack.config.js",
"build": "webpack --config config/development/webpack.config.js",
"watch": "webpack --watch --config config/development/webpack.config.js"
}
}
これでfrontend
直下でnpm run build
と叩けば、WebPack によるビルドが実行されます。
3. WebPack のビルドを Precompile にフックさせる
最後に本番リリース時の処理に特別な手法を加えなくて済むように、Precompile の直前に、npm run release
を実行させるようにします。
task :build_frontend do
cd "frontend" do
sh "npm install"
sh "npm run release"
end
end
Rake::Task["assets:precompile"].enhance(%i(build_frontend))
これで、rake assets:precompile
を叩くと、その直前にfrontend
ディレクトリ直下で、npm run release
が実行されるようになります。
まとめ
Rails から JavaScript を良い感じに分離させるために、
- WebPack で JavaScripts を
app/assets/javascripts
以下へビルドする - Sprockets で
app/assets/javascripts
以下のファイルをそのまま配信する
というアプローチをとってみました。
このアプローチの利点は、設定項目や追加ツールが少なく、メンテも楽だという点だと思います。
無理に Sprockets を置き換えていないことで、JavaScript のビルド設定が非常に単純化されています。
Why we should stop using Grunt & Gulp を読んでから、Gulp 無しで簡潔に設定できないかなと考えていたのですが、Sprockets を適度に受け入れてやることで、思いの外楽に実現できました。
良い塩梅の独立具合になってるんじゃないかと思います。
追記: 完全分離
Sprockets のメリットは Fingerprint 付きでの配信と書きましたが、それをやってくれる WebPack のプラグインがありました。
こちらを使うと Fingerprint 付きでの配信までが WebPack で完結するため、Rails から JavaScript を完全分離できます。
どのようにパス解決を行っているかというと、ビルドすると以下のような対応が定義された JSON が出力されます。こちらを用いてパス解決を行います。
{
"one": {
"js": "/js/one_2bb80372ebe8047a68d4.bundle.js"
},
"two": {
"js": "/js/two_2bb80372ebe8047a68d4.bundle.js"
}
}
Rails での使用例が README の Using this with Rails に載っていますが、非常に簡単そうです。
参考文献
- Why we should stop using Grunt & Gulp
- Replacing the Rails asset pipeline with Gulp
- Gulp - a modern approach to asset pipeline for Rails developers
- Gulp on Rails: Replacing the Asset Pipeline
- フロントエンドエンジニアのための「俺の最近のRailsのJS開発環境」を考察する
- react + rails 構成パターンまとめ。
- RailsのAsset PipelineとPrecompileをNode.jsのみで処理できるgulp-sprocketsを作った