RSS

NAVERまとめに見る遅延ロードのすすめ

by agata on 2013.1.16


あけましておめでとうございます。NAVERまとめのフロントエンドを担当している縣です。初詣で引いた大吉のおみくじを握りしめながら今年も張り切っていこうと思います。

今回はJavaScriptの遅延ロードの仕組みをNAVERまとめに導入した際のお話を紹介します。

遅延ロードの検討

昨年NAVERまとめのまとめ閲覧ページや、まとめ編集ページでのJavaScriptファイルの読み込みを遅延ロード化する作業をしました。元々はページ読み込み時に全て読み込ませていましたが、JavaScriptファイルが巨大になってきてパース・実行に時間がかかるようになったことから遅延ロードを検討することになりました。

遅延ロードの利点というとJavaScriptファイルの読み込み・実行によるブラウザのレンダリング停止を防ぐのはもちろんですが、どのファイルがいつどこで必要になるかを明確にすることもでき、依存関係を動的に解決できます。規模が大きなプロジェクトほど読み込むファイルも増えるでしょうから、スクリプト側で読み込むファイルを把握できたほうが都合がいい場合が多いです。

ファイルの分割・再構成

閲覧ページでは元々閲覧者に必要な機能(共有等)Aとまとめの編集者のみに必要な機能Bを含むファイルがひとつのファイルになっていました。Aはページを閲覧する全てのユーザーに必要ですがBは閲覧のみのユーザーにとっては不要です。これらA,Bの機能が元々分割されていて別々に読み込まれていたとしても、Bのファイルは全く必要がないケースがあります。
このケースではまずAとBを別ファイルに分けた上でファイルを読み込むタイミングを変更しました。読み込むタイミングは必要になった時に初めて読み込みをする形です。「修正」というボタンを押したら初めて編集用のファイルを読み込む、といった感じです。

(読み込み量はgzip後の値)

この変更により閲覧ページでは初回のJavaScriptファイル読み込み量が全体で約40%減りました。ほぼ半分ですね。レンダリング実行までに掛かかっていた時間もおよそ半分に短縮されました。(これぐらい減ると体感でも速くなったことが実感できました)

実装例

閲覧ページではユーザー操作をトリガーに、分割したファイルを読みこませるようにしています。既述したように何かのボタンをクリックしたら読み込みを開始するなどです。ちなみにまとめは遅延ロードにLABjsを利用していますが、いろんなところでDOMイベントを監視してファイルを読み込み、コールバックを実行とやっていると冗長ですしネストも深くなってしまいます。
そういった問題の解決として以下の様なjQueryプラグインを使用しました。

(function() {
  var loaded = {};
  $.fn.hook = function(origiEventName, fileName, callback) {
    var eventName = origiEventName.split(".")[0] + ".LazyLoad",
      $elems = $(this);
    
    $elems.one(eventName, function(e) {
      var $el = $(this);
      e.preventDefault();
      e.stopPropagation();
      if (!loaded[fileName]) {
        $LAB.script(fileName, function() {
          loaded[fileName] = true;
          $elems.bind(origiEventName, callback);
          $el.trigger(origiEventName);
        });
      }
    });
  };
})();

例えばaタグをクリックした時にwasawasaという関数を実行するコードがあったとします。

$("a").bind("click", function() {
  wasawasa();
});

関数wasawasaがwasa.jsというファイルに含まれているとすると、以下のように変更できます。

$("a").hook("click", "wasa.js", function() {
  wasawasa();
});

初回はファイル読み込み後にリスナーを実行、以降はそのままリスナーのみを実行するようになります。既存のコードの形をほとんど崩さないため導入がスムーズです。
ただ、上記のようなコードであれば別にローダー関数を用意して、クリック時にwasawasaをローダー関数経由で実行するなど別の方法も考えられます。

最後に

導入にあたっては以下がポイントになりました。

  • ページ読み込み時の体感速度の向上
  • JavaScriptファイル読み込み量のダイエット
  • 依存関係の動的な解決
  • スクリプト側での読み込みファイル管理

どんなプロジェクトでも必要になるといった類のものではないので、現状の問題解決として有効かの検証が必要になります。ページ下部に読み込み用のscriptタグを移動するだけでも期待している効果が得られるかもしれないし、導入したところで大した差がない可能性もあります。ただ遅延ロードに限ることではないですが、快適なユーザー体験を提供できる可能性があるものは今後もどんどん試していこうと思います!