Open10

ES Modules 周辺調査

フロントエンドえんじにゃーフロントエンドえんじにゃー

ES Modules

JS標準のimport/export機構
https://262.ecma-international.org/6.0/#sec-imports

ESM, ES2015 Modules, ECMAScript Modulesなどと呼ばれている。

これまでhtml + jsでは、scriptタグを利用することでしか外部のファイルを読み込む方法が無かった

・html仕様に依存
・jsファイルの読み込み順に気をつけないといけない

Node.jsでは、ESMが導入される前までCommonJSという独自仕様でモジュールが利用できた。

他にAMDという仕様も存在した。
https://requirejs.org/docs/whyamd.html
https://yukidarake.hateblo.jp/entry/2015/08/21/194520
https://qiita.com/nanocloudx/items/70f1316debf05b93ac82

//Calling define with a dependency array and a factory function
define(['dep1', 'dep2'], function (dep1, dep2) {

    //Define the module value by returning a value.
    return function () {};
});

ESMとCommonJSの記述の違い

CommonJS

  • require
const xxx = require('./')
const {xxx, yyy} = require('./')
  • exports
module.exports = xxx
module.exports = {xxx, yyy}
module.exports = {xxxName: xxx, yyyName: yyy}

ESM

  • import
// 特定エクスポートのインポート
import { xxx } from './'

// モジュールのコンテンツ全てインポート
import * as lib from './'

// デフォルトエクスポートのインポート
import xxx from './'
  • export
export const func = () => {...}
export function func() {...}
export class ClassName {...}
export {a, b, c}
export default function func() {...}
export default {a, b, c}

バンドラーを利用せずにデフォルトのままESMを利用する場合

scriptタグにtype="module"を記述

  <script type="module">
    import {sayMessage} from "./sample-alert.js";
    sayMessage("こんにちは世界");
  </script>

↓こういうのもできる世界になっている

  <script type="module">
    import * as THREE from 'https://cdn.skypack.dev/three';
    
    // Three.jsの起動コード (略)
  </script>
フロントエンドえんじにゃーフロントエンドえんじにゃー

ESModulesとして設計されたライブラリでないと、利用できない
jQuery, ReactなどはESModulesとして配布されていない為、現時点ではブラウザネイティブではESMとして利用できない。

nodeコマンドではesmの記述が直接実行できない

// fail
node import.js

// success
node --experimental-modules import.js
フロントエンドえんじにゃーフロントエンドえんじにゃー

Import maps

import する際のエイリアスとして有効

<script type="importmap">
{
  "imports": {
    "lodash": "https://cdn.jsdelivr.net/npm/lodash-es@4.17.21/lodash.min.js",
    "@" : "./sample-alert.js"
  }
}
</script>

<script type="module">
  import * as _ from 'lodash';
  import {sayMessage} from '@';

  const a = {'a': 1};
  const b = {'a': 3, 'b': 2};
  const c = _.defaults(a, b);

  sayMessage(JSON.stringify(c));// {a: 1, b: 2}
</script>
フロントエンドえんじにゃーフロントエンドえんじにゃー

ESMのデメリット

ファイル転送数が増える

JSファイルを細かく分けると、転送すべきファイル数が増えてしまう可能性がある。
HTTP/1.1プロトコルは同時接続数が限られるため多くのファイル転送が苦手
HTTP/2に対応したWebサーバへ移行するのが適した手段

キャッシュ対応が難しい

ファイル末尾に?○○とつけるとURLがユニークになってキャッシュを避けられるが、
ESMの場合、jsを直接変更する必要があり、あまり賢いやり方ではない。

モジュールバンドラー

webpackやviteを利用することで、ESMを使用したコードを互換性のあるjsファイルへ変換する。

ESMが普及するとこうしたバンドラーが不要になる説がある。
ESMに関しては不要だが、jsxやtsのコンパイラとして必須。
ただ、ESM部分に関しては最適化ができる可能性がある。

フロントエンドえんじにゃーフロントエンドえんじにゃー

モジュール
Node.jsはデフォルトで全てのモジュールをCommonJSで扱うので、以下のいずれかの対応でモジュールシステムを変える必要がある。
Node.jsについてデフォルトで全てのモジュールをCommonJSで扱うので、以下のいずれかの対応でモジュールシステムを変える必要がある

方法1. package.jsonを追加し、"type": "module"を設定する。mainを設定する必要がある。

package.json
{
  "type": "module",
  "main": "./main.js"
}

方法2.--input-type=moduleをつけて実行する

node main.js --input-type=module

方法3. .mjsに拡張子を変える
main.mjs

const functions = require('./libs/functions.mjs')
functions.hoge()
フロントエンドえんじにゃーフロントエンドえんじにゃー

Dllとは

Dynamic Link Library
プログラムを動かす時に合体させるプログラム部品ファイルのこと

https://wa3.i-3-i.info/word11024.html

WebPack DLLPlugin

事前にライブラリを別のbundleとしてビルドしておいて、それに含まれるライブラリに依存するバンドルは後からライブラリのbundleにリンクするという形を取ります。
これによりライブラリとライブラリを使う側のコードのビルドを分離できるので、都度ライブラリ側のコードをビルドしなくて良くなりビルドが速くなります。

https://qiita.com/kazuhei/items/dc4d716a370795276174