以下斜め読んだ内容

pseudo translation of useful posts, book reviews, remarks,etc. twitter: feeddict

Nicholas C. Zakas「ローディング系jsライブラリについて思うところ」

nczonline 2010.12.21のエントリ

Thoughts on script loaders

  • ローディング系jsライブラリはいらない派のNicholasが使われてるテクニックを切り口に見解を淡々を述べる趣旨のエントリ
  • いらない派であること、他の人のエントリの紹介とかが不正確、ということでコメント欄が盛り上がってる
以下斜め読んだ内容

最近のjs高速化ニュースとして・・・

  • ControlJS
  • LABjs
    • ControlJSのライバル。2009年にスタートしたライブラリ
    • クリエータは、Kyle Simpson
    • LABjsのゴールはControlJSのゴールと少し違う
    • LABjsのゴール1
      • jsファイルの並列ローディング。これはControlJSと共通
    • LABjsのゴール2
      • jsファイルの実行順番のコントロール。これはControlJSにはない機能
  • jsの並列ローディングのブラウザのサポート
    • サポートされていたりいなかったり
    • サポートされてないブラウザでも並列ローディングができるようにするテクニックはある
      • たとえばLABjsが提供してる
  • ControlJSもLABjsもそれぞれ異なるブラウザ判別テクニックを使ってる
    • 各ブラウザごとに並列ローディングを最適化するために必要
    • LABjsはFeature Detection(サポートしてるオブジェクトで判別)プラス、いろいろ仮定して判別
      • シンプルなFeature Detectionじゃないのでinference detectionととりあえず呼んでる。
      • Fx判定用にFx3系独自の「__count__」プロパティをチェックして判定してた
      • Fx4(というかjavascript1.85)では「__count__」廃止。このテクニックは死亡
      • Kyleは「__count__」廃止を把握済
      • (補足)LABjsはすでにこの問題を克服済み。
    • ControlJSはUA Detection(UA情報見て判別)使ってる
    • Feature Detectionはアプローチとして問題があるし、UA Detectionよりも高精度というわけでもない
    • 好みになるが自分(=Nicolas)はUA Detection派
      • 「何をしてるのか」が明示的なテクニックの方がエラーが発生したときにデバッグがより早くできると思うから
    • 歯切れ悪いが一応。UA Detectionの方がFeature Detectionよりも優れてるという話ではない。
  • 各ブラウザごとに並列ローディングを最適化するというアプローチがはまり込む問題
    • ブラウザのリリースと更新・変更の嵐に耐えるのは難しい
    • ブラウザの裏をかくタイプのテクニックは使わないほうがいい
    • 最近だとのLABjsがはまった
    • LABjsがナイトリー版Firex4ではまった問題
      • オンザフライで挿入されたscript要素に対しても実行順番のコントロールをしたいときに使うテクニック
        • LABjsが使ってたテクニックがFx4で使えなくなった
    • Fx4で挙動が変更されたこと自体は真っ当
      • HTML5への準拠
      • 他のブラウザと挙動を揃えたい
  • ControlJSも明日は我が身。今のところ無事だが

LABjs/ControlJSなどローダー系ライブラリがページ高速化のために取り組んでる問題

  • 大まかにいうと3つ
    • 1.jsファイルの並列ローディング
    • 2.jsファイルの実行順序のコントロール
    • 3.jsファイルローディングと評価・実行を分離
  • 1.jsファイルの並列ローディング
    • ライブラリによる実装のメリットは大きい
    • ブラウザでjsファイルのローディングがサポートされつつある
      • Steveがエントリ書いて指摘してる
      • Loading Scripts Without Blocking
      • (補足)この紹介は結構ミスリード
        • Steveのエントリはjs/css/img/html等のページのすべてのリソースの問題の並列ローディングの実現がテーマ
        • 最近のブラウザの並列ローディングは向上した点は指摘
        • けど、html(iframe要素経由)や画像(img要素経由)ではブロックされてることを指摘してる
        • Steveの記事は前に斜め読んだ(S.Souders「Loading Scripts Without Blocking」 )
    • 旧世代ブラウザで速度を絞り出すチューニングはパズルとしては面白い
    • jsライブラリが取り組む問題ではない
    • ブラウザが取り組み・サポートしつつある
  • 2.jsファイルの実行順序のコントロール
    • LABjsが注力してる
    • ControlJSはサポートしてない(する気がない)
    • ロードされた複数のjsの実行順番を決める
    • 個人的にはこの機能はおすすめしない
    • この機能を必要としてるエンジニアがいるのは事実
  • 3.jsファイルのローディングと評価・実行の分離
    • いつでも好きなタイミングでjsが実行できるようにする
    • できるだけ高速でjsローディングしたい。けど評価・実行は好きなタイミングでできるようにする
    • jsコミュニティ内部でもさんざん実験・検証がされてきた機能
      • Steveがエントリでも指摘してる
    • ControlJSが注力してる
    • LABjsはサポートしてない
      • (補足)不正確な紹介
      • LABjsもこの機能はNicholasが口述する「不正なmimeタイプを使うテクニック」を使ってサポート
    • progressive enhancementに則ったjsの実装のかたち
    • ブラウザも全然サポートされる気配がない
  • ローディング系jsライブラリの活況は開発者がパフォーマンス向上に取り組む姿勢のあり方を示してる
  • ローディング系jsライブラリを使うことはお勧めできない
  • ローディング系jsライブラリが解決しようとしてる問題群はブラウザレベルでサポート・解決されるべき代物
  • ローディング系jsライブラリの取り組む問題と解決にKyleがクソ長いエントリ書いてる
    • 仕事が忙しいので逐一リプライする余裕がない

script[async]のスペックにKyleが修正案を出してる件

<script async src="foo.js"></script>
<script async="true" src="foo.js"></script>
<script async="false" src="foo.js"></script>
  • html5のasync属性
    • 値を持たない
    • script[async]、script[async=true]、script[async=false]。どれも同じ挙動
      • マークアップされたscript要素の順番を無視してDL完了したjsファイルからどんどん評価・実行されるようになる
      • この挙動はhtml5のスペックに書かれた通りの挙動
  • Kyleの修正案
    • オンザフライで生成したscript要素のasyncプロパティに値を設定するとモードが変わる
    • (コメント受けて追記)
      • ナイトリーWebKitとFx4でKyleの提案通りの実装がされた
      • script[async=false]と設定されたscript要素は、並列ローディングと実行順番のコントロールができるようになった
    • Kyleの努力はすごいと思う
    • "async=false"という字面からイメージされる内容と実際の挙動が違うのがわかりにくい
      • 字面からだとscript[async=false]は「このjsファイルの非同期ローディングはキャンセルする」
      • 実際の挙動は「このjsは非同期ローディング。かつ実行順序は保存します」。
      • コードが読みにくいと感じる。自分は明示的なものが好きなため。
    • (補足)Kyleの提案はHTML5のスペックに採用

Kyleがscriptgroup要素を提案してる件

<scriptGroup id="group1" ordered="true">
   <script src="foo.js"></script>
   <script src="bar.js"></script>
   <script>
     somethingInline();
   </script>
 </scriptGroup>
  • 複数のjsファイルのグループ化と順序付けをできる
  • 明示的なので自分はこの提案は好き
  • scriptgroupにハンドラーをセットすれば、ラップされたjsファイルのローディングが全部完了したタイミングを通知できる
  • 要素が増えるという負担はある。最終的にはこの提案の可否は開発者が支持するかどうか次第

再びjsファイルのローディングと評価・実行の分離

  • この機能自体は今後ますます重要
  • 以前の自分の講演で、ページロード後のjsの動作のせいでサイトのユーザの取りこぼしがある点について話をした
  • 実装した機能のエッセンスは簡単
    • jsのダウンロードをリクエスト
    • ロードしたファイルをキャッシュへ保存
    • 後からjsのコードを実行するためメソッドを呼び出す

Kyleはwikiページ作って要望リストをまとめている

//実装例の骨格だけ
//script要素生成し
//偽のmeme-type"text/cache"をセットしてDL完了後即評価・実行されないようにする
var script = document.createElement("script");
script.type = "text/cache";
script.src = "foo.js";
//jsのローディング完了をチェックし
//DOMツリーに挿入
script.onload = function(){
    //script has been loaded but not executed
};
document.body.insertBefore(script, document.body.firstChild);

//あとからロード済みjsを実行
//メソッドは別に定義。正しいmemetypeをもつscript要素作ってDOMツリーに追加
script.execute();

jsローディングライブラリが色々ある状況

  • SteveやKyleのような優れた開発者が取り組んでる
  • LABjsやControlJSなどいろいろ選択肢のある状況はいいこと
  • 個人としてはこの手のローディングライブラリの使用は勧めない
    • 懸念点として、ブラウザのアップデートをチェックしてメンテしないといけなくなる
    • 新バージョンリリースで挙動がおかしくなってないかチェックしないといけなくなる
    • チェックするコストがかかるがこれは削れるコスト

コメント欄

  • Steve Souders
    • ローディング系jsライブラリの賞味期限は短いが期限切れではない
      • ブラウザがこの手のライブラリの機能をサポートしてくれるのが一番よい
        • ブラウザがサポートすればこれらライブラリは不要になる
      • HTML5のスペックもブラウザも現時点でこれら機能を提供してない
    • 「jsファイルローディングと評価・実行を分離」機能
      • IEだと「jsファイルローディングと評価・実行を分離」の実装は非常に簡単
        • script要素をオンザフライ生成後DOMツリーへの追加をストップ。これだけでJSの実行を遅延できる
      • Fx3.6〜で導入されたlink[rel="prefetch"]のような形でネイティブサポートされていくと予想
  • リプライ(Nicholas)
    • IEのサポートは知らんかった。
  • Kyle Simpson
    • 色々物申す
    • 並列ローディングが解決するべき問題を狭く捉えてる
      • Nicholasはjsとjsの並列ローディングだけを見ている
        • 並列ローディングは2つの仕事がある
        • jsと他のリソース(js/css/img/html)の並列ローディング
        • DOMContedLoadedイベントの発火の遅延などの副作用ゼロで並列ローディングを実現
      • Nicholasはjsとjsの並列ローディングだけに焦点あててるが、ローディング系jsライブラリの把握として不正確
    • LABjsが「jsファイルの実行順序のコントロール」に注力という紹介も不正確
      • ローディング完了したjsから即実行。これがLABjsのデフォルト
      • .wait()メソッドを使うとオプションで実行順序を設定できる
    • 実行順序の指定が必要な場面は現実によくある。望ましい現実化どうかはさておき。
      • jQuery/jQuery UIが普及し、プラグインとかも同時に使われれば実行順序の設定は必要
      • LABjsはこの現実を受け入れ、実行順序の設定をしたい開発者に簡単な方法を提供しようとしてる
    • script[async="false"]は事実確認が不正確。async="false"、async="true"は挙動に違いある。Fx/WebKitで実装済
      • (補足)コメントの突っ込み受けてNicholasが記事更新してる
      • script[async="false"]とscript[async="true"]はマークアップでもオンザフライでの生成・挿入でも挙動同じ
      • 名前重要。「async」という名前よくない。
        • 属性という名前は実際の挙動と字面からイメージされる内容に開きがある
      • script[async]に対して、マークアップされた場合とオンザフライで生成・挿入された場合のそれぞれの挙動についてhtml5のスペックに明確に書かれてないのが現状
    • scriptgroup要素は今の所script[async]ほど反響がない
    • 「jsファイルローディングと評価・実行を分離」をIEがすでに実装済みの件
      • IEのサポートは、ロード済jsファイルがキャッシュされてるかどうかのチェックとかuglyな部分がないのがnice
      • この機能は仕様にも盛り込まれてる。IE以外のブラウザがまだサポートしてない
    • feature inferenceは現状で一番優秀
      • Nicholasのと意見が違うのは認めるが、LABjsで使ってるこのテクニックはうまく機能してる
      • この点については他のローディング系jsライブラリの追随を許さないと自負
  • リプライ(Nicholas)
    • UA detection v.s. Feature inferenceの話を出したは、LABjs批判目的じゃない。ローディング系jsライブラリの比較をするときにアンフェアなものが多いので論点を整理するために出した。エントリの趣旨からすると脱線。
    • async属性の名前がよくないのは同意
  • 再びKyle
    • あとでかく
  • async属性の紆余曲折について、Nicholas/Kyle/Henri Sivonen(@mozilla,html5パーサクリエータ)が議論してる
    • あとでかく