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());