開発本部 データウェアハウス開発部 医療機関基盤グループでシステム開発をしている堀です。
Rails7で標準となったimportmapですが、恥ずかしながら今更存在を知って自分のJavaScript(以下JSと呼称)関連の知識が古くなってることに気付かされました。おかげで知識のアップデートする機会にもなったので、知識の整理の意味も含めて知見を共有させていただきます。
importmapとは
importmapはJSモジュールを管理する仕組みで、HTTP/2による通信の多重化によってユーザーのロードによる待ち時間を低減します。必要なJSファイルだけを個別にロードできるため、デプロイ時のバンドル作業(複数のJSのソースを一つにまとめる)が省けてシンプルな構成が可能となりました。なおChromeなどのモダンブラウザでは対応されてますが、古いブラウザでは未対応の場合もあり注意が必要です。
【importmapが記載されたHTMLヘッダのサンプル】
<html> <head> <script type="importmap" data-turbo-track="reload">{ "imports": { "application": "/assets/application-XXXXXXXXXXXXX.js", "@hotwired/turbo-rails": "/assets/turbo.min-XXXXXXXXXX.js", "@hotwired/stimulus": "/assets/stimulus.min-XXXXXXXXX.js", "@hotwired/stimulus-loading": "/assets/stimulus-loading-XXXXXXXX.js", "controllers/application": "/assets/controllers/application-XXXXXXX.js", "controllers": "/assets/controllers/index-XXXXXXXXXX.js", } }</script> </head> </html>
- scriptタグにtypeで”importmap”を指定
- JSON形式で、モジュール名と対応するソースファイルのパスが記述されている
なおRailsでimportmapを導入するgemは importmap-rails になります。
gem "importmap-rails"
importmapでjqueryを呼び出すサンプル
importmapで登録したモジュールを別のJSのソースからimportするサンプルを試してみます。
【ファイルツリー】(主要箇所の抜粋)
app/ ├── controllers/ │ └── sample_controller.rb ├── javascript/ │ └── controllers/ // importmap-railsにより生成されたディレクトリ │ └── jquery_sample.js // サンプルページのJSファイル └── views/ ├── layouts/ │ └── application.html.erb └── sample/ └── index.html.erb // サンプルページのERB config/importmap.rb // importmap-railsにより生成されたファイル vendor/ └── javascript/ └── jquery.js // jqueryモジュール bin/ └── importmap // importmapのコマンドラインツール
app/javascript/controllers/
ディレクトリ- ここに自作のJSファイルを保存すると自動でインポートされる
vendor/javascript/
ディレクトリ- 利用するESモジュールのJSファイルを保存する
bin/importmap
のpinコマンドでダウンロードされたファイルが保存される(後述)
【実装したプログラム】
追加および編集したソースは以下になります。(なおsample_controller.rbおよびroutes.rbは割愛しています。)
app/views/layout/applicatin.html.erb
<!DOCTYPE html> <html> <head> <%= javascript_importmap_tags %> <%= yield(:sample_content) %> </head>
- 4行目の
javascript_importmap_tags
はimportmap-railsにより挿入されたもの- importmapのJSONを指定したscriptタグが出力される
- 5行目の
yield
を追加- sample_contentエイリアスをyieldする
app/views/sample/index.html.erb
- サンプルページでのみJSを呼び出すように以下を記述
<% content_for :sample_content do %> <%= javascript_import_module_tag "controllers/jquery_sample" %> <% end %>
- index.html.erbの先頭に上記のソースを追記
- 1行目のcontent_forがapplication.html.erbでyeildした sample_contentエイリアスに対応
- 2行目でjavascript/controllersディレクトリに保存したjquery_sample.jsを呼び出し
app/javascript/controllers/jquery_sample.js
- サンプルページで呼び出されるJSファイル
- H1タグにテキストを表示するだけの簡易なもの
import $ from "jquery"; $(function () { $("h1").text("Hello from jQuery!"); });
config/importmap.rb
- コンソールで
bin/importmap
のpinコマンドを実行
$ bin/importmap pin jquery Pinning "jquery" to vendor/javascript/jquery.js via download from https://ga.jspm.io/npm:jquery@3.7.1/dist/jquery.js
- 以下が追記される
pin "jquery" # @3.7.1
vendor/javascript/jquery.js
bin/importmap
のpinコマンドを実行した際にダウンロードされる- ダウンロードされるjquery.jsはjQueryのサイトからダウンロードされるものとは別物
- ダウンロードファイルはJSPMサーバーから提供されており、ESモジュールとして使えるようになっている
- pinコマンドがJSPMサーバーがメンテ中で失敗することもある(その際は慌てずに時間を空ける)
以上で以下のページが表示されたら成功です。
所感
理解して使ってみると、JSをモジュール単位に切り分けて管理できる仕組みが用意されており便利に思います。またimportmapは読み込むページに応じて必要なモジュールのみを非同期で読み込んでくれるわけですから画面のロード時間の短縮が期待できます。さらにRailsアプリのデプロイの仕組みを作るうえでJSファイルのバンドルを考慮しなくても良いのは有り難いです。実行環境にデプロイする際には悩まされてきたのでこのストレスから解消されるなら嬉しい!
さらに最近流行りの某AIに「Railsのimportmapの利用方法」を聞いた内容を抜粋
使いどころ:
・小〜中規模のアプリでフロントエンドにそれほど複雑な処理をしない場合
・ビルドステップを避けて、できるだけシンプルにRailsだけで完結させたいとき
向いていないケース:
・ReactやVueなどを使って、フロントエンドが複雑な場合
・大規模アプリでJSの依存管理やトランスパイルが必要なとき
使いどころとされた項目は私が良い感触を得た通りです。逆に向いてないケースですが、たしかにReactで構築した既存のプロダクトを置き換えることを考えるとそんなに簡単でないことが色々思い当たります。フロントエンドにさほどの機能を持たせてないならいいですが、今後フロントエンドでリッチなUIを実現しようとしたらどこかで問題が出てくるのかもしれません。
まとめ
今回は、最初importmap-railsの仕組みを知らないまま実装したらちょっとハマったので調べることになりました。いくら経験を積んでも普段からのキャッチアップと勉強は大切ですね。。Rails7ではHotwireも採用されていますし、新しい技術にもビビらずに挑む姿勢は持ち続けたいなと改めて思いました。
最後までお読みいただきありがとうございました!
最後に
JMDCでは、ヘルスケア領域の課題解決に一緒に取り組んでいただける方を積極採用中です! フロントエンド /バックエンド/ データベースエンジニア等、様々なポジションで募集をしています。 詳細は下記の募集一覧からご確認ください。 hrmos.co
まずはカジュアルにJMDCメンバーと話してみたい/経験が活かせそうなポジションの話を聞いてみたい等ございましたら、下記よりエントリーいただけますと幸いです。 hrmos.co
★最新記事のお知らせはぜひ X(Twitter)、またはBlueskyをご覧ください! Tweets by jmdc_tech twitter.com