Parcel 入門 ～Parcelはwebpackの代わりになるのか～

# 2018/05/08追記
記事内容をv1.1.0からv1.8.1に対応したものに更新しました。

# はじめに
Parcelというモジュールバンドラを触ってみたので、その備忘録になります。

[webpack時代の終わりとparcel時代のはじまり](https://qiita.com/bitrinjani/items/b08876e0a2618745f54a)

結論から言いますと、~~**機能がシンプルすぎて自分の実務での利用は難しいと感じました**。~~~~そのため、**2017/12/10時点（v1.1.0）ではwebpackの代わりにはならないです**。~~
2018/05/08時点でv1.8.1になり、様々な機能が追加されました。しかし現在もwebpackで利用している機能がParcelにはないため、実務での利用は難しいです。

とは言えども**設定ファイルなしでReact、Vue、ES2015などをビルドできる**ようになっているため、個人でちょっとしたコードを書いてビルドしたい時はこちらを使っていくと便利だと思いました。

そのため

- **webpackは多機能で痒いところまで手が届くモジュールバンドラ**
- **Parcelは細かい設定はできないが、ビルドがお手軽にできるモジュールバンドラ**

という認識を持って、用途に合わせて使っていけば良いと思います。

本記事の目的は以下の通りです。

- Parcelの基本的な使い方を理解する。
- webpackとどんなところが異なるのかをざっくり理解する。

解説に利用しているコードの最終形態はGitHub上にあります。

[https://github.com/hira777/parcel-tutorial](https://github.com/hira777/parcel-tutorial)

## 本記事の前提や注意点
- **v1.8.1**時点で書いた記事になります。
- Parcelの使い方は[公式ドキュメント](https://parceljs.org/getting_started.html)の内容を、より丁寧に解説したものになります。そのため、公式ドキュメントで基礎が理解できるのであればこの記事を読む必要はありません。
- webpackなどのモジュールバンドラを触ったことがある人向けの記事のため、モジュールバンドラ自体の説明は省きます。
- webpackやモジュールバンドラの基礎について学習したい方はこちらの記事（[webpack 入門](https://qiita.com/soarflat/items/28bf799f7e0335b68186)）をご覧ください。
- 本記事ではParcelのことを「モジュールバンドラ」と説明しています。他の記事や公式では「ビルドツール」や「アプリケーションバンドラ」などと説明されていますが、言葉の意味合いはあまり変わらないです。とりあえず「**複数のファイルを1つ（もしくは複数）にまとめてくれるツール**」という認識さえあれば問題ありません。

# Parcelとは
モジュールバンドラのこと。読み方はパーセル（のはず）。

- [https://github.com/parcel-bundler/parcel](https://github.com/parcel-bundler/parcel)（GitHub）
- [Parcel](https://parceljs.org/)（ドキュメント）

## Parcelの特徴（webpackとの違いは何か）
Parcelには以下のような特徴がある。

- ~~ビルド速度がwebpackと比べてかなり速い~~（webpack3時点なので、webpack4と比較すると速いかどうかは不明）
- `webpack.config.js`のような設定ファイルが必要ない（一応webpack4も設定ファイルなしでビルドできるようになった）
- JavaScript以外のファイル（HTMLなど）をエントリーポイントに指定できる

上記のようにwebpackの辛さ（設定ファイルの肥大化など）を解消しているモジュールバンドラ。

もちろん、**辛さを解消したからと言って全ての点がwebpackより優れているわけではない**。

### `webpack.config.js`のような設定ファイルが必要ない
以下のようにエントリーポイントに対してparcelを実行すれば、ファイルがバンドルされる。

```bash
parcel app.js
```

### JavaScript以外のファイル（HTMLなど）をエントリーポイントに指定できる
任意のファイルをエントリーポイントに指定できるらしいが、公式ではJavaScriptかHTMLファイルをエントリーポイントにすることはお勧めしていた。

# Parcelを利用してみる
Parcelを利用してモジュールをバンドルしたファイルを出力してみる。
以下は出力までのイメージ図。

![parcel-image.png](https://qiita-image-store.s3.amazonaws.com/0/69667/19ce2192-4314-a1d1-0b8a-fe641c94e18b.png)

webpackの場合、cssや画像なども1つのJavaScriptファイルにまとめられて出力される。
一方でParcelの場合、依存関係を解決したファイルがそれぞれ出力される。
そのため、今回は4つのファイルが出力される。

## Parcelのインストール
npmか、yarnでインストールできる。

```
npm install -g parcel-bundler
```

```bash
yarn global add  parcel-bundler
```

## ディレクトリ構成
今回Parcelを利用するディレクトリ構成は以下を前提とする。

```
.
├── package.json
├── public
└── src
    ├── scss
    │   └── style.scss
    ├── images
    │   └── parcel.png
    ├── index.html
    └── js
        ├── app.js
        └── modules
            └── add.js
```

### 必要なパッケージのインストール
パッケージをローカルインストールするため、`package.json`は以下のどちらかのコマンドで生成する。今回はParcelが必要なパッケージ（scssをcssに変換するためのnode-sass）を自動でインストールしてくれるため`package.json`を生成しておくだけで良い

```bash
npm init
```

```bash
yarn init
```

## 各ファイルの詳細

### `index.html`（エントリーポイント）
エントリーポイント。このファイルをエントリーポイントとして`parcel`コマンドを実行すると読み込んでいる`app.js`に対してもビルドが実行される。

```html:src/index.html
<html>
<head>
  <meta charset="utf-8">
  <title>Parcel tutorial</title>
</head>
<body>
  <h1>Parcel</h1>
  <div class="parcel"></div>
  <script src="js/app.js"></script>
</body>
</html>
```

### `app.js`
`add.js`を読みこんで数値を計算したり、`style.css`を読み込んでHTMLにスタイル適用しているメインのJavaScriptファイル（webpackでいうエントリーポイント）。

**ES2015で記述されているが、ParcelがBabelのトランスパイルを自動でしてくれる。**

```js:src/js/app.js
import '../scss/style.scss';
import add from './modules/add';

const number1 = 400;
const number2 = 600;
const total = add(number1, number2);

console.log(total);
```

### `add.js`
引数の`number1`と`number2`を合算して返すモジュール。

```js:src/js/modules/add.js
export default function add(number1, number2) {
  return number1 + number2;
}
```

### `style.scss`
`index.html`に適用するスタイルが記述されたモジュール。

```scss:src/scss/style.scss
$baseColor: #000;

body {
  background: $baseColor;

  h1 {
    margin: 0;
    padding: 0;
    color: #fff;
    text-align: center;
  }

  .parcel {
    width: 400px;
    height: 374px;
    margin: 0 auto;
    background: url('../images/parcel.png');
  }
}
```

### `parcel.png`
`style.scss`から読み込んでいる画像（モジュール）。

## `parcel`コマンドでバンドルされたファイルを出力
上記構成の`package.json`が存在する階層で`parcel`コマンドを実行すれば、バンドルされたファイルが出力される。

デフォルトだと、バンドルしたファイルは`dist`ディレクトリに出力されるが、今回は`public`ディレクトリに出力させたいため、以下のようにオプションをつけて実行する。

```
parcel src/index.html -d public
```

実行すると、以下のように`public`ディレクトリにバンドルされたファイルと`.cache`ディレクトリにキャッシュファイルが出力される。

```
.
├── .babelrc
├── .cache
│   └── fd1fb8126031fa8d090710737434e806.json
├── package.json
├── public
│   ├── dddf7ef8a771ea836c5c4ad7d53dfb78.css
│   ├── dddf7ef8a771ea836c5c4ad7d53dfb78.js
│   ├── e55a1d909b7adad3efe4170b8bfee1d8.png
│   └── index.html
└── src
    ├── scss
    │   └── style.scss
    ├── images
    │   └── parcel.png
    ├── index.html
    └── js
        ├── app.js
        └── modules
            └── add.js
```

### バンドルされたファイルを確認する
出力された`index.html`は以下の通り。

```html:public/index.html
<html>
<head>
  <meta charset="utf-8">
  <title>Parcel tutorial</title>
<link rel="stylesheet" href="/public/dddf7ef8a771ea836c5c4ad7d53dfb78.css"></head>
<body>
  <h1>Parcel</h1>
  <div class="parcel"></div>
  <script src="/public/dddf7ef8a771ea836c5c4ad7d53dfb78.js"></script>
</body>
</html>
```

同じく`public`ディレクトリに出力された`dddf7ef8a771ea836c5c4ad7d53dfb78.css`を読み込む記述が追加されている。
また、`dddf7ef8a771ea836c5c4ad7d53dfb78.js`を読み込んでおり、依存関係を解決していることがわかる。

### バンドルされたファイル（ページ）をブラウザで確認する
`parcel`コマンド実行時にサーバーも起動するため、[http://localhost:1234/](http://localhost:1234/)にアクセスしてバンドル後の`index.html`を確認できる。

![parcel2.jpg](https://qiita-image-store.s3.amazonaws.com/0/69667/cf608999-6be3-cd8a-a52c-ab8cf011fb15.jpeg)

コンソールは表示され、スタイルも適用され、画像も読み込まれているため、正常に動作していることがわかる。

また、ファイルはwatchされているため、以下のようにファイルの更新をすればリロードされる。

![watch.gif](https://qiita-image-store.s3.amazonaws.com/0/69667/3eca95c7-0ff0-fbb4-c4ce-289f987f9b8e.gif)

サーバーを起動せずにファイルだけwatchしたい場合は以下のようにコマンドを実行する。

```bash
parcel watch src/index.html -d public
```

# webpackと比べてみてParcelはどうなのか（webpackの代わりになるのか）
記事の冒頭でも記載したが、Parcelはwebpackのような細かい設定ができないため、webpackの代わりにはならない。具体的には以下の通り。

- 出力先や出力するファイル名を指定できない
- ~~複数のhtml（エントリーポイント）から読み込む共通のバンドルファイルを出力できない~~
- webpackの`optimization.splitChunks`や`externals`などに該当する機能がParcelにはない

## 出力先や出力するファイル名を指定できない
上記の例のように、依存関係を解決したファイルは全て同じディレクトリ（今回の例だと`public`ディレクトリ）に出力される。
「画像やcssだけは別のディレクトリに出力する」などができない。
また、出力するファイル名も指定できない。

## 複数のhtml（エントリーポイント）から読み込む共通のバンドルファイルを出力できない
~~`index.html`、`about.html`、`page.html`などの複数のhtml（エントリーポイント）から読み込む共通のバンドルファイルを出力できない。~~
~~そのため、htmlをエントリーポイントとする場合、処理は共通でもエントリーポイントの数に応じたバンドルファイルを出力する必要がある。~~

**2018/12/22 追記**

申し訳ありません。こちら誤りでした。

以下のリポジトリで検証しましたが、記事を執筆した時点でのバージョンでもできました。

https://github.com/hira777/parcel-tutorial/tree/1.1.0

また、@shibukawaさんからコメントをいただいた通り、[Code Splitting](https://parceljs.org/code_splitting.html)という機能を利用すれば、共通処理だけのバンドルファイルを出力することも可能です。

こちらもサンプルを置きましたのでご参照ください。

https://github.com/hira777/parcel-tutorial/tree/code-splitting

## webpackの`optimization.splitChunks`や`externals`などに該当する機能がParcelにはない
[optimization.splitChunks](https://qiita.com/soarflat/items/1b5aa7163c087a91877d)や[externals](https://webpack.js.org/configuration/externals/)など、実務で利用しているwebpackの様々な機能がParcelにはない。

# 終わり
冒頭でも記載しましたが、Parcel自体は便利なビルドツールですので、用途に合わせて使っていけば良いと思います。

初めて記事を書いたv1.1.0からv1.8.1に至るまでに、様々な機能が追加されており、将来はより使いやすくなっている可能性もあります。

興味がある方は

- [issues](https://github.com/parcel-bundler/parcel/issues)や[Pull Request](https://github.com/parcel-bundler/parcel/pulls)
- [Twitter（@parceljs）](https://twitter.com/parceljs?lang=en)
- [devongovett（Parcel開発者のMedium）](https://medium.com/@devongovett)

などを追うことをお勧めします。

ということで、webpackを学習しても無駄ではないので、webpackを学習したい方はこちらの記事をどうぞ。

[webpack 4入門](https://qiita.com/soarflat/items/28bf799f7e0335b68186#commonschunkplugin)

## お知らせ

Udemy で webpack の講座を公開したり、Kindle で技術書を出版しています。

Udemy:
[webpack 最速入門](https://www.udemy.com/course/practical-webpack/?couponCode=B6D5AE3897554CBBDAD7)（**~~10,800 円~~ -> 2,000 円**）

Kindle（Kindle Unlimited だったら無料）:
[React Hooks 入門](https://www.amazon.co.jp/gp/product/B08JTXQ2M6)（500 円）

興味を持ってくださった方はご購入いただけると大変嬉しいです。よろしくお願いいたします。