JavaScriptのモジュールについてまとめてく: モジュールローダ/JavaScript実行環境など編
JavaScript実行環境
JavaScriptを実行するための環境を提供するツール
例 : Node.js、Deno、Bun
Node.js
最もメジャーなJavaScript実行環境。
ブラウザだけではなく、サーバサイドでJavaScriptを実行するために作られた。
初期にはCommonJSモジュール規格(CJS)に準拠していたが、現在ではESMなどにも対応しており、ブラウザ(クライアントサイド)でも使用できる。
- 長所: 大規模なコミュニティ。成熟したエコシステム。広範なサポート。
- 短所: パフォーマンスの問題。新しい技術への遅れ。
- 使用例: 汎用的なアプリケーション。
Dneo
Node.js の作者でもある Ryan Dahl 氏が作成したJavaScript実行環境。
- 長所 : セキュリティ(デフォルトで安全)。ESモジュールのサポート。TypeScriptの組み込みサポート。
- 短所 : まだ新しく、エコシステムがNode.jsに比べて小さい。
- 使用例: セキュリティを重視するアプリケーション、TypeScriptを使ったサーバーサイド開発。
Bun
高速実行を売りにしているJavaScript実行環境。
- 長所: 高速なパフォーマンス。独自のパッケージマネージャー。Web APIへの準拠。
- 短所: まだ初期段階であり、安定性やエコシステムの成熟度が低い。
- 使用例: 高速な開発環境を求めるプロジェクト、実験的なアプリケーション。
結局どれがいいのか
基本はNode.js、試験的なアプリなら高速なBunやセキュアなDneoが良さそう
モジュールローダー
モジュールを動的にWebブラウザでロードするための仕組み。
npmなどのパッケージマネージャーによるモジュールのロードは基本的に静的だが
モジュールローダーでは必要な時だけ、モジュールを読み込むことができる(動的)。
-
依存関係の管理
プログラム内で使用される各モジュール間の依存関係を解決します。 -
遅延ローディング
必要なモジュールのみを適宜ロードし、リソースの効率的な使用を促進します。 -
名前空間の管理
グローバル名前空間の汚染を防ぎ、モジュール間の名前の衝突を回避します。
静的読み込みと動的読み込み
パッケージマネージャーではimport
(ESM)やrequire
(CommonJS)を使ってモジュールを静的にロードする。この方法では、アプリケーションの起動時にすべての必要なモジュールが読み込まれます。
動的読み込みでは、実際にモジュールが呼ばれたときに初めてモジュールを読み込むため、無駄なパッケージを読み込まずに済む。
ECMAScript2020では動的読み込みの仕組みが標準でJavaScriptに導入された
// 静的ロードでは、アプリケーションを起動する際にモジュールが読み込まれる。
// モジュールは実際にそのコード部分が呼ばれる前に事前に読み込まれる。
import 'sample' from 'Sample'
const sampleInstance = new Sample()
// 動的ロードでは、アプリケーションを起動する際ではなく
// アプリケーションを起動して実際にそのコードが呼ばれた際に、モジュールが読み込まれる
import('Sample')
.then(sample => {
// 動的に読み込まれたsampleクラス
const sampleInstance= new sample();
});
モジュールローダの必要性
従来のモジュールローダー(RequireJS、SystemJS)は、モジュール依存関係の管理や名前空間の衝突回避など、多様な機能を提供してきた。しかし、動的読み込みの標準対応により、JavaScriptのネイティブな機能だけで多くのこれらの機能が実現できるようになった。
モジュールローダの役割は減少しているが、複雑なモジュール間の依存関係があるような場面では、モジュールローダーは依然として必要である。
モジュールローダが必要となる例
例えば、moduleAがmoduleBに依存し、moduleBがmoduleCに依存しているとした場合、
モジュールローダを使わないと、以下のように順々にモジュールをロードする必要がある。
// 動的importを使用した例
import('moduleC.js').then(moduleC => {
import('moduleB.js').then(moduleB => {
import('moduleA.js').then(moduleA => {
// ここでmoduleAを使用
});
});
});
モジュールローダを利用するとmoduleAの依存関係を自動的に解決し、適切な順序でmoduleBとmoduleCをロードしてくれる
// SystemJSを使用した例
SystemJS.import('moduleA.js').then(moduleA => {
// moduleAを使用
});
RequireJS
JavaScriptのためのモジュールローダーで、AMD(Asynchronous Module Definition)に対応している。
このツールは、非同期的にJavaScriptファイルをロードし、依存関係を管理する。
// RequireJSを使用したモジュールの定義例
define(['dependency'], function(dependency) {
// モジュールの内容
});
SystemJS
ESモジュール、CommonJS、AMDなど、さまざまなモジュール形式に対応した汎用モジュールローダー。ブラウザやNode.js環境で幅広く使用される。
single-spaなどのマイクロフロントエンドフレームワークで使用が推奨されている。
// SystemJSを使用したモジュールのインポート例
System.import('module').then(function(module) {
// モジュールの使用
});
typescript
Javasciprt に型の概念を導入したプログラミング言語。
typescriptはそのままだと、ブラウザで動かないので、Javasciprtに変換する必要がある。
変換の際に、どのモジュール形式に変換するかはtsconfig.jsonで指定できる。
webpackでtypescriptを読み込む
ts-loaderプラグインを導入する。
ts-loadeプラグインは、Webpackがコードを読み込む前にtsconfig.jsonの設定に基づいて
TypeScriptのコードをJavaScriptに変換する。
Webpackを使用する場合は、tsconfig.jsonで 「Commonjs」 や 「ESNext」 など、Webpackが読み込めるモジュール形式を設定する必要がある。
-
コンパイル
ts-loaderプラグインはtsconfig.jsonで定義されたコンパイラオプションに従って、TypeScriptをJavaScriptにトランスパイルする。 -
読み込み
WebpackはこれらのトランスパイルされたJavaScriptファイルを読み込み、バンドルプロセスを実行する。
viteでtypescriptを読み込む
ViteはTypeScriptを標準でサポートしており、tsconfig.jsonファイルの設定に従ってTypeScriptコードをJavaScriptに変換する。
ViteはこれらのトランスパイルされたJavaScriptファイルを読み込む。
Viteを使用する場合は、tsconfig.jsonにおいて 「ESNext」 や 「ES6」 などのESモジュール互換の形式に設定する。
Import map
外部から読み込むJavaScriptモジュールをURLではなく、エイリアス(例 : vue, axios)で
読み込めるような仕組みを提供するフレームワーク。
JSON形式で、識別子とJavaScriptモジュールURLを1対1対応させて定義する。
{
"imports": {
"vue": "https://unpkg.com/vue@next",
"axios": "https://unpkg.com/axios/dist/axios.min.js",
}
}
Import mpaの使用例
- index.htmlに
<script type="importmap">
を定義し、外部またはインラインからjsonを読み込む。 - Import mapで指定したエイリアスを指定して、モジュールを読み込む
index.html
// 外部からimport_map,jsonを読み込み
<script type="importmap" src-"path/to/import_map.json" />
// インライン
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue@next",
"axios": "https://unpkg.com/axios/dist/axios.min.js",
}
}
</script>
<script src='./main.ts' />
main.ts
// 静的読み込み
import Vue from 'vue';
const app = Vue.createApp({
// ...
});
// 動的読み込み
const axios = await import('axios')
axios.get('https://example.com').then(response => {
console.log(response.data);
});
SystemJSでのImport mpaの使用例
動的モジュールローダーであるSystemJSでImport Mapを用いる際は、下記のように<script type="systemjs-importmap">
を指定する。
index.html
// 外部からimport_map,jsonを読み込み
<script type="systemjs-importmap" src-"path/to/import_map.json" />
// インライン
<script type="systemjs-importmap">
{
"imports": {
"vue": "https://unpkg.com/vue@next",
"axios": "https://unpkg.com/axios/dist/axios.min.js",
}
}
</script>
<script src='./main.ts' />
main.ts
// SystemJSによる動的読み込み
const axios = await System.import('axios')
axios.get('https://example.com').then(response => {
console.log(response.data);
});
Eslint
JavaScriptのコードを静的に解析してくれるツール。
JavaSciprt/TypeScriptをモジュールバンドラーで変換する前にコードをチェックするために用いる。
.eslintrc.jsなどに、解析のルールを定義する。
npm install --save-dev eslint
yarnv add --dev eslint
pnpm add -save-dev eslint
prettier
コードを整形してフォーマットを統一してくれるツール。
prettier.config.jsに整形のルールを定義する。
npm install --save-dev --save-exact prettier
yarn add --dev --exact prettier
pnpm add --save-dev --save-exact prettier
babel
JavaScriptのコードを新しいフォーマットから古いフォーマットに変換するトランスパイラ。
TypeScriptからJavaScriptへの変換も可能。
webpackではbabel-loaderを用いて、babelを使用できる。
viteではbableではなくSWC(Rutstベースのトランスパイラ) を用いて、コードをトランスパイする。
npm install --save-dev @babel/preset-react
yarn add --dev @babel/preset-react
pnpm add --save-dev @babel/preset-react