SPIDERPLUS Tech Blog

建設SaaS「スパイダープラス」のエンジニアとデザイナーのブログ

既存プロジェクトをHMR対応した話(Vite利用)

はじめに

開発一部のわにわに🐊です。

私達のチームは、3Dの設計図面をiPadやブラウザ上で閲覧する機能を持つシステムを開発しています。

今日は既存のプロジェクトにHMRを導入した時のことを書きます。

細かい設定は、システム構成が異なるとあまり役に立たないので、設定の考え方を中心に書いていきます。
大雑把な環境: viteとReactを使ったシステムで、開発はSSH接続が可能なリモートサーバー上で行なっています。

対象読者

  • viteを利用している既存開発プロジェクトをなんとかHMR化したい
    特に既存プロジェクトに後からviteを導入した場合はHMR対応できていなかったりするので
  • Docker設定は、見よう見まね程度だけど自分でできる
  • linuxのコマンドがそこそこ使える
  • Apacheとかリバースプロキシ等の用語が少しはわかる

導入経緯

HMR(Hot Module Replacement)とは、webページ全体をリロードすることなく、cssやjsなどのモジュールの変更をブラウザ上のwebアプリに反映させる技術・環境のことです。
上にも書いたように開発しているシステムは3Dの設計図面を扱うので、ページリロード時のアプリ初期化や3Dモデルの読み込みにそれなりに時間がかかってしまいます。
特にUI調整を行なっていると「コードを少し変えて確認」という作業が繰り返し発生するので、リロードの微妙な待ち時間で作業効率が悪くなり、かなりのストレスになっていたことからHMRを導入することにしました。

結果動画

HMR導入後の動画です。cssやjsの変更が即座に反映されているのがわかります。

開発環境について

開発プロジェクトの開発環境について説明します。


A. 1コンテナホスト上で、複数人の各個人ごとの開発環境コンテナが動いている。
B. 各個人用コンテナは、WebアプリをApacheを通して提供している。(PHPAPIとviteのフロントスクリプト)
C. 各個人用コンテナには、外部からSSHで接続可能になっている。
D. ブラウザからのweb通信は、一度コンテナホストで受け取り、個人ごとに設定されたサブドメインに応じて、各個人の開発用コンテナにリバースプロキシされる。
E. httpsSSL処理はコンテナホストのApacheで処理され、各開発コンテナにリバースプロキシされる時はhttpで送られるので、各開発コンテナ上でのSSL処理は不要となっている。

全く同じ環境という方はあまりいないでしょうが、当記事では各設定について何のために必要なのか、どうしてそのように書いているのかできるだけ説明していきますので、つまみ食い的に参考にしていただければと思います。

やったこと

viteを使ったReactプロジェクトが動いているところから始めたので、やったことはこのくらいです。

  • React HMR用のviteプラグインをインストール
  • PHP APIとフロントスクリプトのリクエストを別々のサービスに振り分ける設定
  • ブラウザ-コンテナ間 HMR用WebSocket通信の確立

React HMR用のviteプラグインをインストール

なお、当記事で利用したviteのバージョンは`vite/5.2.12 linux-x64 node-v20.12.0`です。

Viteビルトインサーバーの起動について

HMRを動かすには、ブラウザからのリクエストをviteビルトインサーバーで処理する必要があります。

npx vite dev -d --config vite.config.js

このようにdevオプション付きでviteを実行するとport5173でビルトインサーバーが起動します。

PHP APIとフロントスクリプトのリクエストを別々のサービスに振り分ける設定

いままで、PHPAPIもフロントのjs,css配布もともにApacheで行なっていました。
しかし、viteのHMRを使うには、viteの開発サーバーを使う必要があるので、PHPAPIへのリクエストをApacheに流し、フロントjs,cssへのリクエストをviteの開発サーバーに流すようにする必要があります。

ここで選択肢が2つありますが、

  1. Apacheのリバースプロキシ機能で、js,cssへのリクエストをviteの開発サーバーに流す
  2. viteの開発サーバーのリバースプロキシ機能で、PHP APIへのリクエストをApacheに流す

以下の理由から、2を選択しました。

  • viteのリバースプロキシ機能は、まさにこのユースケースのために作られている。
  • Apache設定を変更せずにvite設定ファイル変更で済むので、設定変更をgit管理しやすい。
  • `vite dev`を -d オプションでデバッグログ出力すると、プロキシ動作も一緒に出るのでトラブル追跡がしやすい。

そのためには、以下の設定が必要です。

  • ブラウザからのリクエストをviteが受け取れるように、docker composeのポートマッピングを変更する
  • viteの設定ファイルを作成し、Apacheへのリバースプロキシ設定を追加する
Dockerポートマッピングの変更

docker-compose.ymlの設定を変更します。
今まで、コンテナホストのApacheから各コンテナにブラウザリクエストがリバースプロキシされたものをApacheポートで受け取っていた設定をviteサーバーのポートで受け取るように変更します。

`yml
# 変更前
ports:
    - "8043:80"

# 変更後
ports:
    - "8043:5173"
  •  80は、Apacheのデフォルトポート
  • 5173は、viteのデフォルトポート
  • 8043は、コンテナホスト側から見た、Apacheのリバースプロキシターゲットポート
viteの設定ファイルを編集する

vite.config.jsの抜粋です。

json
import react from '@vitejs/plugin-react';

export default defineConfig({
    server: {
        host: '0.0.0.0',
        port: 5173,
        hmr: {
            protocol: 'ws',
            host: 'localhost',
            port: 5174,
            clientPort: 5174,
        },
        proxy: {
            '^.*/api/.*': 'http://localhost',
        },
    },
    plugins: [react()],
});
  • `server.port`は、viteの開発サーバーのポート番号です。
  • hmrが利用するWebSocket用ポートには、5174を指定しています。
  • proxy設定で、`^.*\/api\/.*`にマッチするリクエストを`http://localhost`にリバースプロキシします。
    • ここが、PHP APIApacheにプロキシするための設定です。
    •  `'pathのパターン': 'プロキシ先のURL'`の形式で設定します。
  •  importとpluginsの設定は、ReactコンポーネントをHMRで更新するために必要です。

ブラウザとコンテナの間でHMR用のWebSocket通信を確立する

viteのHMRは、ws通信を使って、ブラウザとviteの開発サーバー間でファイル変更を通知する仕組みです。
なので、どうにかしてブラウザとviteサーバーの間でws通信を確立する必要があります。

ここで、選択肢が3つありそうです。

  1. ブラウザで開いているページのホストに対して、https(port 443)を通してwssで通信する。
  2. 開発コンテナの特定ポートをHMRのws通信用に開放し、ブラウザから直接通信する。
  3. SSHで開発コンテナに接続し、SSHトンネルを使ってws通信を確立する。ブラウザはlocalhostに対してws通信を行う。

1.は、WebsocketをリバースプロキシするためのApache設定変更が必要なので回避しました。
2.は、やってみたのですが、閲覧しているwebページがhttpsのときは、ブラウザがws通信を拒否して、wss通信を要求してきます。vite側をwssに対応するのが面倒なので、回避しました。
3.をやってみたら、localhostに対して暗号化していないws通信をしてもブラウザに拒否されず、また通信はSSHトンネルを通して暗号化されるので、これを採用しました。

SSHトンネルを使ってWebSocket通信を確立する

SSHトンネルは慣れていないと難しそうに感じますが、みんな大好きvscodeの機能を使うとおそろしく簡単にできます。
簡単すぎて、何が起きているかわからないレベルです。

vscodeの「Remote - SSHプラグインには、ポートフォワーディング機能があります。
設定で「Auto Forward Ports」をオンにしておくとvscode内のterminalから、開発コンテナ内でポートをlistenするコマンドを実行するだけで、自動的にSSHトンネルが確立されます。

今回、viteを起動するときに、`npx vite dev -d`で起動していますが、このコマンドを"vscode内のターミナルから"実行すると、自動的にポートフォワーディングが設定され、SSHトンネルが確立します。

つまり`npx vite dev -d`と打つだけで、localhostの5174ポートが開発コンテナの5174ポートにトンネルされ、ブラウザとviteのws通信が確立されるというわけです。
(5174は上の設定ファイルサンプルでclientPortに指定した値です。)
素晴らしく簡単ですね!

ブラウザdevtoolのスクショを見ると、ws通信がlocalhost:5174に確立されているのが確認できます。

できるようになった後の注意点

  • 自分はMacを使っていて、スリープモードに入ると、SSHトンネルが切れることがありました。
    その場合は、vscodeをreloadすることで、再度SSHトンネルを確立することができます。
  • safariだとページURLがhttpsの場合は、通信先がlocalhostといえどもwss通信を要求してきます。
    chrome,edgeを使うか、VPN経由等安全な経路で全部httpにするかしてください。

終わりに

これで、上の動画のように開発することができるようになりました。
HMR導入前と比べると格段に快適です。

cssの調整も今までdevtoolでちまちま変更していたので、操作性も悪く、どこをどう変えていったのかわからなくなることも多かったのですが、vscode上で作業できるようになったことで編集しやすくなりました。
vscode上なので、cssもcopilot君がガシガシ書いてくれるのも最高ですね。

今後は快適にUI開発を進めていけそうです。
やったね。

スパイダープラスでは仲間を募集中です。
既存プロジェクトでも、みんなの開発を効率化していただけるような方は、大大大歓迎です。
ぜひ、お気軽にご連絡ください。