Skip to content

Instantly share code, notes, and snippets.

@uupaa
Last active December 21, 2015 10:18
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uupaa/6290853 to your computer and use it in GitHub Desktop.
Save uupaa/6290853 to your computer and use it in GitHub Desktop.
The typical module pattern が AMD に対応しない理由

The typical module pattern の「AMD をサポートしない理由」をこちらに移動しました。

AMD をサポートしない理由

  • AMDに対応しようとするとコードの上下に無視できない量のノイズ(コード)が乗ってしまいます
  • Node.js 上でAMDは正直使いどころがありません
  • 依存を解消するために、AMD 対応を行うと、今度は何らかのライブラリに依存することになります
  • Mobile WebApplication の エッジトレンドは、今後は WebWorkers にシフトすると考えており、WebWorkers には importScript があるため、AMD 対応する理由がないのです

WebWorkers の importScript は、かなり直感的に機能します。

Worker スレッドはグローバル関数へのアクセスや、importScripts() でスクリプトやライブラリを自身のスコープへインポートすることができます。これはインポートするリソースの URI を 0 個以上、引数として受け入れます。以下の例はすべて妥当です:

  1. importScripts(); /* imports nothing */
  2. importScripts('foo.js'); /* imports just "foo.js" */
  3. importScripts('foo.js', 'bar.js'); /* imports two scripts */

ブラウザはそれぞれのスクリプトを読み込み、実行します。各スクリプトのグローバルオブジェクトは、Worker から使用できます。
スクリプトを読み込むことができない場合は NETWORK_ERROR を投げて、それ以降のコードを実行しません。
それでも、すでに実行されたコード (window.setTimeout で繰り延べされているコードを含みます) は動作します。
importScript() メソッドより後にある関数の宣言は、常にコードの残りの部分より先に評価されることから、同様に保持されます。
スクリプトは順不同にダウンロードされますが、実行は importScripts() に渡したファイル名の順に行われます。これは同時に行われます。すべてのスクリプトの読み込みと実行が行われるまで importScripts() から返りません。

via https://developer.mozilla.org/ja/docs/Web/Guide/Performance/Using_web_workers

AMD ローダーを利用することで、JavaScript の依存関係をブラウザ上で動的に解決することが可能ですが、動作原理を把握できていない場合は、そのような方法にはできるだけ依存せず、自前でファイルを結合しminifyを施したコードを利用する方法を推奨します。  

その理由は、

  1. JavaScript を実行するモバイル端末の実行速度は日々向上していますが、ネットワーク環境はなかなか改善されません。 グローバル視点で他国を見渡せば、劣悪なネットワーク環境などいくらでもあります。自国基準で考えるなかれです。
  2. モバイルブラウザは MaxConnection(同時にアクセスできる本数)が限られています。 いかにしてネットワークアクセスを減らし、他のコンポーネント(styleやimage)の読み込みを邪魔しないかが鍵になります。  
  3. できるだけネットワークにアクセスせず、クライアントサイドで高度にキャッシュを管理する方法がベストです(例のプロジェクトの成果を参照してください)。

です。

依存関係はクライアントサイドに持ち込まず、可能な限りサーバサイドで解決してください。

AMD をサポートしない理由 (Effective)

開発者のニーズに答えるべく、ES6 では module loader が言語仕様として検討されています。

現時点(2013-08-21)で、実装しているブラウザはまだ存在しないようですが、
今後仕様策定が進むと、AMD をベースとしたモジュールシステムは不要になる可能性があります。

また、それまでに AMD が十分に普及している場合は、仕様に別途 AMD モジュールの読み込みサポートが加えられる可能性もありますが、 現状ではそのような動きは確認できていません。
また、そのような事が起きた場合は、このドキュメントを適切に update する予定です。

AMD をサポートしない理由 (More)

AMD ローダーが必要な状況とは、おそらく…

  1. 大量のライブラリやコンポーネントがある
  2. 依存関係の解決にストレスを感じる(面倒な)状態

といった状況で、機械的にできる部分は任せてしまいたい状況だと思います。

代表的な AMD ローダー( require.js )には、サーバサイドで事前にビルドする r.js というビルドツールが付属しています。
r.js を使うと依存関係を加味した状態でサーバサイドで JavaScript を 結合 + Minify できます。

ただし、AMD ローダーが欲しくなるほどの状況で、沢山のファイルを1つのファイルに結合してしまうと、おそらくかなり大きな(500kB〜1MBほどの)ファイルサイズになってしまうのではないでしょうか。
モバイルブラウザでは、1KB の JavaScript のパースに 1ms が必要と言われています( [要出典] )。1MB なら約1秒です。

このような場合は、そもそも1つの大きなファイルにまとめず、

  1. BootStrap 機能のみの小さな js
  2. 共通コンポーネントを集めた js (underscore + backbone + ... を結合したちょっと大きめのファイル)
  3. 特定のシチュエーションや画面で必要なコンポーネントを集めた js (scene1.js, scene2.js ...)

といったように、幾つかのパーツに分割することで、ユーザの待ち時間やネットワークのコストを最小化する方法もあります。
また、共通コンポーネントも毎回ネットワークからダウンロードせず、BootStrap がローカルストレージから取り出しても配置するという方法も、有効な選択肢の1つになるでしょう。

実際のところ、AMD ローダーを導入することで得られるメリットは他の方法でも得ることができ、
AMD ローダーを導入しなかった場合に発生すると予想されるコスト(ファイル結合 + Minify パイプラインを構築する手間)の大部分は、開発のある段階で一度だけ支払えば良いものです。
さらにいえば、クライアント上で実行時に毎回支払う必要のないコストです。
そして、そのコストはGrunt( Gruntfile.js )に記述を追加することで、案外あっさりと支払う事ができるはずです。

require.js をクライアントサイドで利用する場合は、require.js の読み込みコスト実行時コスト も計算に入れる必要があるでしょう。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment