SystemJS 一巡り

JavaScriptをロードするための最も基本的な方法は<script>タグですが、特にSPAを構築する場合などは、主に以下の理由からモジュールローダーを使うのが一般的かと思います。

  • 大量の<script>を書くことになり、順番も重要
  • 開発環境ではモジュール毎ファイルに分けておける

今回はAngular2で採用されているSystem.jsをざっと見てみました。


ES6ではモジュールがサポートされimport/exportを利用できますが、まだES6モジュールをサポートしているライブラリは多くないようです。SystemJSはES6の他、AMD/CommonJS/UMDのモジュールもサポートしているモジュールローダです。

SystemJS入門

ES6の初期の仕様ではSystemというグローバルなオブジェクトが定義され、実行環境にモジュールをロードするために利用することができるというものだったようですが、ES7に持ち越しとなるみたいです。

SystemJSを使うことでSystemオブジェクトが利用できるようになります。まずは、このオブジェクトが持つimport()config()を見ていきます。

import()

名前の通り、モジュールをロードするのに使うメソッドです。

System.import('./my_module.js');  
System.import('angular2/core');  

引数にはモジュール名を渡します。モジュール名はモジュールのファイルへのパス、または論理名です。モジュール名が./から始まれば、そのモジュール名はモジュールファイルへのパスと解釈されます。

import()Promiseを返します。Promiseがモジュールのオブジェクトで解決すると、then()のコールバックが実行されます。以下の様なイメージです。

System.import('./lib.js')  
  .then(module => {
    console.log(module.foo);  // foo
  });

// lib.js
export const foo = 'foo';  

この場合はES6モジュールをimportしていますが、CommonJSモジュールでもそのままimportできるということになります。

Promise.all([  
  System.import('./lib.js'),
  System.import('./lib-es5.js'),
]).then(modules => {
  modules.map((m) => {
    console.log(m.foo); // es6! CommonJS!
  });
});

// lib.js
export const foo = 'es6!';

// lib-es5.js
exports.foo = 'CommonJS!';  

config()

SystemJSの動作はconfig()で設定することができます。先の例では使っていないので、デフォルトでの設定で動いていました。

設定APIはいろいろと用意されていますが、基本的なものを確認しておきましょう。

baseUrl
ロードするモジュールの起点となるパス。指定したパスからの相対パスでモジュール名を解決する。明示的に相対パス、絶対パスが指定された場合はそちらを優先。
map
モジュール名のエイリアスを定義する。
paths
mapに似ているが、ワイルドカードが使える。
transpiler
モジュールをロードした際に使用するトランスパイラを指定する'traceur'(デフォルト)や'typescript'、'babel'を指定する
typescriptOptions
transpilerにtypescriptを使用した際の、TypeScriptコンパイラのオプション

以下のように使用します。

System.config({  
  transpiler: 'typescript',
  baseURL: '/app',
  map: {
    'typescript': '//npmcdn.com/typescript@1.8.10',
    'es6module': 'lib.js', // app/lib/lib.js
    'commonJSmodule': 'lib-es5.js', // app/lib/lib-es.js
    'tsmodule': 'lib.ts'   // app/lib/lib.ts
  },
  paths: {
    '*': 'lib/*'
  }
});

Promise.all([  
  System.import('es6module'),      // mapで定義したエイリアス名を使える
  System.import('commonJSmodule'),
  System.import('tsmodule'),
]).then(modules => {
  modules.map((m) => {
    console.log(m);
  });
});

baseURLpathsmapが適用される順番に注意してください。