JavaScript
Parcelは、ESモジュールやCommonJSを含むJavaScript、多くの種類の依存関係、ブラウザターゲットの自動トランスパイル、JSXとTypeScriptのサポートなど、多くの機能をファーストクラスでサポートしています。
モジュール
#モジュールを使用すると、コードを異なるファイルに分割し、値のインポートとエクスポートによってそれらの間で機能を共有できます。これにより、コードを独立した部分に構造化し、それらの間で通信するための明確なインターフェースを持つことができます。
Parcelは、ESモジュールとCommonJS構文の両方をサポートしています。モジュール指定子は、依存関係解決の説明に従って解決されます。
ESモジュール
#ESモジュール構文は、JavaScriptでファイル間で値をインポートおよびエクスポートするための標準的な方法です。新しいコードでは、CommonJSよりも優先する必要があります。import
ステートメントを使用して、別のファイルのexport
ステートメントによって公開された値を参照できます。
この例では、math.js
からmultiply
関数をインポートし、それを使用してsquare
関数を実装しています。
import {multiply} from './math.js';
export function square(x) {
return multiply(x, x);
}
export function multiply(a, b) {
return a * b;
}
ESモジュールの詳細については、MDNのドキュメントを参照してください。
CommonJS
#CommonJSは、Nodeでサポートされ、npm上のライブラリで広く使用されているレガシーモジュールシステムです。新しいコードを作成する場合は、上記のように、一般的にESモジュール構文を優先する必要があります。CommonJSは、別のファイルによって公開されたexports
オブジェクトにアクセスするために使用できるrequire
関数を提供します。
この例では、require
を使用してmath.js
モジュールをロードし、そのexportsオブジェクトのmultiply
関数を使用してsquare
関数を実装しています。
let math = require('./math');
exports.square = function(x) {
return math.multiply(x, x);
};
exports.multiply = function(a, b) {
return a * b;
};
require
とexports
に加えて、Parcelはmodule.exports
、__dirname
および__filename
モジュールグローバル、および環境変数へのアクセス用のprocess.env
もサポートしています。詳細については、Nodeエミュレーションのドキュメントを参照してください。
CommonJSの詳細については、Node.jsドキュメントを参照してください。
動的インポート
#ESモジュールのimport
ステートメントとCommonJSのrequire
関数の両方は、依存関係を同期的にロードします。つまり、モジュールは、ネットワークリクエストを待たずにすぐに参照できます。通常、同期的にインポートされるモジュールは、親と同じファイルにバンドルされるか、既にロードされている別のバンドルで参照されます。
動的import()
関数を使用すると、依存関係を非同期的にロードできます。これにより、オンデマンドでコードを遅延ロードでき、アプリの初期ページロードのファイルサイズを削減するのに適した手法です。import()
は、依存関係がロードされたときに通知するPromiseを返します。
import('./pages/about').then(function(page) {
page.render()
});
動的インポートの使用方法の詳細については、コード分割のドキュメントを参照してください。
クラシックスクリプト
#モジュール(ESモジュールとCommonJSの両方)の利点の1つは、機能が分離されていることです。つまり、トップレベルスコープで宣言された変数は、エクスポートされない限り、モジュールの外部からアクセスできません。これは一般的に優れた特性ですが、JavaScriptのモジュールは比較的新しく、分離されることを想定していないレガシーライブラリやスクリプトが多数存在します。
クラシックスクリプトは、HTMLの従来の<script>
タグ(type="module"
なし)やワーカーなどの他の手段を介してロードされるJavaScriptファイルです。クラシックスクリプトは、トップレベルスコープの変数を、異なるスクリプト間でもアクセスできるグローバル変数として扱います。
たとえば、jQueryのようなライブラリをロードすると、$
変数がページ上の他のスクリプトに対してグローバルに利用可能になります。jQueryがモジュールとしてロードされた場合、手動でwindow
オブジェクトのプロパティとして割り当てられない限り、$
にアクセスすることはできません。
<script src="jquery.js"></script>
<script>
// The $ variable is now available globally.
$('.banner').show();
</script>
さらに、クラシックスクリプトは、ESモジュールまたはCommonJSを介した同期インポートまたはエクスポートをサポートしておらず、Nodeエミュレーションは無効になっています。ただし、動的import()
は、クラシックスクリプト内からモジュールをロードするためにサポートされています。
Parcelは、クラシックスクリプトとモジュールのブラウザの動作に一致します。コード内でインポートまたはエクスポートを使用する場合は、HTMLファイルからJavaScriptを参照するために<script type="module">
を使用する必要があります。ワーカーの場合は、{type: 'module'}
オプションを使用します(下記参照)。これが欠落している場合、以下の診断のようなものが表示されます。
import.meta
#import.meta
オブジェクトには、参照されているモジュールに関する情報が含まれています。Parcelは現在、モジュールのファイルシステム上のfile://
URLを含む単一のプロパティimport.meta.url
をサポートしています。このURLは、ビルドシステムの詳細を公開しないように、プロジェクトルート(たとえば、Gitが初期化されたディレクトリ)を基準にしています。
console.log(import.meta);
// => {url: 'file:///src/App.js'}
console.log(import.meta.url);
// => 'file:///src/App.js'
URL依存関係
#URL
コンストラクターを使用すると、JavaScriptファイルから画像やビデオなどのJavaScript以外の資産を参照できます。これらは、ベースURLパラメーターとしてimport.meta.url
を使用して、モジュールを基準に解決されます。認識されるためには、最初の引数は文字列リテラルである必要があります(変数や式ではありません)。
この例では、JavaScriptファイルと同じディレクトリにあるhero.jpg
という名前の画像を参照し、それから<img>
要素を作成しています。
let img = document.createElement('img');
img.src = new URL('hero.jpg', import.meta.url);
document.body.appendChild(img);
Parcelは、URL依存関係によって参照されるファイルを、他の依存関係と同様に処理します。たとえば、画像は画像トランスフォーマーによって処理され、クエリパラメーターを使用して、サイズ変更と変換のオプションを指定できます。特定のファイルタイプに対してトランスフォーマーが構成されていない場合、ファイルは変更されずにdistディレクトリにコピーされます。結果のファイル名には、ブラウザでの長期的なキャッシュ可能性のためにコンテンツハッシュも含まれます。
ワーカー
#Parcelには、Webワーカー、サービスワーカー、およびワークレットのサポートが組み込まれており、これにより作業を別のスレッドに移動できます。
Webワーカー
#Webワーカーは、最も一般的なタイプのワーカーです。これにより、ユーザーインターフェースをブロックしないように、バックグラウンドスレッドで任意のCPU負荷の高い作業を実行できます。ワーカーは、Worker
コンストラクターを使用して作成し、上記のようにURL
コンストラクターを使用して別のJavaScriptファイルを参照します。
ワーカーでESモジュールまたはCommonJS構文を使用するには、上記のクラシックスクリプトで説明したように、type: 'module'
オプションを使用します。Parcelは、ターゲットと現在のブラウザのサポートに応じて、必要に応じてワーカーを非モジュールワーカーにコンパイルします。
new Worker(
new URL('worker.js', import.meta.url),
{type: 'module'}
);
Parcelは、異なるブラウザウィンドウまたはiframeでアクセスできるワーカーを作成するSharedWorker
コンストラクターもサポートしています。これは、Worker
について上記で説明したのと同じオプションをサポートしています。
Webワーカーの詳細については、MDNのドキュメントを参照してください。
サービスワーカー
#サービスワーカーはバックグラウンドで実行され、オフラインキャッシング、バックグラウンド同期、プッシュ通知などの機能を提供します。これらは、navigator.serviceWorker.register
関数を使用して作成し、URL
コンストラクターを使用して別のJavaScriptファイルを参照します。
サービスワーカーでESモジュールまたはCommonJS構文を使用するには、上記のクラシックスクリプトで説明したように、type: 'module'
オプションを使用します。Parcelは、ターゲットと現在のブラウザのサポートに応じて、必要に応じてサービスワーカーを非モジュールワーカーにコンパイルします。
navigator.serviceWorker.register(
new URL('service-worker.js', import.meta.url),
{type: 'module'}
);
注: 動的な import()
はサービスワーカーではサポートされていません。
サービスワーカーは、JavaScript、CSS、画像などの静的アセットをプリキャッシュするために一般的に使用されます。@parcel/service-worker
を使用すると、サービスワーカー内からバンドル URL のリストにアクセスできます。また、マニフェストが変更されるたびに変更される version
ハッシュも提供されます。これを使用してキャッシュ名を生成できます。
まず、プロジェクトに依存関係としてインストールします。
yarn add @parcel/service-worker
この例は、サービスワーカーがインストールされたときにすべてのバンドルをプリキャッシュし、アクティブ化されたときに古いバージョンをクリーンアップする方法を示しています。
import {manifest, version} from '@parcel/service-worker';
async function install() {
const cache = await caches.open(version);
await cache.addAll(manifest);
}
addEventListener('install', e => e.waitUntil(install()));
async function activate() {
const keys = await caches.keys();
await Promise.all(
keys.map(key => key !== version && caches.delete(key))
);
}
addEventListener('activate', e => e.waitUntil(activate()));
サービスワーカーの詳細については、MDN のドキュメントと、web.dev の オフラインクックブック を参照してください。
クラシックスクリプトワーカー
#type: 'module'
オプションは、ワーカーとサービスワーカーをモジュールではなくクラシックスクリプトとして扱うために省略することもできます。importScripts
関数は、クラシックスクリプトワーカーで追加のコードをロードするために使用できます。ただし、コードは実行時にロードされ、Parcel によって処理されません。これは、importScripts
がワーカー スクリプトではなく、ページに対する相対 URL を解決するためです。したがって、importScripts
のパラメーターは、相対パスではなく、完全修飾された絶対 URL である必要があります。
次の例は、サポートされているパターンとサポートされていないパターンを示しています。
// ✅ absolute URL
importScripts('http://some-cdn.com/worker.js');
// ✅ computed URL
importScripts(location.origin + '/worker.js');
// 🚫 relative path
importScripts('worker.js');
// 🚫 absolute path
importScripts('/worker.js');
ワークレット
#Parcel は、CSS Houdini ペイントワークレット や Web オーディオワークレット など、ワークレットもサポートしています。これらを使用すると、ブラウザでのレンダリング プロセスやオーディオ処理パイプラインの低レベルな側面にフックできます。
ペイントワークレットは、次の構文を使用して自動的に検出されます
CSS.paintWorklet.addModule(
new URL('worklet.js', import.meta.url)
);
Web オーディオワークレットは静的に分析できないため、これらについては、worklet:
スキームを使用して、正しい環境用にコンパイルされたワークレットファイルへの URL をインポートできます。
import workletUrl from 'worklet:./worklet.js';
context.audioWorklet.addModule(workletUrl);
ワークレットは常にモジュールです。クラシックスクリプトワークレットはありません。これは、ワークレットには type: 'module'
オプションは必要なく、importScripts
はサポートされていないことを意味します。
さらに、動的な import()
はワークレットではサポートされていません。
トランスパイル
#Parcel には、JSX、TypeScript、および Flow のトランスパイルを標準でサポートしており、古いブラウザをサポートするために最新の JavaScript 構文をトランスパイルすることもできます。さらに、Babel がサポートされており、実験的またはカスタムの JavaScript 変換を有効にできます。
JSX
#Parcel は JSX を標準でサポートしています。JSX は、.jsx
または .tsx
ファイルで自動的に有効になるか、次のいずれかのライブラリが package.json に依存関係として定義されている場合に有効になります。
react
preact
nervjs
hyperapp
正しい JSX プラグマも、使用するライブラリに基づいて自動的に推論されます。Parcel は、インストールされている React または Preact のバージョンも自動的に検出し、サポートされている場合は 最新の JSX 変換 を有効にします。
JSX コンパイルは、tsconfig.json
または jsconfig.json
ファイルを使用して構成することもできます。これにより、ランタイム、プラグマ、その他のオプションをオーバーライドできます。詳細については、TSConfig リファレンス を参照してください。
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
Flow
#Flow のサポートは、flow-bin
がプロジェクトのルート package.json に依存関係としてリストされている場合に自動的に有効になります。コンパイルするファイルには、@flow
ディレクティブも使用する必要があります。
Parcel は現在、Babel を使用して Flow 型を削除しています。カスタム Babel 設定がある場合は、Flow プラグインを自分で追加する必要があります。詳細については、Babel を参照してください。
TypeScript
#TypeScript を参照してください。
ブラウザの互換性
#デフォルトでは、Parcel は古いブラウザ用の JavaScript 構文のトランスパイルを実行しません。これは、最新の言語機能を使用してコードを記述した場合、Parcel がそれをそのまま出力することを意味します。package.json の browserslist
フィールドを使用して、アプリでサポートするブラウザを宣言できます。このフィールドが宣言されると、Parcel は、サポートされているブラウザとの互換性を確保するために、コードを適切にトランスパイルします。
{
"browserslist": "> 0.5%, last 2 versions, not dead"
}
この構成方法と、Parcel の自動 差分バンドル のサポートの詳細については、ターゲット ドキュメントを参照してください。
デフォルトでは、Parcel はすべての標準 JavaScript 機能と、1 つ以上のブラウザで出荷された提案をサポートしています。tsconfig.json
または jsconfig.json
ファイルを使用して、将来の提案のサポートを有効にすることもできます。現在、サポートされている提案は デコレーター のみです。これは、TypeScript を通じて使用している可能性があります。
{
"compilerOptions": {
"experimentalDecorators": true
}
}
より実験的な機能は、Babel を使用して有効にできます。
Babel
#Babel は、JavaScript の一般的なトランスパイラであり、プラグインのエコシステムが豊富です。Parcel での Babel の使用は、スタンドアロンまたは他のビルドツールでの使用と同じように機能します。.babelrc
などの Babel 設定ファイルを作成すると、Parcel が自動的にそれを取得します。
Parcel は、babel.config.json
などのプロジェクト全体の設定ファイルと、.babelrc
などのファイル相対設定の両方をサポートしています。構成の詳細については、Babel ドキュメントを参照してください。
注: JavaScript Babel 設定 (例: babel.config.js
) は避ける必要があります。これらは、Parcel のキャッシュの効果を低下させ、Parcel を再起動するたびにすべての JS ファイルが再コンパイルされることを意味します。これを回避するには、代わりに JSON ベースの設定形式 (例: babel.config.json
) を使用してください。
デフォルトプリセット
#Parcel には、ブラウザターゲット (@babel/preset-env
と同等)、JSX (@babel/preset-react
と同等)、TypeScript (@babel/preset-typescript
と同等)、および Flow (@babel/preset-flow
と同等) のトランスパイルがデフォルトで含まれています。これらがプロジェクトで必要な唯一の変換である場合は、Babel がまったく必要ない場合があります。
上記のプリセットのみを含む Babel 設定が既存のプロジェクトにある場合は、それを削除できる可能性があります。Parcel の組み込みトランスパイラは Babel よりもはるかに高速であるため、これによりビルドパフォーマンスが大幅に向上する可能性があります。
カスタムプラグイン
#上記にリストされているもの以外のカスタム Babel プリセットまたはプラグインがある場合は、Babel を構成して、これらのカスタムプラグインのみを含め、標準のプリセットを省略できます。これにより、Babel の作業量を減らし、Parcel がデフォルトの変換を処理できるようにすることで、ビルドパフォーマンスが向上します。
{
"plugins": ["@emotion/babel-plugin"]
}
Parcel は Babel を使用して Flow をトランスパイルするため、カスタムプラグインとともに @babel/preset-flow
を Babel 設定に保持する必要があります。そうしないと、Babel 設定が Parcel のデフォルトを上書きします。上記にリストされている他の Babel プリセットは削除できます。
@babel/preset-env
と @babel/plugin-transform-runtime
は Parcel の ターゲット を認識していないため、差分バンドル は正しく機能しません。これにより、不要なトランスパイルが発生し、バンドルサイズが大きくなる可能性があります。さらに、@babel/preset-env
はデフォルトで ES モジュールをトランスパイルするため、スコープホイスティング で問題が発生する可能性があります。
@babel/preset-env
と @babel/plugin-transform-runtime
は、ブラウザターゲットのトランスパイルが Parcel によって自動的に処理されるため、必要ありません。ただし、何らかの理由で必要な場合は、代わりに Parcel のターゲットを認識する Parcel のラッパーを使用できます。
{
"presets": ["@parcel/babel-preset-env"],
"plugins": ["@parcel/babel-plugin-transform-runtime"]
}
他のツールとの併用
#Parcel にはデフォルトでトランスパイルが含まれていますが、Jest などのテストランナーや ESLint などのリンターなど、他のツールで Babel を使用する必要がある場合があります。この場合、Babel 設定を完全に削除できない可能性があります。代わりに Parcel に Babel 設定を無視させることができます。これにより、パフォーマンスが向上し、上記で説明した他の問題を防ぐことができます。
Parcel で Babel のトランスパイルを無効にするには、JavaScript のデフォルトの Parcel 設定をオーバーライドして、@parcel/transformer-babel
を除外します。
{
"extends": "@parcel/config-default",
"transformers": {
"*.{js,mjs,jsx,cjs,ts,tsx}": [
"@parcel/transformer-js",
"@parcel/transformer-react-refresh-wrap"
]
}
}
これにより、他のツールは Babel 設定を引き続き使用できますが、Parcel での Babel トランスパイルは無効になります。
本番環境
#本番モードでは、Parcel にはコードのファイルサイズを削減する最適化が含まれています。この仕組みの詳細については、本番環境 を参照してください。
圧縮
#本番モードでは、Parcel はバンドルのファイルサイズを削減するためにコードを自動的に圧縮します。デフォルトでは、Parcel は SWC を使用して圧縮を実行します。下位互換性のために、これは Terser 設定ファイルをサポートしています。圧縮ツールを構成するには、プロジェクトのルートディレクトリに .terserrc
ファイルを作成できます。使用可能なオプションについては、SWC ドキュメントを参照してください。
以前のバージョンの Parcel では、Terser がデフォルトの JavaScript 圧縮ツールとして使用されていました。SWC の代わりに Terser を引き続き使用するには、.parcelrc
で @parcel/optimizer-terser
プラグインを使用するように Parcel を構成できます。詳細については、プラグイン を参照してください。
ツリーシェイキング
#本番ビルドでは、Parcel は各モジュールのインポートとエクスポートを静的に分析し、使用されていないものをすべて削除します。これは「ツリーシェイキング」または「デッドコード削除」と呼ばれます。ツリーシェイキングは、静的および動的な import()
、CommonJS および ES モジュール、さらには CSS モジュールを使用した言語間でサポートされています。
また、Parcel は、各モジュールを別々の関数でラップするのではなく、可能な場合はモジュールを単一のスコープに連結します。これは「スコープホイスティング」と呼ばれます。これにより、圧縮がより効果的になり、モジュール間の参照を動的なオブジェクトルックアップではなく静的にすることで、ランタイムパフォーマンスも向上します。
ツリーシェイキングをより効果的にするためのヒントについては、スコープホイスティング ドキュメントを参照してください。