30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

Dynamic import() で JavaScript ファイルを動的に読み込む

ECMAScript では、モジュールを読み込むための仕組みとしてimport文を定義している。

// sub.js

export const foo = 1;
export default 2;

// main.js

import value, {foo} from './sub.js';

console.log(value, foo); // 2 1

import文は仕様上、トップレベルで書くことになっており、以下のような書き方はシンタックスエラーになる。

// main.js

// SyntaxError: 'import' and 'export' may only appear at the top level
const call = () => {
  import value, {foo} from './sub.js';
};

// SyntaxError: 'import' and 'export' may only appear at the top level
if (true) {
  import value, {foo} from './sub.js';
}

そして、読み込まれるファイルは、読み込まれた瞬間に実行される。
そのため、以下のmain.jsを実行すると、submain2の順番に表示される。

// sub.js

console.log('sub');

export const foo = 1;
export default 2;

// main.js

import value from './sub.js';

console.log('main');
console.log(value);

ES2020 で追加される仕様のひとつであるimport()は、これまでのimport文と異なり、モジュールを動的に読み込む。
また、トップレベルでないと使えないという制約もない。

読み込みたいモジュールのパスを引数として渡すと、読み込んだモジュールをPromiseでラップして返す。

// sub.js

export const foo = 1;
export default 2;

// main.js

console.log(import('./sub.js') instanceof Promise); // true

import('./sub.js').then(res => {
  console.log(res.default); // 2
  console.log(res.foo); // 1
});

非同期処理なので、以下のmain.jsを実行すると先にmainが表示され、その後にsubが表示される。

// sub.js

console.log('sub');

export const foo = 1;
export default 2;

// main.js

import('./sub.js');

console.log('main');

関数のなかで使うこともできる。

// sub.js

export const foo = 1;
export default 2;

// main.js

const call = () => {
  return import('./sub.js').then(res => {
    return res.default;
  });
};

call().then(res => {
  console.log(res); // 2
});

フラグに応じて読み込むファイルを変える、ということも可能になる。
以下のコードでは、sub.jssomeFlagtruthyのときにのみ読み込まれる。

// sub.js

export default 'This is confidential information.';

// main.js

(async () => {
  const someFlag = true;

  const message = someFlag
    ? await import('./sub.js').then(res => res.default)
    : 'please login';

  console.log(message); // This is confidential information.
})();