前の章で説明したエクスポートとインポート文は “static(静的)” と呼ばれます。この構文は非常にシンプルかつ厳密です。
まず import
の任意のパラメータを動的に生成することはできません。
モジュールパスはプリミティブな文字列でなければならず、関数呼び出しもできません。これは動作しません。:
import ... from getModuleName(); // Error, string だけが許可されています
次に、条件に応じたインポートや、実行時にインポートすることはできません。:
if(...) {
import ...; // Error, 許可されていません!
}
{
import ...; // Error, 任意のブロックに import を置くことはできません
}
これは、import/export
はコード構造のバックボーンを提供することを目的としているためです。コード構造は分析することができ、特別なツールを利用してモジュールを集め一つまとめることができ、未使用のエクスポートは除去されます(tree-shaken)。これはインポート/エクスポートがすべてが固定されているがゆえに可能なことです。
では、どのようにしてモジュールを動的に、オンデマンドでインポートするのでしょう?
import() 式
import(module)
式は、モジュールを読み込み、モジュールがもつすべてのエクスポートを含むモジュールオブジェクトになる promise を返します。
コードの任意の場所で動的に利用できます。以下は例です:
let modulePath = prompt("Module path?");
import(modulePath)
.then(obj => <module object>)
.catch(err => <loading error, no such module?>)
あるいは、async function 内であれば let module = await import(modulePath)
とすることができます。
例えば、次のようなモジュール say.js
があるとします:
// 📁 say.js
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
…動的インポートは次のようにできます:
let {hi, bye} = await import('./say.js');
hi();
bye();
また、say.js
が default export を持っている場合は次のようになります:
// 📁 say.js
export default function() {
alert("Module loaded (export default)!");
}
…そこへアクセスするには、モジュールオブジェクトの default
プロパティを使用します。:
let obj = await import('./say.js');
let say = obj.default;
// あるいは、1行で: let {default: say} = await import('./say.js');
say();
ここに完全な例があります:
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
export default function() {
alert("Module loaded (export default)!");
}
<!doctype html>
<script>
async function load() {
let say = await import('./say.js');
say.hi(); // Hello!
say.bye(); // Bye!
say.default(); // Module loaded (export default)!
}
</script>
<button onclick="load()">Click me</button>
ダイナミックインポートは通常のスクリプトで動作するので、script type="module"
は必要ありません。
import()
は一見すると関数呼び出しに見えますが、たまたま括弧を使用している特別な構文です(super()
と同様です)。
したがって、変数に import
をコピーしたり、call/apply
を使用することはできません。関数ではないからです。