Inline JSONPの長所について (続: サーバサイドからクライアントサイドのJavaScriptを呼び出す際のベストプラクティス)

サーバサイドからクライアントサイドのJavaScriptを呼び出す際のベストプラクティス - kazuhoのメモ置き場 の件、id:tokuhirom がさくっと HTML::CallJS というモジュールを書いて公開してくれた (Shipped HTML::CallJS - tokuhirom's blog 参照) ので、どういう Inline JSONP を使うとどういう形で書けるか、ぱっと例をあげたいと思います。

まず、サーバサイドのプログラム。

以下は perl で、tokuhirom の HTML::CallJS と Text::MicroTemplate を使っている例。

JavaScript の呼出を保存する配列を用意し、そこに呼出をどんどん追加していっています。一定条件下でのみ特定のクライアントサイド処理を呼び出したり、配列の各要素について呼び出したりすることも簡単にできます。

# Inline JSONPで呼び出すコードを入れるためのリスト
my @inlineJsonP;

push @inlineJsonP, call_js("foo", { a => 1});        # まず foo を呼ぶ
if (...) {
	push @inlineJsonP, call_js("bar", { ... });  # 一定条件が成立したらbarを実行
}
for my $e (@list) {
	push @inlineJsonP, call_js("baz", $e);       # 配列の各要素を引数としてbazを実行
}

# レンダリングエンジンを実行
my $html = $renderer->({
    ... # other args to template engine
	inlineJsonP => [ map { encoded_string($_)  } @inlineJsonP ],
})

次にテンプレートのコード。

HEAD要素の終了部に Inline JSONP の SCRIPT 要素を展開するコードを入れる形がいいでしょう。Text::MicroTemplate の場合は、以下のような感じになるかと思います。HEAD内でいいので、サービス全体で共通のヘッダ用テンプレートに仕込む形になります。

<html>
<head>
...
? for my $e (@{$_[0]->{inlineJsonP}}) {
<?= $e ?>
? }
</head>
<body>
...

最後にJavaScriptのコード。

エントリポイントを定義するだけです。

function foo(arg) {
    ...
}

function bar(arg) {
    ...
}

function baz(arg) {
    ...
}

では、この手法のメリットが DOM ベースでの情報受け渡しに比べて何が便利なのか、あらためて確認してみましょう。

  • 呼び出す必要のあるクライアントサイドのコードが増えたり、引数が変わったりしてもテンプレートを変更する必要がない
  • クライアントサイドのコードを呼出しタイミングや条件をサーバサイドで制御できる
  • クライアントサイドの処理が関数として抽象化されているのでテストしやすい
  • HTML内に人間が読んで意味不明な情報が混入しない

別の言い方をすると、DOMベースでの情報受け渡しは、グローバル変数を使っているのと同じなのです*1。関数呼出を使った方がカプセル化できて見通しがよくなるのは当然ではないでしょうか。

PS. 「Inline JSONP」という名前は はてなブックマーク - rryuのブックマーク / 2013年11月6日 からいただきました。rryu++

*1:というより、RPCを実行する際にファイルに呼出し情報を書いているようなもの、というのが正しい