webpackとBabelの基本を理解する(2) ―Babel編―

独学の内容をまとめたものです。誤りがございましたら、ご連絡いただけると幸いです。

**リンク**
1. [webpackとBabelの基本を理解する(1) ―webpack編―](https://qiita.com/koedamon/items/3e64612d22f3473f36a4)
2. webpackとBabelの基本を理解する(2) ―Babel編―（本記事）
3. [webpackとBabelの基本を理解する(3) ―webpackとBabel編―](https://qiita.com/koedamon/items/6cf2201be78c3d79516d)
4. [webpackとBabelの基本を理解する(4) ―React編―](https://qiita.com/koedamon/items/8fb75a7c001da40c7bea)
5. [webpackとBabelの基本を理解する(5) ―Sass編―](https://qiita.com/koedamon/items/a5be9456e0591db4d1f4)

# 概要
## この記事の概要

- 目的
  - フロントエンドの環境構築に利用されるツールへの理解を深める
- 本記事のゴール
  - Babelで最新のJS構文を環境に応じたバージョンに変換する方法を知る
- 対象者
  - WEBフロント担当者
  - HTML,CSS,JavaScript(es2015含む)の基本的な構文を理解している人
  - npmの利用方法を理解している人
- 環境・バージョン
  - Windows10
  - Node.js(推奨版) 10.15.01
  - npm 6.4.1
  - babel-core 7.3.4
  - babel-cli 7.2.3 
  - preset-env 7.3.4

## Babel
JavaScriptのコンパイラです。トランスパイラとも呼ばれています。公式サイトはcompilerと呼んでいます。
主に、es2015以降の新しい構文をes5の構文に変換するのに利用されています。もちろん、設定によってはes2018→es2015といった記述方法も可能です。その他にも、ReactのJSXやTypeScriptの変換にも使われます。

[Babel](https://babeljs.io/)

# Babelの導入
## インストール
npmで三つのパッケージをインストールします。

```console
$ npm install @babel/core @babel/cli @babel/preset-env --save-dev
```

- @babel/core: Babel本体
- @babel/cli: コマンドライン操作用
- @babel/preset-env: 変換内容設定用

## .babelrcによる設定
Babelはそのままでは何も変換してはくれないので、どのように変換するのかを事前に設定します。作業フォルダのルートに`.babelrc`を作成し、JSON形式で設定情報を追記していきます。

```
sample/
  ├ src/
  │   ├ component/
  │   │    └ test.js
  │   └ index.js 
  ├ dist/
  ├ webpack.config.js
  └ .babelrc
```

ひとまず、`.babelrc`に下記設定情報を書きます。（解説は後述）

```json
{
  "presets": ["@babel/preset-env"]
}
```

## コンパイルする
下記内容で、`before.js`を用意します。
es2015で配列に対してスプレッド演算子が導入されましたが、es2018ではオブジェクトでも利用できるようになりました。もちろんレガシーなブラウザでは使えません。

```js
/**
 * before.js
 */ 
{
  const data = {
    hoge: 'hoooooo',
    fuga: 'gaaaaaa',
    piyo: 'piiiii'
  };

  const { hoge, ...other } = data;

  console.log(hoge, other);
}
const hoge = 'mumumu';
```

そして、同フォルダ内で下記コマンドを実行します。

```console
$ npx babel before.js --out-file after.js
```

同じフォルダ内に`after.js`が作成されます。
いろいろコードが増えていますが、es2015で導入された`const`が、`var`に変換されていたり、スプレッド構文が分解されていたりします。

```js
/**
 * after.js
 */ 
"use strict";

function _objectWithoutProperties(source, excluded) {...}

function _objectWithoutPropertiesLoose(source, excluded) {...}

{
  var data = {
    hoge: 'hoooooo',
    fuga: 'gaaaaaa',
    piyo: 'piiiii'
  };

  var _hoge = data.hoge,
      other = _objectWithoutProperties(data, ["hoge"]);

  console.log(_hoge, other);
}
var hoge = 'mumumu';
```

ブロックはそのままなのですが、`var`はブロックスコープに対応していません。その為、ブロック内の変数`hoge`は、グローバル変数`hoge`と名前が衝突しないよう、アンダーバーが追加されています。

**import/export**
Babelではファイルのバンドルまでは行わない為、import/export文はNode.jsのrequire文に置き換えられるのみです。

Babelがどんな風に変換されるかは、オンラインで確認できます。
**[BABEL REPL](https://babeljs.io/repl)**

## Presets
### プリセット(Presets)の基本
プリセットは、Babelが変換処理を行う際に利用するプラグインのコレクションです。設定情報を基に、コンパイルに必要なプラグインのリストをBabel本体に渡す役割をしているそうです。2019年2月現在、公式では下記4つのプリセットが用意されています。その他にもいろいろ開発されています。

- [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env): ECMAScript用
- [@babel/preset-flow](https://babeljs.io/docs/en/babel-preset-flow): Flow用
- [@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react): React用
- [@babel/preset-typescript](https://babeljs.io/docs/en/babel-preset-typescript): Typescript用

利用したいプリセットをインストールした後、配列で名前を指定します。後ろから順に適用されます。下記のケースでは、typescript → react → env の順で実行されます。

```json
{
  "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"]
}
```

各プリセットにて詳細な設定をしたい場合は、配列で囲みます。0番目に名前、1番目に設定情報です。

```json
{
  "presets": [
    ["@babel/preset-env", {<@babel/preset-envの設定値>}],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}
```

### @babel/preset-env
`@babel/preset-env`は、出力したいECMAScriptのバージョンを指定するためのプリセットです。基本的には、サポートしたい範囲（ブラウザバージョンやブラウザシェア、サポートの可否など）を指定します。指定内容に応じて適切なバージョンのJSに変換されるようにしてくれます。特に何も指定しない場合は、一律es5の構文に変換されます。
下記の設定は、Chrome40以上・iOS10以上をサポート対象とした場合です。es5で出力されました。

```json
{
  "presets": [
    ["@babel/preset-env", {
        "targets": [
          "chrome 40",
          "iOS 10"
        ]
      }
    ]
  ]
}
```

`cover 80% in JP`は、ブラウザシェア率を基に日本で80%のユーザをカバーするという指定です。es5で出力されました。モダンブラウザの新しいバージョンを指定すると、es2015以降のバージョンで出力されます。

環境の指定方法は多岐にわたるので、こちらもあわせてご確認ください。
[Browserslist](https://github.com/browserslist/browserslist)

### PolyfillとuseBuiltInsオプション
初期設定では、`@babel/preset-env`が行うのは構文の変換のみです。`Promise`や`generator`など、新たに追加された機能の変換には対応していません。オプションで、`useBuiltIns`に`usage`を指定すると、必要に応じてPolyfill(代替コード)に変換してくれます。（初期値はfalse）

```json
{
  "presets": [
    ["@babel/preset-env", {
        "targets": [...],
        "useBuiltIns": "usage"
      }
    ]
  ]
}
```

### @babel/polyfill
2019年2月現在、「useBuiltIns:'usage'」の項目には「experimental(実験的)」とあります。お使いの環境でうまく動かなかった場合は、`@babel/polyfill`と「`useBuiltIns:'entry'`」の合わせ業もあります。

`@babel/polyfill`をインストール

```console
$ npm install @babel/polyfill --save
```

アプリ（サイト）内の**一箇所にだけ**、下記インポート文を記述します。複数個所に記述するとエラーとなるそうです。この記述は、必要に応じて個別のpolyfillのインポート文に書き換えられます。


```js
// 記述する内容
import "@babel/polyfill";


// 最終的に個別のimport文に変換される
import "core-js/modules/es7.string.pad-start";
import "core-js/modules/es7.string.pad-end";

```

[ガイド: @babel/preset-env](https://babeljs.io/docs/en/babel-preset-env)

# 設定ファイルをpackage.jsonにまとめる
設定ファイルを増やしたくない場合は、`package.json`にまとめることも可能です。`babel`という項目を追加します。記述方法は`.babelrc`の場合と同様です。また前回のwebpack同様、コマンドを`scripts`に登録して利用することも可能です。

```json
{
  "name": "sample",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "babel ./src/index.js --out-file ./dist/index.js"
  },
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.4",
    "@babel/preset-env": "^7.3.4",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.2.3"
  },
  "babel": {
    "presets": ["@babel/preset-env", {
        "targets": [...],
        "useBuiltIns": "usage"
      }
    ]
  }
}
```

以上がBabelの基本（の一部）となります。設定方法一つとっても手段が多種多様で迷います。。。
次回は、Babelでバベりつつwebpackでバンドっていきたいと思います。

# 参考情報
[今更のバベる。 Babel 7を試してみたメモ](https://chaika.hatenablog.com/entry/2018/11/21/150000)
[@babel/preset-envのuseBuiltInsを使ってpolyfillする](https://qiita.com/shisama/items/88080011bbc69e3e620b)
[.babelrcと.eslintはpackage.jsonに](https://qiita.com/sakana/items/c64abe5db3f9ea76dbf2)