何故、webpackが必要なのか?
このサイトは作成途中のものを公開しています。 また、Webpackのバージョンが1.13.0の頃に作成されたものですが、2017年2月現在Webpackは1系を非推奨として2の使用を勧めています。
今日のWebサイトは、サイトというよりWebアプリケーションに進化しつつあります。
- ページ内に占めるJavaScriptのコード量は増加する一方です。
- 多くの事が、モダンブラウザ上で行えるようになりました。
- ページを全て再読み込みして、少しのことを行っていましたが、1ページで多くのコードで処理を行うようになりました。
結果として、クライアント側のコード量が増大しました。
大規模なコードは、その構成を考えて整理する必要があります。 モジュール・システムは、コードをモジュールに分割するための手段を提供してくれます。
モジュール・システムの形式
モジュール・システムには依存性の定義と値をexportをするための、標準となる幾つかの形式が存在します。
-
<script>
タグ形式(モジュール・システムを使用しない) - CommonJS
- AMD(もしくは、その派生)
- ES6 modules
- それ以外…
<script>タグ形式
モジュール・システムを使用したことが無ければ、モジュール化したコードはこの方法で扱っているでしょう。
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>
モジュールはグローバル・オブジェクト(例えば、window
オブジェクト)にインターフェースをexportします。
モジュールは、グローバル・オブジェクト越しに依存関係にあるインターフェースにアクセスすることが出来ます。
問題点
- グローバル・オブジェクト内での衝突。
- importする順番が重要になります。
- 開発者がモジュール/ライブラリの依存性を解決しなくてはいけません。
- 大規模なプロジェクトでは読み込みのリストが肥大化し、管理が困難になります。
CommonJs: 同期require
この形式は、依存性を読み込むために同期requireメソッドを使用して、exportされたインターフェースを返します。
モジュールは、exports
オブジェクトにプロパティを追加するか、
module.exports
の値に設定することで、exportの指定が可能になります。
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
これは、node.jsによってサーバーサイドで使用されています。
メリット
- サーバーサイドのモジュールは、再利用される事があります。
- 既に多くのモジュールがこの形式に対応しています。(npm)
- シンプルで使用しやすい形式です。
デメリット
- ブロッキング呼び出しは、ネットワーク上でうまく適用されません。ネットワークのリクエストは非同期です。
- 複数のモジュールを並列にrequireすることが出来ません。
実装
- node.js (サーバーサイド)
- browserify
- modules-webmake
- wreq
AMD: 非同期require
非同期モジュール定義 (Asynchronous Module Definition)
他の(ブラウザ用の)モジュール・システムは、同期require (CommonJS)と、非同期版の導入(そして、モジュール定義と値のexport)の問題を抱えてしました。
require(["module", "../file"], function(module, file) { /* ... */ });
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
メリット
- ネットワーク内の非同期リクエスト形式に適合します。
- 複数のモジュールを並列に読み込みます。
デメリット
- コーディングが煩雑になり、コードの読み書きが難しくなります。
- いわゆる、ワークアラウンド(回避策)です。
実装
詳細は、CommonJs とAMD を参照して下さい。
ES6 modules
EcmaScript6は幾つかの言語構造をJavaScriptに追加しましたが、そのモジュール・システムはまた別の形式となります。
import "jquery";
export function doStuff() {}
module "localModule" {}
メリット
- 静的解析が容易
- 将来のEcmaScript標準
デメリット
- ネイテイブ・ブラウザへのサポートにはまだ時間が掛かります。
- この形式をサポートしているモジュールは非常に少数です。
先入観を持たない公平な解決策の提供
webpackはモジュール形式の選択を、開発者に委ねます。 既存のコードを動作させたまま、カスタムされたモジュール形式を追加しやすい仕組みを提供します。
モジュールの受け渡し
モジュールはクライアント側で実行されるため、サーバーからブラウザに受け渡されなければいけません。
モジュールの受け渡しには、2つの両極端な方法が使用されています。
- モジュール毎に、1リクエストを送信・取得
- 全モジュールを、1リクエストで送信・取得
どちらも、そこかしこで使用されている方法ですが、最善策とは言い難いものです。
モジュールの固まり(chunk)の受け渡し
より柔軟なモジュールの受け渡し方法があれば、それに越したことはありません。 前述した両極端の手法が互いに歩み寄ることで、多くのケースでより望まれる手法になることでしょう。
全てのモジュールをコンパイルする代わりに、複数の小さな固まり(chunk)にモジュールの集まりを分割することが出来ればどうでしょう?
複数のより小さなリクエストを送り、モジュールの固まりは初回時にはrequireせず、必要な時にのみrequireすることが出来ればどうでしょう? 初回のリクエストでは全コードを対象としない、より小さなリクエストにすることが出来るでしょう。
この「分割点(split points)」は開発者が任意に定めることになります。大規模なプロジェクト(コード)に適した仕組みです。
このアイディアは、GoogleのGWTによるものです。
詳細は、コード分割を参照して下さい。
何故、JavaScriptだけ?
何故、モジュール・システムはJavaScriptしか解決してくれないのでしょうか? 扱うべき静的リソースは他にもたくさん存在します。
- スタイルシート
- 画像
- Webフォント
- テンプレート用のHTML
- その他
また、
- coffeescript → javascript
- elm → javascript
- less スタイルシート → css スタイルシート
- jade テンプレート → HTMLを生成するjavascript
- i18n(国際化)ファイル → 何か
- その他
同じように簡単に扱えてもいいはずです。
require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");
詳細は、ローダーの使用方法とローダーを参照して下さい。
静的解析
全てのモジュールをコンパイルする際に、静的な解析(static analysis)はその依存性を見つけようとします。
通常、式を伴わない単純な検索しか行われませんが、
例えば、require("./template/" + templateName + ".jade")
のような書き方は、よくある一般的なものでしょう。
多くのライブラリは異なる形式で書かれており、それらの幾つかには非常に奇妙なものがあります。
方針
webpackのclever(賢い)パーサーであれば、ほとんどの既存コードを動くようにしてくれるでしょう。 もし、その奇妙なものが含まれていたとしても、このパーサーは最も適した解決策を探しだそうとするでしょう。
© 2010 - 2017 STUDIO KINGDOM