Webフロント:Javascriptで扱う事前データをレンダリングの遅延なくAPIから読み込む

2020/09/26に公開

0. 目的

  • ユーザーの操作が関係しない、事前に サーバサイドで設定できるデータ(例えば認証結果) をフロントのJSに送信する。
  • 上記のデータをレンダリングの遅延なく読み込む。つまり、画面が白くなったり、コンポーネントに一時的にデータがない状態を防ぐ

1. フロント側:レンダリング以前に必要なデータの読み込み方法

- HTMLのhead内にデータ読み込みのJavascriptを指定する。
 - Javascriptはサーバサイドで動的に作成する。

head
<head>
<script src="htts://www.example.com/app/preset"></script>
</head>
  • headの内容を読み込まないとbody移行の処理は実行されないが、事前データはすべて揃ってから後段の処理を実行したい為、この読み込みの仕様に即するほうが合目的である。
  • Ajaxなどで同期で読み込むとレンダリングの遅延が発生する。
  • 非同期であっても、読み込み完了のチェックを検査するため、やはりレンダリング遅延が発生する。
  • そもそも事前に送信するデータ量を抑えれば画面が真っ白になることはほぼない。(おそらく認証データや初回表示データぐらいなら問題ない。経験的にも。)

2. サーバ側:動的にJavascriptファイルを作成

  • 認証データなど事前に必要となるデータをJavascriptファイルとして動的に送信するAPIを作成する。
  • 以下、PHPでの例。
  • windowオブジェクトに値を設定するコードを返却。
  • windowオブジェクトは事前データとして必ずアクセスでき、グローバル参照可能。
  • 以下は、簡易的にしているが、windowsの直下はアプリケーションを識別するプロジェクトキーをつけるといいかもしれない。
preset.php
//-------------------------------------------------------------------------
// http://www.example.com/app/prestでアクセスした場合に以下を返却
//-------------------------------------------------------------------------

//スクリプトを返すヘッダを送信
header('Content-Type: application/x-javascript; charset=utf-8');

//送信するデータのJSONを作成
$data = ['auth' => 1];
$json = json_encode($data);

//事前設定データをJavascriptのwindowオブジェクトに設定するコードを送信
echo "(function(){window[\"preset\"] = ${json};)();";

3. フロント側:データの取得

  • windowオブジェクトに設定されたデータを読み込む。
init.js
document.addEventListener("DOMContentLoaded", () => {

  //presetキーのチェック(なければ終了)
  if(false === 'preset' in window)return;

  //authキーのチェック(なければ終了)
  if(false === 'auth' in window.preset)return;

  //認証チェック(フラグが1でなければ終了)
  if(1 !== Number(window.preset.auth)) return;
  
  //認証成功
  alert('valid user!');
});

4.クエリパラメーターはリファラーから取得

  • パラーメーターは動的なJavascriptのタグには付けられないが、動的Javascriptを設定してあるページにクエリパラメーターを設定してアクセスし、サーバ側でリファラーで値を取得する。
get_referer.php

public function refererQuery($key) {
  $decoded = urldecode($_SERVER['HTTP_REFERER']);
  $parts = parse_url($decoded);
  parse_str($parts['query'], $queries);
  return $queries[$key];
}

5. 備考

  • 実際に、上記の方法で、初回表示でコンテンツが真っ白になったりすることはほぼなくなった
  • サーバサイドレンダリングをしなくてもこの方法でレンダリングの遅延はある程度防げる。
  • **データがない場合は、空文字(何も文字がない、Empty)**を返し、無駄なJsの処理を発生させない。
  • 事前に読み込むデータは1つのソース、つまり、HTMLのheadには1つのタグのみ指定し、その中で必要なデータすべてを受け取るようにする方が、アクセス回数を減らせるので良い。
  • 事前データを1つのアクセスで取得する場合、RESTful設計のリソースの考え方を広げて考える必要がある。複数のDBテーブルのデータを統合した、「commonSet」、「sharedSet」など統合リソースを作るほうがよいと思う。
  • RESTful設計でなく、GraphQLなら柔軟に設計できると思う。
shared_set.php

$data = [
  'auth' => [ 'name' => 'test user'],
  'products' => $products
];
$json = json_encode($data);

Discussion