Closed5

HTML PrefetchでMPAでInstantClickのようなものを実装する

webuilder240webuilder240

発端

HotwiredのTurboでInstant Clickっぽいものを実現するPRが作成され、
取り込まれそうな気配でちょっと話題に。
https://github.com/hotwired/turbo/pull/1101/files

InstantClick

かなり昔からあるライブラリでmouseover とか touchstart などをきっかけにプリフェッチを行うライブラリ。

http://instantclick.io

クリックテスト

ここで、Hover -> クリックまでの時間を計測できる。
この時間を短縮するのがInstantClickの役割。
http://instantclick.io/click-test

Dev.to(forem)

このInstantClickの仕組みは、dev.toでも利用されていて、軽快なページ遷移に一役買っている。

https://developers.forem.com/frontend/instant-click

webuilder240webuilder240

InstantClickはPjaxなのでMPAでできないか?

InstantClickはPjaxなので、JSでHTMLをレンダリングし直している。
Turboもそうだけど、厳密にはMPAではないような気がするのでTurboやInstantClickを使わないMPAでも同じような事が実現できないか?というのが今回の検証のきっかけ。

Prefetch属性

<link rel="prefetch" href="https://www.example.com/solutions" />

このPrefetch属性を使うことで、リンク先のprefetch、つまり先読みができる。
このPrefetchをmouseoverなどのイベントを拾って、先読みができれば実現できそう。

https://developer.mozilla.org/ja/docs/Glossary/Prefetch#リンクのプリフェッチ

ブラウザの対応状況

結構昔からある印象だけど、Safari、iOS Safariでは使えないらしい。

https://caniuse.com/?search=prefetch

webuilder240webuilder240

Prefetch属性を使って実際にプリフェッチを行う

簡単なサンプルで、HTMLクラスにprefetchがついているaタグのリンク先をprefetchするコード。
ホバーしたリンク先のlinkタグをdocument.bodyに挿入する。
instantclickはtouchstartだったりでプリフェッチをしているけど割愛。

function preloadLink(event) {
    const href = event.currentTarget.getAttribute('href');
    const link = document.createElement('link');
    link.rel = 'prefetch';
    link.href = href;
    document.head.appendChild(link);
}
const links = document.querySelectorAll('a.prefetch');
links.forEach(link => {
    link.addEventListener('mouseover', prefetchLink);
    link.addEventListener('touchstart', prefetchLink);
});

挙動

ざっくりChromeで動かした。振る舞い的にはプリフェッチしてるときにデータを読み込んでも引き継がれ、
例えば2秒かかるページを、プリフェッチして1秒後にアクセスしたら残り1秒読み込んだ段階でページが表示された。

⚠注意点

Cache-Control: no-cache, privateとかだとキャッシュされないらしい。
ブラウザによってキャッシュポリシーが異なるなどの差異については調べていない。
Railsで取り込まれそうなPRについてはキャッシュの保持期間が10秒程度なので、そこに合わせてCache-Controlを設定しておく。

class PagesController < ApplicationController
  def show
    response.headers["Cache-Control"] = "public, max-age=10"
    # その他のアクションのロジック
  end
end

まとめ

  • 古き良きMPAでどうしても早くしないといけないみたいな案件が有るときに奥の手として使うのはあり。
    • prefetchを使って確かにチューニングはできるけど余計なリクエストをしているような感じもするので、絶対に使うものとかではない。
  • Safariで使えないのは素直に諦めて、プログレッシブエンハンスメントの考えでやるほうが幸せになれる。
  • prefetchについては昔からあったけど、こうやってちゃんと向き合うことで面白さを感じた。
このスクラップは3ヶ月前にクローズされました