ES Modules
調べたいこと
- ES Modules
- CommonJS
- UMD
- DLL
ES Modules
JS標準のimport/export機構
ESM, ES2015 Modules, ECMAScript Modulesなどと呼ばれている。
これまでhtml + jsでは、scriptタグを利用することでしか外部のファイルを読み込む方法が無かった
・html仕様に依存
・jsファイルの読み込み順に気をつけないといけない
Node.jsでは、ESMが導入される前までCommonJSという独自仕様でモジュールが利用できた。
他にAMDという仕様も存在した。
//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()
大体CJS, ESMの違いなどは分かった。
あとはUMD
UMD→Universal Module Definition
サーバ、クライアントのどちらでも動くことができる。
基本的にRollupやwebpackなどのバンドルツールから生成される。
AMDとCommonJSの記法をサポートした古のもの
次、dynamic import周り
declare module “url”でCDNに型をつけてESMとして扱うについて
Dllとは
Dynamic Link Library
プログラムを動かす時に合体させるプログラム部品ファイルのこと
WebPack DLLPlugin
事前にライブラリを別のbundleとしてビルドしておいて、それに含まれるライブラリに依存するバンドルは後からライブラリのbundleにリンクするという形を取ります。
これによりライブラリとライブラリを使う側のコードのビルドを分離できるので、都度ライブラリ側のコードをビルドしなくて良くなりビルドが速くなります。