RailsでViteを使用したらハマりまくった件
前置き
本記事では、以下の構成で Vite を使った Rails 開発環境を構築し、 ホットリロードが動作するまでに必要な設定と、実際に起きたトラブルとその解決方法 をまとめています。
vite_rails(vite_ruby)独自の挙動や設定があり、調査に時間がかかったのでまとめました。
※ 環境構築手順は GitHub リポジトリに上げてあります(ページ下部参照)。
前提
- ホストにdocker,gitが導入済みであること
- webとnpm run devが同一環境であること
- Gemfileに
vite_rails
を追加し、インストール済みであること
まだならgitのREADMEをみてください。
環境概要
本アプリケーションは、以下の技術スタックおよびDockerを用いた仮想環境で構築されています。
アプリケーション構成
- ruby 3.4.2
- Rails 8.0.2
- postgres 17.5
- pgAdmin 9.4
- Bundler version 2.6.9
- puma version 6.6.0
- node.js v20.19.3
- npm 11.4.2
- nginx version: nginx/1.27.5
- tailwindcss 3.x
Gemfile
rails new後、自動で追加されるものに加え、以下を追加しています。(最低限)
- dotenv-rails
- vite_rails (3.0.19)
- vite_ruby (3.9.2)→vite_railsの内部で依存しており、自動でインストールされる
- pry-rails
- pry-byebug
- pry-nav
一気に変更するファイルまで飛ばします。
vite関連の設定変更箇所
viteの初期設定(bundle exec vite install後)に以下のファイルが作成される
- config/vite.json
- vite.config.ts
- procfile.dev
1と2+.envの設定が必要になる。
3はbin/vite devができるようになる設定ファイル
設定の詳細と期待される結果は以下に記載する。
config/vite.json
設定例
ポイントは
sourceCodeDir
をassetsを配置している箇所に設定する。
additionalEntrypoints
にビルドしたいファイルを指定する。
development
のhost
、autoBuild
の設定を確認する。
今回、app/frontend
以下にcss
,js
,images
が存在し、さらにその中にurl単位のディレクトリが存在する。
その場合はadditionalEntrypoints
で明示的に読まないとビルド対象とならない。
{
"all": {
"sourceCodeDir": "app/frontend",
"watchAdditionalPaths": [
"app/frontend",
"vendor/assets"
],
"entrypointsDir": "app/frontend",
"additionalEntrypoints": [
"~/js/**/*",
"~/css/**/*",
"~/images/**/*"
]
},
"development": {
"host": "0.0.0.0",
"autoBuild": true,
"publicOutputDir": "vite-dev",
"port": 3036
},
"test": {
"autoBuild": true,
"publicOutputDir": "vite-test",
"port": 3037
}
}
vite.config.ts
tailwindcssのバージョンがv4だと別途パッケージ追加が必要になりますが、ここでは省略します。
npm installして以下のようにimportすればいいはずです。
ポイント:
-
server
のhost
,port
をconfig/vite.json
と合わせる。 -
resolve
のalias
にvueを設定する。 -
plugins
にRubyPlugin()
,vue()
を設定する。
注意点:
server
のhost
,port
は環境変数にVITE_RUBY_HOST
、VITE_RUBY_PORT
が設定されていると
vite.config.ts
に値を設定しても使用されない。
正確にはViteはvite.config.ts
よりも.env
で定義されたVITE_RUBY_HOST
/VITE_RUBY_PORT
を優先して読み取る為、結果として設定した値が使用されない現象が起きる。
気づきにくい仕様があるので環境変数の設定をしている人は注意が必要。
また、defineConfig
にbuildの設定が可能ですが、今回はconfig/vite.json
に集約しています。
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// import tailwindcss from "@tailwindcss/vite"; // v4用
export default defineConfig({
server: {
host: '0.0.0.0',
port: 3036,
strictPort: true,
},
resolve: {
alias: {
'vue': 'vue/dist/vue.esm-bundler.js',
'@': path.resolve(__dirname, 'app/frontend/js'),
'@components': path.resolve(__dirname, 'app/frontend/js/components'),
'@css': path.resolve(__dirname, 'app/frontend/css'),
},
},
plugins: [
RubyPlugin(),
vue(),
// tailwindcss() // v4用
],
})
tailwind
設定例
application.css
をapplication.js
からimportすることで利用される。
import defaultTheme from 'tailwindcss/defaultTheme';
import forms from '@tailwindcss/forms';
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./app/views/**/*.{html,erb,haml,slim}', // Rails のビューファイル
'./app/helpers/**/*.rb', // helper 内でHTMLタグを返すことがある場合
'./app/frontend/js/**/*.{js,ts,jsx,tsx,vue}', // Viteで読み込むフロントエンドコード
'./app/frontend/css/**/*.{css,sass}', // Viteで読み込むフロントエンドコード
'./app/components/**/*.{erb,html,rb}', // ViewComponent
],
theme: {
extend: {
fontFamily: {
sans: ['Figtree', ...defaultTheme.fontFamily.sans],
},
},
},
plugins: [forms],
}
html.erb
画面から利用する方法も一応記載します。
application.html.erb
CSSは開発時に JS で import していれば削除可能。ただし本番環境で vite build 時の CSS 出力に依存している場合は残す必要がある
<%# viteサーバー起動時にvite_client_tagの箇所にscriptタグが出力される %>
<!-- ここにvite_client_tag -->
<%= vite_client_tag %>
<%# 共通のCSS %>
<%= vite_stylesheet_tag 'css/application' %>
<%# ページ固有のCSS %>
<%= yield :stylesheet %>
<%# 共通のJS %>
<%= vite_javascript_tag 'js/application' %>
<%# ページ固有のJS %>
<%= yield :scripts %>
個別のhtml.erb
<% content_for :scripts do %>
<!-- 個別のjsを出力 -->
<%= vite_javascript_tag 'js/samples/index' %>
<% end %>
動作確認
bin/vite dev
# 以下のようなログが出力される
# Local+Network両方出ていればOK
appuser@0663c0951aeb:/app/project$ bin/vite dev
VITE v5.4.19 ready in 248 ms
➜ Local: http://localhost:3036/vite-dev/
➜ Network: http://172.18.0.3:3036/vite-dev/
# NGパターン
# Networkが表示されないとホットリロードされない。
続いてビルドも試す
bin/vite build
# 以下のようなログが出力される
appuser@5cf2c67b9d69:/app/project$ bin/vite build
Building with Vite ⚡
vite v5.4.19 building for development...
transforming...
✓ 2 modules transformed.
rendering chunks...
computing gzip size...
../../public/vite-dev/.vite/manifest-assets.json 0.13 kB │ gzip: 0.11 kB
../../public/vite-dev/assets/Test-m7WPhM6X.vue 0.29 kB
../../public/vite-dev/.vite/manifest.json 0.82 kB │ gzip: 0.27 kB
../../public/vite-dev/assets/application-BLDWVu3h.css 13.32 kB │ gzip: 3.10 kB
../../public/vite-dev/assets/index-l0sNRNKZ.js 0.00 kB │ gzip: 0.02 kB
../../public/vite-dev/assets/application-D0qSg-6n.js 0.06 kB │ gzip: 0.07 kB
../../public/vite-dev/assets/index-ufHPCSAQ.js 170.64 kB │ gzip: 63.84 kB
✓ built in 1.84s
Build with Vite complete: /app/project/public/vite-dev
# NGパターン
# 以下のログが出たらjsやvue側に問題がある。ログを確認して対処する
Build with Vite failed! ❌
続いてbin/vite devをし、viteサーバーを立ち上げた状態でブラウザから画面にアクセスする。
コンソールにエラーが出ておらず、ネットワークで各種js,vueが読み込まれていることを確認する。
コンソールには以下のように出力される
[vite] connected.
html.erb,js,vue,cssを書き換えてみてホットリロードされていればOK!
manifest.json
以下のようなファイルが作成される。
bin/vite build実行時にも確認はできるが、ファイルで確認する際にみる。
{
"Test.vue": {
"file": "assets/Test-m7WPhM6X.vue",
"src": "Test.vue"
},
"css/application.css": {
"file": "assets/application-BLDWVu3h.css",
"src": "css/application.css",
"isEntry": true
},
"css/samples/index.css": {
"file": "assets/index-l0sNRNKZ.js",
"name": "css/samples/index.css",
"src": "css/samples/index.css",
"isEntry": true
},
"js/application.js": {
"file": "assets/application-D0qSg-6n.js",
"name": "js/application.js",
"src": "js/application.js",
"isEntry": true,
"css": [
"assets/application-BLDWVu3h.css"
]
},
"js/samples/index.js": {
"file": "assets/index-BQjj5Zxf.js",
"name": "js/samples/index.js",
"src": "js/samples/index.js",
"isEntry": true,
"imports": [
"css/samples/index.css"
]
}
}
トラブルシューティング
コンソールに出力される情報、ネットワーク情報、ログ(tail -f log/development.log)に出力される情報は重点的に確認をします。
特に、F12のコンソールと要素の確認は必須です。
わざとエラーが起きるようにしましたが、別なエラーもあると思います。
その際は情報共有もらえると助かります。
- 環境変数が反映されない
rails cしてENVの中身をみて変わっていても実際には変わらない。
webをrestartする
# puma使用かつ環境変数レベルであれば以下でOK
docker compose exec web pkill -USR2 -f puma
# Railsの設定ファイル系を変えた際は念の為サーバーを落としてから再スタートする
docker compose stop web
docker compose start web
- bundle installでエラー
今回あまり関係ないが起こり得る問題として。
permissionエラーなら一度インストール先のディレクトリを削除の上bundle installし直すか、chown -Rで権限を変える。
→rootでbundle installし、作業用ユーザーでbundle installした時に発生する。
基本的にはrootでbundle installはNGなので作業用ユーザーに変更する
初回のrails newの時くらい。
- bin/vite dev時のエラー
- (!) Could not auto-determine entry point from rollupOptions or html files and there are no explicit optimizeDeps.include patterns. Skipping dependency pre-bundling.
ビルド対象のファイルがエントリーポイントにいないという警告。
config/vite.json
のsourceCodeDir
、additionalEntrypoints
あたりを重点的に確認する。
bin/vite build
時のエラーで、ビルド対象がいない場合も同様の対応をする。
-
@vite/client
が生成されない
個人的に最もハマったポイント
本来、application.html.erb
にて、<%= vite_client_tag %>
をすると以下のタグが出力される。
<script src="/vite-dev/@vite/client" crossorigin="anonymous" type="module"></script>
それが生成されず、コンソールにもエラーが起きない現象が起きる。
bin/vite devをした際に、
Local: http://localhost:3036/vite-dev/
これのみだと起こる模様。
原因として、config/vite.json
とvite.config.ts
の設定に不整合があると発生する。
他の原因もありそうだが確定なのはこれ。
私もこれが起きて、一回環境をリセットしてやり直したレベルで原因がわからなかった。
なお、.env
に以下の環境変数が定義されている場合、 vite.config.ts
よりも優先される(Vite Ruby内部の仕様)
- VITE_RUBY_HOST
- VITE_RUBY_PORT
この設定があるせいで不整合が起きていた。
多分おそらく確認はしていないが、.envに記載がなく、compose.yamlにも設定がなければ素直にvite関連の設定変更箇所の設定が使われると思う。
- hmrされない
監視対象になっていない可能性があるので、bin/vite buildした時のパスに含まれているか(manifest.json)を確認する。
含まれていなければconfig/vite.json
を確認する。
問題なければブラウザ側のコンソールを確認し、@vite/clientがあるか等を確認する。
- wsのサーバーへの接続ができない
ws://localhost:3036/vite-dev/?token=Tlqw8-MMN3ud のサーバーへの接続を確立できませんでした。
とでる。
config/vite.json
とvite.config.ts
の設定の不整合があると発生する。
クロスオリジン系の問題が出ている場合は、ホストに0.0.0.0を設定してしまう。
修正後、bin/vite devの実行コンソールに
➜ press h + enter to show help
1:52:40 PM [vite] page reload /app/project/app/views/layouts/application.html.erb
1:53:28 PM [vite] page reload /app/project/app/views/layouts/application.html.erb (x2)
1:55:36 PM [vite] page reload js/samples/index.js
このようなログが出るようになればOK。
- パスの参照エラー
MIME タイプ (“”) が許可されていないため、“http://localhost:3000/vite-dev/@fs/app/project/application.js” からのモジュールの読み込みがブロックされました。
のようなエラーが出る。
vite_javascript_tagなどにmanifest.jsonに存在しないパスを指定した場合発生する
パスを確認の上修正をする。
- ViteRuby::MissingEntrypointError
ブラウザアクセス時、ViteRuby::MissingEntrypointErrorが発生する。
Vite Ruby can't find js/samples/index.js in the manifests.
のように原因が表示されているので対応する。
- Vueがbuildされない
vite.config.ts
のresolve
のailias
にvueが抜けていると発生する。
設定を追加する。
補足
- manifest.jsonについて
/project/public/vite-dev/.vite
に生成される。
github
以上。