目次


次世代の JavaScript モジュールを利用するために jspm を使用する

モジュール解決を自動化して、次世代の言語の機能に今から備える

Comments

JavaScript のパッケージ化は、クライアント・サイドとシステム/サーバー・サイドの両サイドにまたがっています。クライアント・サイドに関しては、JavaScript の依存関係管理とパッケージ化が長い間 Web 開発の弱点となっており、この欠点を克服するために多種多様な断片的ソリューションが使用されてきました。このチュートリアルを作成している時点ではまだ標準プレビューの段階にある ECMAScript 6 (ES6) は、これらのソリューションの中から最善のアイデアを採用して JavaScript 言語への融合を試みています。

jspm は、この ES6 の依存関係処理構文を現在の JavaScript に対して使用できるようにする、漸進的なフレームワークです。jspm では、以下の 2 つを組み合わせて利用しています。

  • (Babel を使用した) トランスパイル: 次世代の言語の機能を使用できるようにします。
  • SystemJS: 使用されている複数のパッケージング・スタイルを認識できるモジュール抽象化層。

jspm は Babel と SystemJS の上に位置し、一貫した構文でこれらを扱っており、npm や GitHub などのレジストリーを使用してモジュール解決を自動化します。jspm フレームワークにはさらに、CSS ロードと開発サーバーのサポートも組み込まれています。

このチュートリアルでは、jspm を実用面から紹介し、このフレームワークによってプロジェクトに何がもたらされるかを概説します。コンテキストとして、まず、Node Package Manager (npm) によるサーバー・サイドの依存関係管理の簡単な例を取り上げた後、それに匹敵する使いやすさ (そして、その他の利点) を jspm がブラウザー・サイドでどのように実現するかを説明します。サンプル・コードを入手するには、「ダウンロード」を参照してください。

npm ローカル

Node.js と、ブラウザーにとらわれない汎用言語として Node.js に適用されている JavaScript については、お馴染みのことでしょう。Node.js では JavaScript ファイルを実行することができますが、Node.js におけるほぼすべての関心事は、組み込まなければならない「モジュール」(「依存関係」とも呼ばれます) によって実現されます。そして、サーバー・サイドの依存関係をグローバル・システム・レベルでも、ローカル・プロジェクト・レベルでも、ごく簡単にインストールして制御できるようにするのが npm です。

お使いのシステムに Node.js と npm がまだインストールされていない場合は、今すぐインストールしてください。インストールが完了すると、nodenpm という 2 つのコマンドがグローバル・パスに設定された状態になります。

このチュートリアルで取り組むプロジェクトでは、スピナーを組み込んだコマンド・ライン・アプリケーションを作成します。スピナーとは、処理が進行中であるため待つようにユーザーに伝える、アニメーション化されたアイコンのことです。スピナーは、以前はコンピューティング・インテンシブな機能でしたが、今では日常的に使えるように単純化されて、node モジュールとして存在しています。

node モジュールをグローバル・コンテキストにインストールするには npm install <モジュール名> -g を実行しますが、このアプリのコンテキストをグローバルにするのは適切ではありません。それよりも、プロジェクトの依存関係を明示的に定義して、それらの依存関係をプロジェクトに含めるほうが適切です。そうすれば、バージョン管理からプロジェクトをチェックアウトして、プロジェクトそれ自体の中から依存関係をインストールすることができるほか、プロジェクトに含まれる依存関係をリスト表示することもできます。

そこで、代わりに package.json (プロジェクト内で npm を制御するファイル) を使用して、そこに依存関係の情報などを含めることで整理された状態を維持するようにします。リスト 1 に、このアプリの単純な package.json を記載します。プロジェクト用に新しいフォルダーを作成して、そのフォルダーのルートにこのファイルを配置してください。

リスト 1. 依存関係を定義する package.json
{
    "name": "jspm-intro",
    "preferGlobal": true,
    "version": "1.0.0",
    "author": "user",
    "description": "JSPM Intro - Demo App",
    "license": "",
    "engines": {
        "node": ">=0.10"
    },
    "scripts": {
        "go": "node app-cl.js"
    },
    "dependencies": {
        "cli-spinner": "0.2.2"
    }
}

package.json の dependencies エントリーで cli-spinner モジュールを参照し、バージョン番号を指定していることに注目してください (ほとんどの JavaScript パッケージは、npm ページ (例えば cli-spinner ページ) に最新のバージョンを指定します)。現在のバージョンを取得することもできます。それには、コマンド・ラインから npm install <パッケージ名> を実行します。

依存関係をインストールするには、コマンド・ラインから npm install を実行します。npm によって依存関係がダウンロードされて /node_modules に格納される様子を観察してください。

リスト 1 では、scriptsgo コマンドが含まれていることも注目に値する点です。scripts プロパティーでは、定義された npm コンテキスト内で任意のコマンドを実行することができます。上記の場合、このプロパティーによって node app-cl.js コマンドで依存関係が使用可能になります (npm run-script コマンドの実行中に、/node_modules ディレクトリーがパスに追加されます)。

リスト 2 に、単純ながらも奥の深い app-cl.js プログラムを示します。

リスト 2. Node.js アプリでモジュールを必須として使用する
"use strict";
var Spinner = require('cli-spinner').Spinner;

var spinner = new Spinner('This is gonna blow your mind... %s');
spinner.setSpinnerString('|/-\\');

spinner.start();

setTimeout(function() {
    spinner.stop();s
}, 3000);

コマンド・ラインから、npm run-script go を実行すると、スピナーが動作しているのがわかります。このように、依存関係を簡潔なやり方で使用することができます。

Web アプリでの jspm の使用法

例えば、Web アプリを作成していて、そこに jQuery を組み込みたいとします。npm がインストールされていれば、スピナー・モジュールをインストールしたのと同じ方法で、package.json の dependencies セクションに「"jquery": "2.2.0"」を追加することで jQuery モジュールをインストールすることができます。この行を追加してから npm install を実行すると、jQuery が /node_modules に格納されます。ただし、jQuery をどのようにインストールするかに関わらず、jQuery モジュールを Web アプリに取り込むための手段が必要です。

Web アプリには index.html ページがあるので、このページから jQuery を使用できるようにしたいとします。そのための簡単な方法は、index.html ファイルを作成し、そのファイル内に <script> タグを指定して、node_modules/jquery/dist にある jQuery ファイルを読み込むという方法です。リスト 3 に、この方法の例を示します (デモ・アプリで、このファイルに該当するのは index-script.html です。「ダウンロード」を参照してください)。

リスト 3. <script> タグを使用してファイルを読み込む典型的な昔流の index.html
<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <title>JSPM Intro</title>
  <meta name="description" content="JSPM Intro">
  <meta name="author" content="MTyson">
</head>

<body>
  <script src="node_modules/jquery/dist/jquery.js"></script>
  <script src="app.js"></script>
</body>
</html>

app.js において、ドキュメント本体に何らかのコンテンツを追加したいとします。それには、&("body").append("<div>Ground control to major Tom.</div>"); とすることができます。これが可能なのは、jQuery ファイルを読み込んだことから、$ グローバル変数にアクセスすることができるためです。

この方法は有効ですが、さまざまな欠点があります。プロジェクトが複雑化してより複雑なモジュールに依存するようになると、なおのことです。例えば、HTML に書き出されるさまざまな (深くネストされている可能性がある) 依存関係をすべて保持しなければなりません。また、パッケージを読み込む部分と、パッケージを使用する部分は切り離す必要もあります。さらにこの方法では、モジュールがグローバル名前空間で公開されていなければなりません。

この昔ながらの単純なグローバル変数の方法を用いるのではなく、Java の世界の MavenGradle のような依存関係マネージャーで管理できる、(Java の JAR や import 文を使用するときのように) 適切にモジュール化されたファイルを読み込みたいものです。

読み込むモジュールは ES6 で作成することができ、AMD、CommonJS、ES6、そして昔流のグローバル・パッケージをはじめとする、さまざまなフォーマットのパッケージを読み込むことができます。

モジュール化を構文で行う方法はいくつかありますが、最近では CommonJS を使用するのが中心的な手法となっており、npm パッケージでも CommonJS を使用しています。npm パッケージは、Browserify などのツールによってブラウザーで使用可能なパッケージに変換されます。

jspm は CommonJS をサポートしていますが、さらに一歩先を行っており、SystemJS によって、新しく標準となる ES6 の読み込み構文をサポートしています。SystemJS は、メタインポート互換層のようなものです (ES6 では、System オブジェクトがブラウザーのネイティブ・オブジェクトとなる予定であるため、これにちなんで SystemJS という名前が付けられています)。SystemJS を使用すると、読み込むモジュールを ES6 で作成することができ、AMD、CommonJS、ES6、そして昔流のグローバル・パッケージをはじめとする、さまざまなフォーマットのパッケージを読み込むことができます。

これから、jspm を使用して jQuery を読み込む方法、つまり具体的には、jspm を使用して依存関係を app.js ファイルに関連付ける方法を説明します。

jspm をインストールする

  1. jspm をプロジェクトに追加するには、package.json の dependencies セクションに jspm の追加をハンド・コーディングすることもできますが、ここでは別の方法を紹介します。コマンド・ラインを開いて、npm install jspm --save-dev と入力してください。このコマンドは jspm パッケージを package.json の devDependencies フィールドに追加します。devDependenciesdependencies セクションと同じ役割を果たしますが、このフィールドは当該アプリケーションの機能では使用されず、開発ツールでだけ使用されます。jspm はビルドにのみ使用するので、dependencies セクションに含める必要はありません。
  2. jspm コマンドを使用可能にします。この場合も jspm をグローバルにインストールするのではなく、npm を使用して整理された状態を維持するようにします。"jspm": "jspm"scripts フィールドに追加してから、コマンド・プロンプトで npm run-script jspm init を実行します。
  3. jspm からの問いかけに対しては、すべてデフォルト値を受け入れます。これにより、jspm が自動的に config.js ファイルを作成し (このファイルは主として依存関係マップを作成します)、package.json を更新し、jspm_modules ディレクトリーを作成します。

package.json の jspm エントリー

package.json を見ると、jspm のインストールによって jspm オブジェクトが追加されたことがわかります。jspm オブジェクトにネストされている新しい devDependencies は、他の npm 依存関係と同じように機能しますが、対象とするのは jspm だけです。このように、jspm によって整理された状態が維持されます。jspm の依存関係の構文は少し異なることに注目してください。パッケージ名にはプレフィックス npm が付けられます (例: "npm:babel-core@^5.8.24")。このプレフィックスが使用される理由は、jspm は npm を含め、複数の「レジストリー」をサポートするからです (組み込みレジストリーには GitHub もあります。jspm のレジストリー・システムは拡張可能なので、例えば組織固有のレジストリーを使用するために、新しいレジストリー・エンドポイントを定義することもできます)。"npm:babel-core@^5.8.24" というコードは、jspm に対し、npm から babel-core パッケージを取得するよう指示します。そして jspm が ES6 構文を最近のブラウザーで読み取ることのできる JavaScript にトランスパイルするために使用するのが Babel です。

jQuery を追加するために、コマンド・ラインで npm run-script jspm install jquery を実行します。package.json を調べると、新しいエントリー "jquery": "npm:jquery@^2.2.0" が見つかります。このエントリーと同じく、「名前、レジストリー、モジュール名、バージョン要件」で構成されるパターンは目にするはずです。jspm は jQuery のような一般的な要件の小さなレジストリーを維持管理します。このようにして、jspm は jquery から npm:jquery に変換する方法を認識します。あまり一般的ではないモジュールの場合は、コマンド・ラインでモジュールをインストールする際に、レジストリーとモジュールの両方を指定する必要があります。

アセットは jspm によって自動的に準備されるので、早速アセットの使用方法を調べることができます。

この時点では、app.js ファイルを index.html に含めるのではなく、このファイルをモジュールとしてインポートすることができます。ただしその前に、サポート・ファイルを 2 つ読み込む必要があります。具体的には、jspm によって自動的に作成される config.js ファイルと、system.js ファイル (SystemJS) です。ES6 構文を使用した依存関係の読み込みは、SystemJS により、巷に存在するさまざまなパッケージに適用できるようになります。

リスト 4 に示す index.html ファイルは、jspm によって生成されるビルドを使用します。他の読み込みモジュールについて心配する必要はありません。この後すぐに、本番用にさまざまなものをバンドルする方法を説明します。

リスト 4. jspm によって生成されるビルドを使用した index.html
      <!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <title>JSPM Intro</title>
  <meta name="description" content="JSPM Intro">
  <meta name="author" content="MTyson">

  <head>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
      System.import('./app');
    </script>
  </head>
</head>

<body>
</body>
</html>

以下のコードからなる app.js ファイルを作成します。

import $ from 'jquery';

&("body").append("<div>Ground control to major Tom.</div>");

import $ from 'jquery'; によるインポートに注目してください。これは、「名前指定インポート」です。パッケージを要求するためには、ES6 構文をいくつかの異なる方法で使用することができます。上記の例では、デフォルト・モジュールをインポートするために、昔ながらのドル記号を使用しています。

エクスポートされる関数

ES6 構文をもう少し掘り下げて検討しましょう。ES6 では、1 つのモジュールで複数のエクスポートを定義することができます。従って app.js ファイル内で、エクスポートされる関数を作成することができます。

import & from 'jquery';
export function init(){
      &("body").append("<div>Good morning!.</div>");
      &("body").append("<div>How are you?.</div>");

index.html 内では、リスト 5 に示すエクスポートを利用することができます。これらのモジュールは非同期でロードされるため、AMD 構文と同様の .then(...) プロミスを使用してコールバック関数を定義していることに注意してください。

リスト 5. エクスポートされるモジュールを使用する
  <script>
      System.import('./app').then(function(app){
        app.init();
      });
    </script>

CSS およびその他のアセットのローダー・プラグイン

アプリに統合したい CSS ファイルがあるとします。この場合、まず始めにコマンド・ラインから npm run jspm install css=npm:jspm-loader-css を実行して、css という名前の jspm CSS ローダーをインストールします。

次に、以下の内容の単純な app.css ファイルを作成します。

div {
  background-color: red;
}

ここで、import './app.css!'; という行を app.js に追加することで、上記 CSS を自分の JavaScript にインポートすることができます。また、CSS は評価時 (つまり、スクリプトがブラウザーで実行されるとき) にロードされるので、必要に応じてオンデマンドで CSS モジュールがロードされるよう、賢明な動作にすることができます。

npm run jspm install scss=sass を実行すれば、SASS、CSS プリプロセッサーでも同じような結果を実現することができます。SystemJS および jspm に対応したローダー・プラグインをすべて網羅したリストを参照してください。

jspm dev サーバーを使用してテストする

ブラウザーはローカル・ディスクからパッケージをロードすることができないため、アプリをテストするには、サーバー内でファイルをホストする手段が必要です。その手段としては、nginx、Apache、Express サーバーのどれでも使用することができます。しかし、jspm が稼働中になっているので、代わりに jspm dev サーバーをインストールして使用することができます。

ユーティリティーとしての jspm dev サーバーは、グローバルにインストールすることが望まれます。ステージング環境、テスト環境、本番環境といった環境では、アプリは特定のサーバーによってホストされる可能性が高いことから、この jspm dev サーバーという依存関係は、グローバルにインストールするほうが理に適っているのです。npm install -g jspm-server を実行します。プロジェクトのルートで jspm-server と入力します。そして、jspm によってブラウザー・ウィンドウがオープンし、http://127.0.0.1:8080/ にリダイレクトされて、実行中のアプリが歓迎の挨拶を表示する状態になるまで待ちます。

jspm dev サーバーは、リクエストのプロキシー処理をサポートすることに注意してください。従って、バックエンド・サービスを統合するのなら、開発中に特定の URI パス (例えば、/api) をそのバックエンドにマッピングすることができます。

本番用にバンドルする

依存関係ツリーは本番用にバンドルすることができます。コマンド・ラインから npm run jspm bundle app --inject を実行すると、jspm が app.js エントリー・ポイントに基づいて、依存関係グラフ全体を build.js という単一のファイルに抽出します。従って、システムが読み込みモジュールを検出した時点で非同期にロードするのではなく、すべてのものを一挙にロードすることができます。

ロードする時点で、例えば <script src="build.js"></script> とすれば、app.js の代わりに build.js を読み込むことができます。ただし、system.js と config.js が必要なことに変わりはありません。

まとめ

jspm を使用する素晴らしい点は、いずれブラウザーが SystemJS 構文をネイティブ・サポートするようになる日に備え、今からアプリケーションを準備できることにあります。将来に備えた構文を使用することで、幅広い既存のバンドルを利用することができます。さらにその過程で、CSS の組み込みや dev サーバーといったビルド手段を利用することもできます。SystemJS の大部分はネイティブ・ブラウザー・サポートで置き換えられる可能性がありますが、そうなったとしても、jspm のパッケージ化とバンドルの側面は引き続き有用であり、必要となるはずです。


ダウンロード可能なリソース


関連トピック

  • jspm: jspm.js プロジェクトの Web サイトにアクセスし、ドキュメント、サンプル・コード、コミュニティーでのディスカッションを利用してください。
  • GitHub 上の jspm: jspm の GitHub ページです。
  • Foundation: Foundation のレスポンシブ UI フレームワークを掘り下げてください。
  • npm: npm パッケージ・マネージャーについて探ってください。
  • 『Javascript in 2015』: jspm の紹介が含まれる動画によるツアーを見てください。
  • SystemJS: jspm が使用する SystemJS ES6 構文エミュレーターについて学んでください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Open source
ArticleID=1033722
ArticleTitle=次世代の JavaScript モジュールを利用するために jspm を使用する
publish-date=06232016