前書き
こちらはAteamFinergyのアドベントカレンダー3日目の記事になります。
内容としてはInstantClickの説明が大半を締めており
目的としては、InstantClickの認知を増やす事です
InstantClickとは
Webサイトを劇的に高速化するJavaScriptライブラリ」
-- from instantclick.io
JavaScriptのライブラリにInstantClickというものがあり、Webサイトに対して適用すると高速になる...というものがあり
そのInstantClickを実際に使用している例を上げると「dev.to」や「FC2爆速テンプレート」などで使用されています
特にdev.toについては阿部寛のホームページと比較している記事が多く見られ、ご存知のかたは多いのではないでしょうか
InstantClickではpjax(pushState + ajax)を使用して高速化を図っており
リンク先のHTMLをpreloadしておき、リンク先へ遷移するタイミングに「head
, body
, title
」を入れ替え、ユーザーに対してとても素早いページ遷移の体験を提供している様です。
pjaxは、ajaxを介してサーバーからHTMLを取得し、ページ上のコンテナ要素のコンテンツを読み込まれたHTMLに置き換える
-- from jquery-pjax
ブラウザ上のページを変更することはないがInstantClickによってHTML要素が変更され、結果SPAに近しい挙動となります
仕組みについて
リンク先のHTMLをpreloadすると言っても、Webサイトには無数のリンクがあるため、「どのタイミング」で「どのくらい」取得する事が大事になるはずです
InstantClickのデフォルトの設定では、遷移先のリンクに対して「マウスを重ねた瞬間」に「リンク先の要素」を取得するようです
実際にユーザーがリンクにマウスオーバーしてからクリックするまでの間に200~300ms経過するため、その間にリンク先をpreloadしておく事でスムーズな遷移を提供しています
実際にこちらで、「MouseOverイベントが走ってからクリックするまでの秒数」と「MouseDownイベントが走ってからクリックするまでの秒数」を確認する事ができるので一度見てみると良さそうです
また、モバイルについてはmouseoverではなく、touchstart
イベントが対象となっているようです
実際にこちらを見ていただくと検証画面のInitiatorの箇所がInstantClickによって遷移先のHTMLを取得している事が確認できます
FC2爆速テンプレートdemoページより
導入方法
導入の仕方についてはとても簡単で
「InstantClickをダウンロード」「ファイルを任意の箇所に配置」
「読み込みのscript
タグと初期化のscript
タグを記述」の3ステップで出来ます。
- こちらからProduction Versionをダウンロード
- InstantClickの読み込みをページの下部で実施
<script src="instantclick.min.js" data-no-instant></script>
<script data-no-instant>InstantClick.init();</script>
</body>
注意点
InstantClickを使用する際にはいくつか注意する事があります
JSで取得する事ができるイベントについて
InstantClickはpjaxの技術でHTMLを入れて表示を切り替えるので
DomContentLoaded
jQueryのready()
などのイベントが発火しなくなります。
そのため、InstantClickではいくつかの既存のライフサイクルをフックする事で解決します。
解決法: 既存ライフサイクルのフック
InstantClickでは下記の4つが提供されており、それぞれ元のイベントの代替になります。
- change
-
DomContentLoaded
の置き換えとなり、InstantClickが読み込まれていない最初の遷移時にも発火
-
- fetch
- マウスオーバーした時に、遷移先のHTMLをpreloadするタイミングで発火
- recieve
- preloadが完了したタイミングで発火
- await
- ユーザーがクリックしたがpreloadが完了していない待ち時間の間に発火
導入した際には
上記の様なイベントに既存のイベントが置き換わるため注意が必要です
サーバーへのリクエスト数
デフォルトの設定のままだと、マウスオーバーするだけでサーバーへリクエストを送る事になり、場合によってはサーバーへの負荷が高まることやブラウザ側では使用しないムダなHTMLを取得する事になります
そのため、InstantClickでは2つの解決策を用意しています。
- マウスダウンイベントへの変更
- マウスオーバー時のリクエスト遅延
解決法①: マウスオーバーからマウスダウンへの変更
InstantClickを初期化するタイミングでmouseover
イベントからmousedown
イベントに変更する事ができます。
Webサイトによっては、マウスオーバー程効果を得る事が出来ない可能性はありますが
それでもマウスダウンからクリックするまでに80~120ms程度かかるのでその間に普段のページ遷移より体感としては早く行えます。
計測はこちら
初期化の記述にmousedown
を引数として渡してあげるだけで変更できるようです
<script data-no-instant>InstantClick.init('mousedown');</script>
実際に下記のコードを確認すると
初期化のタイミングでこの引数を渡すとdocument
に対してmouseover
かmousedown
かのどちらのイベントをリッスンするか変更する事が確認できます
https://github.com/dieulot/instantclick/blob/master/src/instantclick.js#L804
https://github.com/dieulot/instantclick/blob/master/src/instantclick.js#L830
解決法②: マウスオーバー時のpreloadリクエストの遅延
デフォルトとマウスダウンへ変更した時とのちょうど間にある選択肢として
「マウスオーバー時にリクエストは送るけれど、カーソルが触れただけの時とかは無しにしたい」というケースでは
マウスオーバー時のリクエストを遅延させる事で解決できます
こちらを適用するには、マウスダウンへ変更した時と同様に、初期化のタイミングで何ms遅延させたいかを渡してあげるだけで大丈夫です
下記の例は75ms遅延させる時
<script data-no-instant>InstantClick.init(75);</script>
実際のコードを確認すると
初期化で渡された引数が保存され、setTimeout
によって設定した時間分遅れてpreloadを実行するようです。
https://github.com/dieulot/instantclick/blob/master/src/instantclick.js#L454
ただ何も渡さない場合は65msにpreloadするようです。
https://github.com/dieulot/instantclick/blob/master/src/instantclick.js#L30
リンク毎のpreload制御
初期設定だと一部を除いてほとんどのリンクがpreloadの対象となります。
具体的には下記の様なリンクがpreloadの対象外となります
-
target
またはdownload
属性を所持している - 現在のWebサイトとは別の
domain
またはprotocol
- 同じページへのanchor
<a href='https://qiita.com/' target='_blank'>not preload</a>
<a href='https://qiita.com/' download>not preload</a>
<a href='https://github.com/'>not preload</a>
<a href='http://qiita.com/'>not preload</a>
<a href='#InstantClickとは'>not preload</a>
ただ、使用する際に「ほとんどのリンクは適用したいけれど、このリンクはpreloadしたくない」などのケースが出てくる時があるでしょう
その時は「このリンク適用しない」と設定が可能な、ブラックリスト機能があります
解決法: ブラックリスト・ホワイトリスト機能
設定方法は簡単で、preloadして欲しくないリンクに対してdata-no-instant
属性を付与するだけです。
<a href='https://qiita.com/' data-no-instant>not preload</a>
また、該当箇所が複数あった場合については親の要素にdata-no-instant
属性を付与する事ですべての子要素に対してpreloadが走らなくなります。
<div data-no-instant>
<a href='https://qiita.com/'>not preload</a>
<a href='https://qiita.com/'>not preload</a>
<a href='https://qiita.com/'>not preload</a>
</div>
複雑化した際の方法としては
- 親要素に
data-no-instant
を記述して子要素をまとめてブラックリスト追加 - その子要素の任意のものだけ
data-instant
属性を追加する
とする事でホワイトリストに追加し簡易的な対応ができます
<div data-no-instant>
<a href='https://qiita.com/'>not preload</a>
<a href='https://qiita.com/' data-instant>preload</a>
</div>
まとめ
- InstantClickはpjaxで遷移先の値をpreloadし、入れ替える
- 遷移先を取得するのはmouseoverイベントのタイミングなのでユーザーに素晴らしい体験を提供できる
- サーバーへの負荷が高まる可能性がある
- javascriptの元々のイベントを取得できなくなる
いかがだったでしょうか、InstantClick自体が少し古い技術かと思いますが「dev.toめっちゃはやいやんけ!」という仕組みの片鱗を知って、興味を持っていただけたら幸いです
引用元
http://instantclick.io/
https://developer.mozilla.org/ja/
https://dev.to/
https://fc2information.blog.fc2.com/
https://github.com/defunkt/jquery-pjax