つい最近、Railsで自作したブログを公開しましたが、運用するにあたり気になっていたことがありました。
それは、投稿(編集)時におけるメディアライブラリの読み込みに関するものです。
自作ブログでは画像投稿(サムネイル画像の設定含む)もできるよう、新規作成(編集)ページ内にWordPress風のメディアライブラリを作ってみたのですが、
アップロード済みの画像が増えれば増えるほど、新規作成(編集)ページを開くときのページ読み込み時間(正確にはメディアライブラリの全画像の読み込み時間)がえげつないことになってしまいます。
(僕の自作ブログでは画像を大量に使うので、画像をアップロードしまくったおかげで初回読み込み時間が遅くなっていることに気づいたw)
そこで、遅延読み込み(無限スクロール)を実装することで、新規作成(編集)ページを開いたときの初回読み込み時間を改善しようと思った次第です。
当記事では、Rails7で自作したブログに遅延読み込みを実装する流れをご紹介します。
開発環境
- Ruby 3.1.2
- Ruby on Rails 7.0.4.3
- Bootstrap 5.2.3
- M1 Macbook Air 2020
- mac OS Monterey (ver. 12.4)
出来上がりイメージ
以下のように、メディアライブラリを開いて下にスクロールするたびに遅延読み込みさせるようにします。
表示する画像サイズにもよりますが、僕の場合は30枚ずつ読み込むよう設定しました。
それでは、具体的な実装方法についてみていきましょう。
遅延読み込み(無限スクロール)を実装する方法
今回ご紹介する遅延読み込み(無限スクロール)は、 Rails7 の Turbo Frames による Lazy Loading とページネーション用Gemである「kaminari」を用いて実装しています。
以上を含めて、下記の前提条件のもと話を進めていきます。
- Rails7 でアプリ作成
- kaminari を導入する
- 記事投稿機能の作成済み(Postモデル)
- 画像ライブラリ作成済み(active_storage)
画像ライブラリの作成方法については、またいずれ記事にする予定です(気が向いたらw)。
それでは参ります。
ページネーション(kaminari)を導入する
Gemfileに「kaminari」を追記します。
gem "kaminari"
$ bundle install
してGemをインストールします。
コントローラーの編集
アップロード済みの画像をインスタンス変数@images
として定義します。
@images = current_user.images.page(params[:page]).per(30).order(created_at: :desc)
ここで、page(params[:page])
はkaminariのメソッドで、per(30)
とすることで@images
を30枚ずつ分割して表示するようにします。
(50枚ずつ遅延読み込みしたい場合はper(50)とすればOK)
定義したインスタンス変数@images
はnew
とedit
で用いるので、posts_controller.rb
に以下のように記述します。
class PostsController < ApplicationController
before_action :set_images, only: %i[ new edit ]
def new
end
def edit
end
・・・(省略)・・・
private
def set_images
if current_user.images.exists?
@images = current_user.images.page(params[:page]).per(30).order(created_at: :desc)
end
end
・・・(省略)・・・
end
turbo_frame_tagを用いて無限スクロールを実装する
最後に、新規・編集用の共通フォームビュー_form.html.erb
内の、メディアライブラリを表示させたい箇所に以下のように記述します。
/** アップロード済みの画像をプレビュー(無限スクロール) **/
<% if current_user.images.exists? %>
<%= turbo_frame_tag "images-page-#{@images.current_page}" do %>
<% @images.each do |image| %>
<div class="image-box d-inline-flex justify-content-center mx-1 mb-3" data-action="click->images#selectedImageBox">
<%= image_tag(image, class: "mx-auto", id: image.blob_id) %>
</div>
<% end %>
<%= turbo_frame_tag "images-page-#{@images.next_page}", loading: :lazy, src: path_to_next_page(@images) %>
<% end %>
<% end %>
遅延読み込みを行うためにはturbo_frame_tag
でsrc
オプションを使います。
また、以下の3つはkaminariのメソッドになります。
@images.current_page # 今のページ数を返す(例: `0`)
@imagess.next_page # 次のページ数を返す(例: `1`)
path_to_next_page(@images) # 次のページのpathを返す(例: `/images1/`)
具体的な処理の流れを説明すると、
まず以下の<turbo-frame "images-page-2">
まで画面をスクロールすると、遅延読み込みにより/images?page=2
へTurbo Frameリクエストが実行されます。
// <%= turbo_frame_tag "imagess-page-#{@images.next_page}", loading: :lazy, src: path_to_next_page(@images) 部分 %>
<%= turbo_frame_tag "images-page-2", loading: :lazy, src: "/images?page=2" %>
すると以下の<turbo-frame>
を含むHTMLがレスポンスされて、↑の<turbo-frame "images-page-2">
を置換します。
/** アップロード済みの画像をプレビュー(無限スクロール) **/
<%= turbo_frame_tag "images-page-2" do %>
<% @images.each do |image| %>
<div class="image-box d-inline-flex justify-content-center mx-1 mb-3" data-action="click->images#selectedImageBox">
<%= image_tag(image, class: "mx-auto", id: image.blob_id) %>
</div>
<% end %>
<%= turbo_frame_tag "images-page-3", loading: :lazy, src: "/images?page=3" %>
<% end %>
そして次は<turbo-frame "images-page-3">
のところまでスクロールすると、/images?page=3
へTurbo Frameリクエストされ、、
といった具合に、これが最終ページに行くまで無限に繰り返されます。
これで遅延読み込み(無限スクロール)の実装は完了です。
コメント