概要
簡単に部分テンプレートを遅延ロードさせるRailsプラグイン作った。
View template内で、render 'partial'
をstep_render 'partial'
みたいに書き換えるだけ。
なぜ作ったか
まず、部分テンプレートを遅延ロード(あるいは非同期読み込み)させる目的のGemはすでに存在します。探した限り、以下の二つが見つかりました。
しかし、これらのGemはテンプレートごとにアクションやルーティングを作成する必要があったりと、手軽にプロジェクトに組み込める感じではありませんでした(堅実な作りとも言えますが・・・)。
今回作成したGemでは、render
メソッドで部分テンプレートを呼び出すところをstep_render
に変えるだけで遅延ロードを実現させるため、扱いが非常にシンプルです。まあ、いくつか細かい制約はありますが・・・
使用方法・解説
導入
まず、普通にbundle installします。
おそらくRails 4.2以上でしか動作しません。
リポジトリはここです
https://github.com/EastResident/step_render
gem 'step_render'
次に、jsのコードを挿入します。このプラグインはlazysizesというjsのライブラリを利用しているので、lazysizesをプロジェクトに導入してないなら、application.html.erb
等に以下の記述すればCDNで読み込みます。
<head>
〜〜〜
〜〜〜
<%= import_step_render %>
〜〜〜
</head>
最後に、ルーティングを追加します。routes.rb
にmount_step_render
の記述を追加すれば、必要なルーティングが作成されます。
Rails.application.routes.draw do
〜〜〜
mount_step_render
〜〜〜
これで準備は完了です。
使用例
後は、render
メソッドと同じような感覚で使用できます。以下の例では、Article
というモデルのインスタンスを引数に渡しています。Aが通常のパターンで、BがGemを利用した遅延読み込みのパターンです。
A. renderの場合
<% @articles = Article.find(10) %>
<%= render 'article', article: @article %>
<h2><%= article.title %></h2>
〜〜〜
〜〜〜
B. step_renderでの遅延ロード
<% @articles = Article.find(10) %>
<%= step_render 'articles/article', article: @article %>
<h2><%= article.title %></h2>
〜〜〜
〜〜〜
step_renderでの遅延ロードを行う際の違いは、render
をstep_render
に置き換えることと、部分テンプレートのファイルを絶対パス("app/views"からの相対パス)で指定することのみです。部分テンプレート側の変更は必要ありませんし、actionやroutingを追加する必要はありません。
仕組みの説明
どのようにこれを実現させているかですが、まず、部分テンプレートの代わりに以下のようなdiv
タグが挿入されます。
<div class="related-articles lazyloaded" data-include="/step_render/%04%08%5B%07I%22%0Carticle%06%3A%06ET%7B%07I%22%0Carticle%06%3B%00F%7B%06I%22%11_aj_globalid%06%3B%00TI%22%1Agid%3A%2F%2Fchai%2FArticle%2F32%06%3B%00TI%22%14_aj_symbol_keys%06%3B%00T%5B%06I%22%0Carticle%06%3B%00F">loading</div>
ここで、data-include
属性の/step_render/
以下の文字列は、step_render
メソッドに渡した引数をダンプしてCGIエスケープしたものです。ActiveRecordのモデルが引数に渡された場合は、Global ID化した上でダンプされます。
この/step_render/~~~~
形式のURLにGETすることで本来のHTMLが返ってきます。
GETで渡せるURLには長さの制限があるので、これを超えるような引数を指定することはできません。
step_render
Gemでは、2083文字を超える場合に、StepRender::RequestURITooLarge
という独自例外が発生します。
試した感じだと、20~30個くらいのARインスタンスなら引数として渡せます。以下は実行可能です。
<%= step_render 'partial', articles: Article.limit(20).to_a %>
まとめ
このGemの使用用途としては以下のようなものが考えられます。
- ページ読み込みが重い部分に適用
- fragment cacheの内側で一部書き換えたい場合
- 無限スクロール
いずれの用途でも最小限のコード変更で実現できますが、内部的には少し荒っぽいことをしてるので動作は少し不安です・・・
最後になりますが、PORT株式会社では自社サービスを支えてくれる優秀なRubyエンジニアを募集しています(Rubyエンジニア以外も)。
もくもく会も行なっていますので、ぜひ一緒にもくもくしましょう!
PORTもくもく会