追記(2017.12.12)
webpacker 3系に対応した記事 を書きました
Webpacker2.0からデフォルトでcss(sass)のビルドも出来るようになってて且つ、digestの付与とかも問題なくいけるっぽいので、現在制作中でSPAではなくまだjavascriptの規模が大きくないアプリのjavascriptとsassをwebpackerに移行してみました。
モチベーションとしてはjavascriptはES6で書きたい+viewライブラリ使いたい、sassはPostCSS使いたいとかよくある感じの奴です。
インストール
Gemfileにwebpackerを追加します。
gem "webpacker", "~> 2.0"
webpackerの設定を追加します。
$ bin/rails webpacker:install
上記コマンドでwebpacker(webpack)の設定とか諸々追加されます。ちょっと特徴的なのがwebpackでコンパイル対象となるディレクトリがapp/javascript/packs
以下のファイル全てがentryになるところでしょうか。
構成
私自身が普段基盤システムやDevOps系のことをやっててフロントエンド周りにそこまで詳しくないので、今回は一旦assets以下の構造をapp/javascript
以下に移行する形でやってみようと思います。
app/javascript
│
├── javascripts
│ ├── application.js
│ ├── ...
│
├── packs
│ └── application.js
│
└── stylesheets
├── application.scss
├── ...
packs/application.js
ではjavascripts/application.js
とstylesheets/application.scss
をimportするだけにして、それぞれをこれまでのsprocketsと同じように管理できるようにしてみます。
import '../javascripts/application';
import '../stylesheets/application';
app/javascript/javascripts
というディレクトリがだいぶアレな感じですが、webpackの設定を変えればいいだけなので一旦動作するところまではそのままでいっちゃいましょう。
起動設定
はじめに、簡単に動作確認できるようにforemanでwebpack-dev-serverを起動する設定をやっておきます。
group :development do
gem "foreman"
#...
end
web: bin/rails s
webpacker: bin/webpack-dev-server
$ bundle install
$ bundle binstubs foreman
これで、 bin/foreman start
で開発用のサーバーが立ち上がります。ただ、foreman経由で起動した場合にRailsのポートが5000になるので、http://localhost:5000 でアクセスしましょう。
Sass
まずはじめにsassをwebpacker管理に移行します。app/assets/stylesheets
ディレクトリ以下のファイルをまるっとapp/javascript/stylesheets
ディレクトリにコピーしちゃいましょう。
次に、webpackのsass-loaderはglobでのimport (@import "layout/*";
みたいな奴) に対応していないので、import-glob-loader
とその設定を追加します。
$ yarn add import-glob-loader
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const { env } = require('../configuration.js')
module.exports = {
test: /\.(scss|sass|css)$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
{ loader: 'postcss-loader', options: { sourceMap: true } },
'resolve-url-loader',
{ loader: 'sass-loader', options: { sourceMap: true } },
'import-glob-loader', // ここに追加
]
})
}
これで、globでのimportの解決ができるようになります。
次に、app/views/layouts/application.html.haml
に以下の行を追記します。
-# = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
後はwebpackerのデフォルト設定でsassやpostcssの設定が幾つか含まれているので、gem系のassetsやimage-urlなどのhelperを使ってなければ多分そのまま動きます。
sprokectsのsass helperを使っている場合には このへん を参考に泣きながら修正しましょう。
gemでfont-awesomeやbootstrap-sassを使っている場合には若干の変更が必要です。基本的には対応するnpmパッケージをインストールしてパスを書き換えるだけなので、さくっとやっちゃいましょう。
font-awesome
yarnでパッケージを追加します。
$ yarn add font-awesome
次に、sassでのimportを以下に変更します。
@import "~font-awesome/scss/font-awesome";
ちなみに、gem "font-awesome"
を使ってる場合、fa_icon
などのhelperはそのまま使い続けても問題ないので無理に外す必要は無いです。
bootstrap-sass
yarnでパッケージを追加します。
$ yarn add bootstrap-sass
次に、sassでのimportを以下に変更します。
$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/';
@import '~bootstrap-sass/assets/stylesheets/bootstrap';
あるいは個別にカスタマイズしているのであれば、
$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/';
// Bootstrap v3.3.7 (http://getbootstrap.com)
// Core variables and mixins
@import "~bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
// Reset and dependencies
@import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/print";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/glyphicons";
// Core CSS
@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/type";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/code";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/forms";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/buttons";
// Components
@import "~bootstrap-sass/assets/stylesheets/bootstrap/component-animations";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/button-groups";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/input-groups";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/navs";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/navbar";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/pagination";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/pager";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/labels";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/badges";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/jumbotron";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/thumbnails";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/progress-bars";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/media";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/list-group";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/panels";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-embed";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/close";
// Components w/ JavaScript
@import "~bootstrap-sass/assets/stylesheets/bootstrap/modals";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/tooltip";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/popovers";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/carousel";
// Utility classes
@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
その他のパッケージも同じようなノリで書き換えちゃっていきましょう。
javascript (coffeescript)
javascriptの場合はsprocketsのrequireをes6 styleのimportに書き換える必要があるので、ちょっとづつ移行していきます。coffee-loaderもデフォルトで含まれているので、一旦は.coffeeのままコピーしても問題無いです。
// TODO
import './xxxxx.coffee';
import './yyyyy.coffee';
// ...
gem系のassetsに関しても同じようにnpmから同様のバージョンを追加していきます。
bootstrap-sass (+ jQuery)
yarnでパッケージを追加します。
$ yarn add bootstrap-sass # sassの時に追加してたら要らないです
$ yarn add jquery
次に、jsのimportを追加します。
import 'bootstrap-sass';
jQueryに関しては、$
とjQuery
がグローバルにエクスポートされている必要があるので、webpackの設定の方を変更します。
// Note: You must restart bin/webpack-dev-server for changes to take effect
//...
plugins: [
new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
new ManifestPlugin({
publicPath: output.publicPath,
writeToFileEmit: true
}),
new webpack.ProvidePlugin({ // ここに追加
$: 'jquery',
jQuery: 'jquery'
})
],
rails-ujs + turbolinks
ただ、以下のライブラリは一旦gemそのままで使うことにしたため、こちらはsprocketsと併用しています。
//= require rails-ujs
//= require turbolinks
どちらもnpmにパッケージがあるので移行すること自体は問題ないですが、rails-ujs
に関してはRailsのバージョン変更の際の考慮漏れになりそうだったのと、turbolinksはredirect_to
に幾つかの処理を追加するような感じになっていたので今回はそのままにすることにしました。ただ、これもそのうち全部移行するかもしれません。
最後に、app/views/layouts/application.html.haml
に以下の行を追記します。
= javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
= javascript_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
終わり
これで、今のところ既存からの変更をあまり意識せずいい感じに動いています。ただ、本番運用してないのでもしかしたらもう少し問題が出るかもしれないのでなんかツッコミあったらよろしくお願いします
webpackerに関しても、フロントエンドに明るくない側からするとある程度デフォルトで色々入ってるのは結構助かるかなーと。デフォルト設定がイケてないとかあるっぽいですが、まあ別にインストールした後自分で変更しちゃえばいいんじゃないですかね。