webpackではなく、Rails の gem であるWebpackerの話。
Webpackerを止める方法について書く予定だったが、前提であるWebpackerの説明だけで長くなってしまったので別の記事として切り出した。
脱 Webpacker についてはこちら。 numb86-tech.hatenablog.com
rails newから始めて、WebpackerやVueを使う方法について説明していく。
以下のバージョンで作業している。
- Ruby 2.5.3
- Ruby on Rails 5.2.2
- @rails/webpacker 3.5.5
プロジェクトの作成
まず、$ rails new sample_app --webpack --skip-turbolinksでプロジェクトを作成する。
--webpackオプションによって、Webpackerがインストールされた状態でプロジェクトが作成される。
作成されたプロジェクトを見ると、app/javascriptというディレクトリがある。これが、Webpackerに関するディレクトリ。そして、app/javascript/packsに入っているファイルがエントリファイルになる。
以下で、動作確認用の適当なデータを用意する。
$ bin/rails g scaffold post title:string $ bin/rails db:migrate
$ bin/rails sでサーバーを起動してhttp://localhost:3000/postsにアクセスすれば、ページが表示されるはず。
この画面に、Webpackerでビルドした内容を反映させていく。
javascript_pack_tag
Webpackerでビルドしたスクリプトを読み込むためにはjavascript_pack_tagを使う。
例えば ERB ファイルのなかで<%= javascript_pack_tag 'application' %>とすると、app/javascript/packs/application.jsが読み込まれる。
試しにこれをapp/views/posts/index.html.erbに書いてみる。
app/javascript/packs/application.jsにはconsole.log('Hello World from Webpacker')とあるので、これが実行される。
app/javascript/packs/application.jsを更新してからブラウザをリロードすれば、更新した内容が実行される。
変更を検知して自動的にビルドやブラウザのリロードを行うには、webpack-dev-serverを使う。具体的には、rails sを走らせているときに別プロセスで$ ./bin/webpack-dev-serverを実行すればよい。
ビルドが行われると、public/packs/manifest.jsonが生成・更新される。
{ "application.js": "/packs/application-xxxxx.js", "application.js.map": "/packs/application-xxxxx.js.map" }
このファイルによって、app/javascript/packsに入っているエントリファイルと、public/packsに生成されたダイジェスト値付きのバンドルファイルを紐づけている。
そして、テンプレートファイル(app/views/posts/index.html.erb)を元に作られたHTMLファイルを見ると、ちゃんとダイジェスト付きのファイルを読み込んでいることが分かる。
<script src="/packs/application-xxxxx.js"></script>
以上のように、ダイジェスト付きのバンドルファイルをビルドし、マニフェストファイルによってバンドルファイルとHTMLを紐付けるのが、Webpackerが果たしている役割である。
なお、$ ./bin/webpack-dev-server実行時に生成されるのはpublic/packs/manifest.jsonのみで、バンドルファイルはメモリ上に存在するだけで実際には生成されない。
プロダクション環境で確認する
先程はデベロップメント環境での確認だったので、プロダクションでも確認してみる。
まずは、ローカルでプロダクション環境を起動するための準備を行う。
config/master.keyが存在するかを確認する。- 環境変数
RAILS_SERVE_STATIC_FILESに適当な値を入れる。$ export RAILS_SERVE_STATIC_FILES=1。 $ bin/rails db:migrate RAILS_ENV=productionを実行して、プロダクション環境のデータベースにpostsテーブルを作成する。
これでプロダクション環境を利用できるようになる。
まず、分かりやすくするためにpublic/packsを丸ごと削除してみる。
次に、$ bin/rails assets:precompile RAILS_ENV=productionを実行する。デフォルトでは、このコマンドでWebpackerによるビルドも行われる。
public/packsにマニフェストファイルとバンドルファイルが生成されているのを確認できる。
$ bin/rails s -e productionでプロダクション環境でサーバーが起動される。
http://localhost:3000/postsを表示すると、正しくバンドルファイルが読み込まれているのを確認できる。
stylesheet_pack_tag
Webpackerでは最初から、.cssや.sassなどのスタイルシートにも対応している。
stylesheet_pack_tagを使うことで利用できる。
試しにapp/javascript/packs/application.sassを作成し、app/views/posts/index.html.erbで読み込ませてみる。
// app/javascript/packs/application.sass h1 border-bottom: 3px solid #000
<%= stylesheet_pack_tag 'application' %>
http://localhost:3000/postsを表示すると、<link rel="stylesheet" media="screen" href="/packs/application-xxxxx.css" />でビルドされたスタイルシートを読み込んでいるのが分かる。

もちろんpublic/packs/manifest.jsonにも追加されている。
Vue の導入
Webpackerを使えば、React や Vue などのライブラリを簡単に導入できる。
今回は Vue を使ってみる。
$ bin/rails webpacker:install:vueを実行すれば、Vue を使えるようになる。
サンプルファイルとしてapp/javascript/app.vueとapp/javascript/packs/hello_vue.jsが自動生成されるので、これを読み込ませてみる。
diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb <%= link_to 'New Post', new_post_path %> <%= javascript_pack_tag 'application' %> <%= stylesheet_pack_tag 'application' %> +<%= javascript_pack_tag 'hello_vue' %>
ビルドされたpacks/hello_vue-xxxxx.jsが読み込まれ、Hello Vue!と表示されているのが分かる。

しかし、スタイルが反映されていない。
app/javascript/app.vueには以下の記述があるので、pタグについては中央寄せになっていないとおかしい。
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
マニフェストファイルを見ると、hello_vue.jsだけでなくhello_vue.cssも出力されているのが分かる。
{ "application.css": "/packs/application-xxxxx.css", "application.css.map": "/packs/application-xxxxx.css.map", "application.js": "/packs/application-xxxxx.js", "application.js.map": "/packs/application-xxxxx.js.map", "hello_vue.css": "/packs/hello_vue-xxxxx.css", "hello_vue.css.map": "/packs/hello_vue-xxxxx.css.map", "hello_vue.js": "/packs/hello_vue-xxxxx.js", "hello_vue.js.map": "/packs/hello_vue-xxxxx.js.map" }
実はデフォルトでは、Webpackerはスタイルシートは別ファイルとして抽出してビルドするようになっている。
そのため、スタイルシートも別途読み込む必要がある。
diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb <%= javascript_pack_tag 'application' %> <%= stylesheet_pack_tag 'application' %> <%= javascript_pack_tag 'hello_vue' %> +<%= stylesheet_pack_tag 'hello_vue' %>
これで、スタイルシートも反映される。

画像の利用
次は画像の扱いを見てみる。
次の2つの画像を用意し、それぞれ、スタイルシートと Vue のコンポーネントで使用する。
- app/javascript/images/bg.png
- app/javascript/images/special/usa.png
diff --git a/app/javascript/app.vue b/app/javascript/app.vue index e304dc1..cb37cff 100644 --- a/app/javascript/app.vue +++ b/app/javascript/app.vue @@ -1,6 +1,7 @@ <template> <div id="app"> <p>{{ message }}</p> + <img src="./images/special/usa.png"> </div> </template> diff --git a/app/javascript/packs/application.sass b/app/javascript/packs/application.sass index de2c914..c613a49 100644 --- a/app/javascript/packs/application.sass +++ b/app/javascript/packs/application.sass @@ -1,2 +1,4 @@ h1 border-bottom: 3px solid #000 +p + background-image: url('../images/bg.png')
こうすると、次のように表示される。

マニフェストファイルには、画像について追加されている。
"images/bg.png": "/packs/images/bg-xxxxx.png", "images/special/usa.png": "/packs/images/special/usa-xxxxx.png"
ユニットテストの導入
Vue コンポーネントのユニットテストも導入できる。
詳しくは以下の記事を参照。
numb86-tech.hatenablog.com
Webpacker の設定
Webpackerの設定を確認するには、config/webpack/environment.jsを見ればよい。
このファイルの中に出てくるenvironmentに設定が入っている。
const { environment } = require('@rails/webpacker'); module.exports = environment;
だがこれはWebpacker専用の特殊なオブジェクトなので、environment.toWebpackConfig()でwebpack本来の設定に置き換える必要がある。
diff --git a/config/webpack/development.js b/config/webpack/development.js index c5edff9..617a568 100644 --- a/config/webpack/development.js +++ b/config/webpack/development.js @@ -2,4 +2,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development' const environment = require('./environment') +console.log(environment.toWebpackConfig());