11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PORTAdvent Calendar 2017

Day 2

部分テンプレートを遅延ロードさせるGem作った

Last updated at Posted at 2017-12-27

概要

簡単に部分テンプレートを遅延ロードさせる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.rbmount_step_renderの記述を追加すれば、必要なルーティングが作成されます。

Rails.application.routes.draw do
  〜〜〜
  mount_step_render
  〜〜〜

これで準備は完了です。

使用例

後は、renderメソッドと同じような感覚で使用できます。以下の例では、Articleというモデルのインスタンスを引数に渡しています。Aが通常のパターンで、BがGemを利用した遅延読み込みのパターンです。

A. renderの場合

app/views/articles/index.html.erb
<% @articles = Article.find(10) %>
<%= render 'article', article: @article %>
app/views/articles/_article.html.erb
<h2><%= article.title %></h2>
〜〜〜
〜〜〜

B. step_renderでの遅延ロード

app/views/articles/index.html.erb
<% @articles = Article.find(10) %>
<%= step_render 'articles/article', article: @article %>
app/views/articles/_article.html.erb
<h2><%= article.title %></h2>
〜〜〜
〜〜〜

step_renderでの遅延ロードを行う際の違いは、renderstep_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_renderGemでは、2083文字を超える場合に、StepRender::RequestURITooLargeという独自例外が発生します。
試した感じだと、20~30個くらいのARインスタンスなら引数として渡せます。以下は実行可能です。

<%= step_render 'partial', articles: Article.limit(20).to_a %>

まとめ

このGemの使用用途としては以下のようなものが考えられます。

  • ページ読み込みが重い部分に適用
  • fragment cacheの内側で一部書き換えたい場合
  • 無限スクロール

いずれの用途でも最小限のコード変更で実現できますが、内部的には少し荒っぽいことをしてるので動作は少し不安です・・・

最後になりますが、PORT株式会社では自社サービスを支えてくれる優秀なRubyエンジニアを募集しています(Rubyエンジニア以外も)。
もくもく会も行なっていますので、ぜひ一緒にもくもくしましょう!
PORTもくもく会

11
4
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
11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?