@NgModule
がなんなのかを知るにあたり、いくつか前提知識が必要となります。
最初はJavaScriptのモジュールです。
ES6 In Depth:Modules の勝手訳です。
前段
Googleマップが現れる前は、「典型的なJavaScriptプログラムは1行のみ」というジョークがはやっておった。
<input onChange=
というやつじゃ。大抵はそれで事足りた。
現代のJavaScriptプログラムはいくつものファイルやディレクトリにまたがる巨大なものになった。
そんなとき、必要とされるのはモジュールシステムじゃろう。
実際、JavaScriptにはモジュールシステムがある。いくつもな。。。
ここで現れたES6のモジュール文法は、パーティに遅刻してきた輩じゃと感じるかもしれん。
モジュールの基礎
ES6のモジュールは、JavaScriptプログラムを含んだファイルです。そこにはmodule
という単語はありません。普通にJavaScriptプログラムとして読めます。普通と違うところは、以下の2つです。
- 自動的にstrictモードになります。
-
export
とimport
が使えます。
export
とimport
既定では、モジュール内で宣言されたすべてのものは、モジュールの中からだけ見えます。モジュールの外に公開したいときはエクスポート(export)します。エクスポートのやり方はいくつかあります。一番簡単なのはexport
キーワードを付けることです。
export function helloWorld() {
// 何か楽しいこと
}
export class TheWorld {
// もっと楽しいこと
}
// この関数はエクスポートされません。
function workHard() {
}
トップレベルのfunction
、class
、var
、let
、const
にexport
を付けることができます。
モジュールを書く上で知っていなくちゃいけないことは2つです。全体を即時関数に入れないこととコールバックにしないことです。
エクスポートしたものを他のファイルから使いたいときはインポート(import)します。
import { helloWorld } from 'helloworld.js'
一度に複数個インポートすることもできます。
import { helloWorld, TheWorld } from 'helloworld.js'
import
文は一番最初に実行されます。深さ優先で実行していきます。
エクスポートリスト
エクスポートする別の書き方もあります。
export { helloWorld, TheWorld }
function helloWorld() {}
class TheWorld {}
エクスポートリストは先頭にある必要はありません。ファイルのどこに書いてもよいです。
別名をつける
インポートすると名前が衝突するとき、別名をつけることができます。
import { Solaris as SolarisOS } from 'sun.js'
import { Solaris as SolarisMovie } from 'hollywood.js'
エクスポートするほうで別名をつけることもできます。
class Kuniko {}
export { Kuniko as Satomi }
Defaultエクスポート
ES6モジュールはCommonJSやAMDとの相互運用性も考えられています。
人気のlodashを使うとき、ES6では各関数をインポートします。
import { each, map } from 'lodash'
each([3, 2, 1], x => console.log(x));
けど、each
と書かずに_.each
と書くシーンもあるかもしれません。
あるいは_
を関数として扱いたいとか。
そんなときは中括弧をつけずにこう書きます。
import _ from 'lodash'
これは、import { default as _ } from 'lodash'
を省略した書き方です。
CommonJSやAMDのモジュールは、default
としてエクスポートされます。
モジュールオブジェクト
長くなりました。もう少し語ってないことがあります。import *
です。
import * as cows from 'cows'
ここでcows
がmoo
をエクスポートしていれば、cows.moo
として参照するようにします。
モジュールを集約
複数ファイルから構成されるモジュールを、一箇所でエクスポートしたいことがあるかもしれません。
// yokohamaをインポートして、Shumaiを再エクスポートします。
export { Shumai } from 'yokohama'
// fukushimaをインポートして、MomoとSakuranboを再エクスポートします。
export { Momo, Sakuranbo } from 'fukushima'
// hokkaidoをインポートして、すべてを再エクスポートします。
export * from 'hokkaido'
実際のところインポートって何してるのさ
何もしてない、、、かもしれません。
大雑把に言うとこんな感じです。
- パース - ソースを呼んで文法チェックします。
- ロード - モジュールを再帰的に読み込みます。このあたりはまだ標準化されてないとか。
- リンク - 読み込んだ各モジュールを適切なスコープに配置したりなんだりします。もしここでエクスポートされていないのにインポートしているものがあれば、エラーになります。
- 実行 - 読み込み終わったモジュールを実行します。もしここで
import
文があっても何も起きません。