2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DockerでRails 8 + Viteのホットリロードが効かない時に確認すべきポイントまとめ

Posted at

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後)に以下のファイルが作成される

  1. config/vite.json
  2. vite.config.ts
  3. procfile.dev

1と2+.envの設定が必要になる。
3はbin/vite devができるようになる設定ファイル

設定の詳細と期待される結果は以下に記載する。


config/vite.json

設定例

ポイントは
sourceCodeDir をassetsを配置している箇所に設定する。
additionalEntrypoints にビルドしたいファイルを指定する。

developmenthostautoBuildの設定を確認する。
今回、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すればいいはずです。

ポイント:

  1. serverhost,portconfig/vite.jsonと合わせる。
  2. resolvealiasにvueを設定する。
  3. pluginsRubyPlugin(),vue()を設定する。

注意点:

serverhost,portは環境変数にVITE_RUBY_HOSTVITE_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.cssapplication.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時のエラー
  1. (!) 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.jsonsourceCodeDiradditionalEntrypointsあたりを重点的に確認する。
bin/vite build時のエラーで、ビルド対象がいない場合も同様の対応をする。


  1. @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.jsonvite.config.tsの設定に不整合があると発生する。
他の原因もありそうだが確定なのはこれ。
私もこれが起きて、一回環境をリセットしてやり直したレベルで原因がわからなかった。
なお、.env に以下の環境変数が定義されている場合、 vite.config.ts よりも優先される(Vite Ruby内部の仕様)

  • VITE_RUBY_HOST
  • VITE_RUBY_PORT

この設定があるせいで不整合が起きていた。
多分おそらく確認はしていないが、.envに記載がなく、compose.yamlにも設定がなければ素直にvite関連の設定変更箇所の設定が使われると思う。


  1. hmrされない

監視対象になっていない可能性があるので、bin/vite buildした時のパスに含まれているか(manifest.json)を確認する。
含まれていなければconfig/vite.jsonを確認する。

問題なければブラウザ側のコンソールを確認し、@vite/clientがあるか等を確認する。

  1. wsのサーバーへの接続ができない
ws://localhost:3036/vite-dev/?token=Tlqw8-MMN3ud のサーバーへの接続を確立できませんでした。

とでる。

config/vite.jsonvite.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。

  1. パスの参照エラー
MIME タイプ (“”) が許可されていないため、“http://localhost:3000/vite-dev/@fs/app/project/application.js” からのモジュールの読み込みがブロックされました。  

のようなエラーが出る。

vite_javascript_tagなどにmanifest.jsonに存在しないパスを指定した場合発生する
パスを確認の上修正をする。

  1. ViteRuby::MissingEntrypointError

ブラウザアクセス時、ViteRuby::MissingEntrypointErrorが発生する。

Vite Ruby can't find js/samples/index.js in the manifests.  

のように原因が表示されているので対応する。

  1. Vueがbuildされない

vite.config.tsresolveailiasにvueが抜けていると発生する。
設定を追加する。

補足

  • manifest.jsonについて
    /project/public/vite-dev/.vite に生成される。

github

以上。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?