これからpjaxを使う人に知っておいてほしいこと

147 0

この記事を書こうとした2016年の春頃にはReactだ、Vueだ、Web Componentsだ、Riotだ…と、これまでの高コストなDOM操作をする方法ではなく、ユーザーの操作に応じてインタラクションを返す際は、Virtual DOMを使ったりコンポーネントを取り替えるようにしろ、的なものが主流になってきていたのでpjax自体もう枯れた技術になるかな…と考えた為、お蔵入りにしていました。

しかし、ちょっと調べ物をした時に、最近でもまた結構記事が増えていることに気付いて、まだ興味を持っている人も多いのだ、ということと、『pjaxを使ううえで忘れてはならない大事なポイント』にまだ誰も触れていないことに気付き、改めてちゃんと書こうと思い、筆をとった次第です。

これまで約3年半に渡りpjaxを使い続けてきて、色々と蓄積されたノウハウを公開していければと思います。

第一回目の今回では、主に基礎知識となる部分のお話しをさせていただきます。
「今更基礎…?」と思われるかもしれませんが、突っ込んだ内容も書いております。ここをしっかり理解しておかなければ、実装の際いらぬところで詰まったり、無用なエラーを招くことが多いため、どうぞお付き合いください。

1.最初に、僕がpjaxを使ってきた経歴

初めてpjaxを使ったのは2015年1月のThe New York Times様の案件でのことです。

コンテンツのカテゴリーに応じて、動的にレイアウトが変化していく、というコンセプトで作成しました。
その変化もリアルタイムに見て取れるように。つまり、『(ユーザーの操作に合わせて)レイアウトがリアルタイムで変化していくサイト』があったら面白いな、と考えたのがきっかけです。 しかも、『その状態が一時的ではないもの』で。

JavaScriptやAjaxを使えば、リアルタイムでレイアウトやコンテンツを変化させるのは造作もありません。しかし、それは一時的なものでしかなく、スタートの形状は常に同じ。 ブラウザの戻る/進む/リロードを使えばスタートの形状へ強制的に戻されます。
ハッシュを使えばそれも多少解消されますが、検索には引っかかりません。(検索エンジンから見れば、「ハッシュは一時的なもの」という認識に近い)

検索からの直接流入でも、変化した状態でアクセスし、 変化した状態それぞれヒストリーが残る必要がある = 変化状態それぞれにユニークなURLが存在すれば可能(逆説的ですが…)

つまり、pjax(pushState + Ajax)を応用すればそれも可能、ということです。

それ以来、これまで複数ページある製作の案件ほぼすべてでpjaxを採用してきました。(このブログでも使っていますね)

その経験から、これからpjaxの利用を考えている製作者の方に、覚えておいていただきたいポイントと、使い方に関するtipsを紹介できればと思います。

2.pjaxって結局何がいいの?

極論するとこの2つだと考えます。

  1. 高速なページ遷移
  2. 新鮮でユニークなユーザー体験

pjaxの利点としては、必要な部分のみを取り替えるという方法ゆえにページロードに必要なデータ量を大幅に削減できます。 その結果、ページ遷移が非常に高速になり、従来型のサイトやアプリケーションの製作においては、非常に強力なツールではないかと考えています。

高速なページ遷移

Webサイトなどでは、基本header、footerはどのページでも共通になることがほとんどでしょう。場合によってsidebarがあったりなかったりすることがありませんが、その表示内容は一定であることがほとんどではないでしょうか?

そうなると、各ページ間の差異は画像でいう「#contents」の箇所のみになります。

しかし、通常のhttpリクエストにおいてはページ間を遷移する際、遷移元で読み込まれレンダリングされていたこのDOMをすべてリフレッシュして、あらためて遷移先のDOMをすべて読み込んでレンダリングします。cssやJavaScriptももう一度、一から読み込んで実行する、という処理が行われます。

pjaxでは、読み込み済みの遷移元のDOMへ、遷移先のDOMからpjaxで指定した箇所を読み込んできて置き換えるだけになります。
その為、cssやJavaSciptを改めて読み込んで一から実行しなおす…といった無駄もありません。

ページ表示を遅らせる大きな原因であるhttpリクエスト数を大きく減らせること、scriptタグによるブロッキングが起こらないのも高速な遷移の一因です。(※HTTP2環境ではhttpリクエストの最大同時読み込み可能数が大きく向上している為、リクエスト時のブロッキングは起こりにくくなっていますが)

新鮮でユニークなユーザー体験

モバイルアプリなどでページ遷移(コンテンツ切り替え)がシームレスに行われるものが多く、非常に軽快で小気味のいい操作感のものが多くなっています。

その“今っぽい”操作感と比べて、webページはどうでしょう?
今でも、今までと大差ない動作をしていないでしょうか?

実際のところ、それで特に問題はないでしょう。
しかし、とても作り込まれているサイトなのに、ページを移動する際は「いつも通り」では、むしろ違和感にも感じ、なんだかそこだけが何十年も前のまま何も変わらない方法だな…と、興ざめしてしまいます。その部分だけが、異様に古臭くも感じます。
だからこそ、ページ移動時にも拘ったインタラクションがあったほうがいい

例えばこの動画の最後にあるようなスライダー型のpjax。
一見スライダーのように見えますが、ちゃんとURLは変わっています。これはある意味、理想的な遷移の一つではないかなと考えています。

ユーザーにとっては、ページ移動していると感じにくい。これは閲覧時の心的ストレスを大きく緩和します。
個人的にではありますが、ページ遷移時の「チラつき」がとても嫌いで、あの一瞬のチラつきを見たくないために、ページ遷移を極力しない、くらい。

同時に、一つの繋がりの輪として認識されるということ。

つまりは、複数ページに渡る情報量でありながら、それらを一括りとして感じてもらえる。 これはそれぞれのコンテンツが孤立しやすいデジタルコンテンツにおいて、大きなアドバンテージではないでしょうか?

Webサイトであれば当然、どのページから閲覧するか分からない。
一連の関連情報として認知しやすく、かつ「すぐにアクセスしやすい・できる」というものは使い勝手が良く、分かりやすいものとなるでしょう。

よくあるブログの「関連記事」的なもので十分と考えられるのも、それも間違いではないでしょう。 しかし、「ベスト」ではないはずです。 ならば、よりよい手法を創造していくべきだと考えます。

まして、すべてのWebページがブログ形式ではないのですから。

3.各Pjaxプラグインの特徴

今使われている主なpjaxのプラグインは下記の3つだと思われます。(これら以外もあるのでしょうが使ったことがない為割愛)
それぞれの特徴などを個人的な感覚で紹介します。(各特徴は2018年5月現在の最新Ver.のもの)

jquery-pjax PJAX Barba.js
サイズ 37KB(v2.0.1) 229KB(v3.21.3) 41KB(※minify 16KB)(v1.0)
必要環境 jQuery依存 Nativeのみ 両方可
実装の難易度 易しい 易しい やや高い
実行速度 やや遅い 速い 速い
pjaxエリアの自由度 ×
遷移時の演出
クリック時に処理を挟む ×
プリロード × ×
Use Case
  • jQueryベースでの開発時
  • 複雑なコンテンツ切り替えを行いたいケース
  • Native環境でシンプルなpjaxを利用したい時
  • 高速な遷移を実現したいケース
  • 凝った演出を使用したいケース
  • 複雑なイベント処理を行いたいケース

これらの中ではjquery-pjaxが最も古く、Barba.jsが一番新しいもので、それぞれにこのような特徴があります。

  • jquery-pjaxはjQuery依存だけあって実行速度などが他のものより遅くなる反面、様々なカスタマイズが簡単にでき、自由度も高い(だいたいのことは頑張ればなんとかなります)。
  • PJAXはインターフェースやAPIがかなりシンプルなので、実装そのものは簡単です。ただし、ほとんどを内部的に処理している為、カスタマイズはあまりできません。
  • Barbar.jsはAPIが非常に豊富な為、nativeでの実装でも様々なカスタムが可能です。pjax可能なエリアは固定という点に難があるのと、実装は少し難易度が上がります。

製作するものの要件に応じて、それぞれを選ぶと良いでしょう。今後、主にこの3つを使用しての方法で説明していきます。

4.pjaxを使用する際の注意点

2の箇所で少し触れていましたが、『遷移元のDOMへ』『遷移先のDOMから部分的に』変更する為、遷移元と遷移先のDOM構造に差異があれば、pjaxでの遷移時と通常のリクエスト時で表示内容に違いが出てしまいます

上記のような状態でpjaxを用いるとエラーやバグを生みます。

たしかにpjax後にjsファイルやcssファイルの追加読み込みは可能です。しかし、遷移毎に追加されていくファイルのコンフリクト回避などのチェックに返って時間が生じるでしょう。はじめからすべて読み込ませる方式のほうが安全です。(※腕に自信があればこの限りではありません)

pjaxを使用した製作では、以下のことを注意してください。

  • あらかじめDOMの構造をpjaxで更新したい箇所を中心に考えた構成にしましょう。
    (ex. サイドバーの有無がページ毎にあるならば、pjaxさせるエリア内にサイドバーを配置する など)
  • 必要ファイルは最初からすべて読み込ませる。(1ファイルにまとめるのもおすすめです)

同時に、これらの点を考慮した製作になるということは、工数ばかりが増えることもあり、決まったデータをただひたすら表示するだけの静的なhtmlサイトにはあまりpjaxを採用するメリットはありません。(もちろん使うことには大賛成ですが)
MVCフレームワークなどを使ってViewがテンプレート化されている状態や、phpやRuby、pythonなどを使って様々な処理を行ったうえでhtmlを生成する(もちろんパート毎にテンプレート化されている)ようなサイトでこそ真価を発揮します。

5.ちゃんとイベント管理してますか?

過去teratailで回答者をやっていた頃にも論じていたのですが、いまだに誰もこのことに触れていないのでちょっと書かせていただきます。

最初に覚えておいていただきたいのが、pjaxを採用したサイトでは多くの場合、メモリリークさせてしまいがちです

5,6ページほども遷移すれば何だか動作が重くなる…なんてことはないでしょうか?そういう場合、メモリリークを疑ってみてください。

このTimeline(現Performance->Memory)はpjaxによる遷移を11ページ行ったものです。

見てみるとListeners(黄色)がどんどん増えていっています。(※Nodesも増え続けていますが…)こういうものが積もっていってメモリを圧迫するのがメモリリークです。

皆様はURLの変化を伴わないアプリケーションを製作したことはありますでしょうか?
pjaxサイトの製作方法は、それとほぼ同様です。
スクリプトの記述の仕方によっては、明示的にイベントや変数などを削除するなどする必要があります。

明示的な削除も重要ですが、上記の状態を起こす原因となりやすいものがもう一つあります。

//.selectorは#contents内の要素
$('.selector').on('click', fun);

jQueryを使用していると、よく上記みたいな指定をしていたりしませんでしょうか?

pjaxサイトでは、このイベント指定方法は御法度です。

なぜならこの場合、この.selectorは暗黙的にdocumentオブジェクトに紐付けられます。

documentオブジェクトというのは、上記の画像のように、htmlドキュメントの上のノードにあたります。

当然、#contentsに紐付いていない為、この指定方法を行ったイベントは削除しない限り残り続けます。(しかも、pjax遷移前に削除しないと削除できなくなったりするケースもあります。。)

//.selectorは#contents内の要素
$('#contents .selector').on('click', fun);

のような指定方法をしてやるとこれを(一応)回避することができます。(ベターなのは#contents下にユニークなidを付与した要素があり、その要素のidに紐づけるべき)

このあたりに関しては、後日の「pjaxでのイベント処理(仮題)」回で詳しく書かせていただきますので、今回はさわりとして簡単に。
今回はこのへんで。

次回は実際のコーディングに関する内容を書いていきます。

pluginpjax

Comments

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

認証コード * Time limit is exhausted. Please reload CAPTCHA.