Quantcast
Channel: JavaScript Advent Calendarの記事 - Qiita
Viewing all articles
Browse latest Browse all 25

Babel 7 の主な変更点まとめ

$
0
0

2019/06/21 追記

記事内容を Babel 7.4.0 に対応したものに更新しました。

主に追記した箇所は以下に関してです。

はじめに

今更ですが、Babel 7 の主な変更点をまとめた備忘録です。

ほとんどの内容は公式ドキュメントである「Upgrade to Babel 7 」と「Usage Guide」を参考にしているため、既にこちらを読んで理解した方々には不要な記事だと思います。

また、普段 Babel と webpack を併用しているため、Babel CLI や.babelrcなどに関しては触れません。

記事本文でも紹介しますが、Babel 7 にマイグレーションしたサンプル(webpack と併用)は GitHub に置いてあります。

hira777/webpack-with-babel7

本記事での「プロポーザル」の定義

以下のような意味があるが

  • ECMAScript の新たな仕様として提案された機能
  • ECMAScript の新たな仕様として提案され、策定中の機能
  • ECMAScript の新たな仕様として追加する機能の提案書

本記事では「(ECMAScript の新たな仕様として提案され、)策定中の機能」ぐらいな認識で問題ない。

そのため、以下の言葉の意味は大体同じ。

  • 「Stage 4 未満のプロポーザル」 = 「Stage 4 未満の策定中の機能」

プロポーザルや Stage などをより詳しく知りたい方は以下を参照。

目次

yearly presets は非推奨になった

以下のような yearly presets は非推奨になった(Babel 6 から非推奨だった気がするが、一応記載)。

  • babel-preset-es2015
  • babel-preset-es2016
  • babel-preset-es2017
  • babel-preset-latest

そのため preset は@babel/preset-envを利用する。

Babel 6

babel-preset-es2015を利用する場合。

npm install --save-dev babel-preset-es2015

Babel 7

npm install --save-dev @babel/preset-env

stage-x presets は非推奨になった

preset-stage-0などの stage-x presets は非推奨になった。

Babel 6

preset-stage-1を利用する場合。

npm install --save-dev babel-preset-stage-1

Babel 7

個別にプラグインをインストールする必要がある(以下は 2018-10-11 時点での Stege 1 のプラグイン)。

npm install --save-dev @babel/plugin-proposal-export-default-from @babel/plugin-proposal-logical-assignment-operators @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-pipeline-operator @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-do-expressions

babel-upgrade を利用して Babel 7 へのアップグレードを自動で行える(環境によっては完全にアップグレードできるわけではない)

babel-upgradeを利用すれば、Babel 7 へのアップグレードに伴う依存関係、設定ファイルや JavaScript ファイルをに自動的に更新できる。

例えば、以下のようなpackage.jsonのある階層でnpx babel-upgrade --writeを実行すると

{
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.7.0",
    "babel-preset-stage-1": "^6.24.1"
  }
}

以下のようにpackage.jsonが更新される。

{
  "devDependencies": {
    "@babel/core": "^7.0.0",
    "@babel/plugin-proposal-class-properties": "^7.0.0",
    "@babel/plugin-proposal-decorators": "^7.0.0",
    "@babel/plugin-proposal-do-expressions": "^7.0.0",
    "@babel/plugin-proposal-export-default-from": "^7.0.0",
    "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
    "@babel/plugin-proposal-function-sent": "^7.0.0",
    "@babel/plugin-proposal-json-strings": "^7.0.0",
    "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
    "@babel/plugin-proposal-numeric-separator": "^7.0.0",
    "@babel/plugin-proposal-optional-chaining": "^7.0.0",
    "@babel/plugin-proposal-pipeline-operator": "^7.0.0",
    "@babel/plugin-proposal-throw-expressions": "^7.0.0",
    "@babel/plugin-syntax-dynamic-import": "^7.0.0",
    "@babel/plugin-syntax-import-meta": "^7.0.0",
    "@babel/preset-env": "^7.0.0",
    "babel-loader": "^8.0.0"
  }
}

現時点では、webpack.config.jsなどの設定ファイルは更新されないため、開発環境によっては babel-upgrade を利用してもアップグレードを完全に行えない可能性もあるため注意。

webpack.config.jsの更新は今後実装予定である。他にどのような機能を実装する予定なのかはこちらを参照。

babylon がリネームされた

Babel で利用されている JavaScript のパーサーであるbabylon@babel/parserにリネームされた。

多くのパッケージの提供が Scoped Packages に変更された

多くのパッケージは scoped packages として提供されるようになった。

つまり、@babel/preset-envのように@babel/(スコープ)がついて提供されるようになった。

そのため、様々なパッケージがリネームされており、babel-cli -> @babel/cliのようにbabel-がついていたパッケージは、基本的にbabel-@babel/に置き換わっている。

自分が利用しているパッケージがリネームされていないか確認した方が良い(大体リネームされているらしい)。

リネームに伴い、コンフィグの記述も変更

以下はその例

Babel 6

module.exports = {
  presets: ['preset-env'],
  plugins: ['plugin-transform-arrow-functions']
};

以下のようにショートハンド(preset-plugin-の短縮)も利用できた。

module.exports = {
  presets: ['env'],
  plugins: ['transform-arrow-functions']
};

Babel 7

module.exports = {
  presets: ['@babel/preset-env'],
  plugins: ['@babel/plugin-transform-arrow-functions']
};

ショートハンド(preset-plugin-の短縮)は引き続き利用できるが、@babel/の指定は必須。

module.exports = {
  presets: ['@babel/env'],
  plugins: ['@babel/transform-arrow-functions']
};

ショートハンドは便利だが、パッケージ名をフルで書いた方が何のパッケージを利用しているのかを確実且つ即座に理解できる。

全員がショートハンドを利用できることを知っているわけではないため、チーム開発などの時は認識合わせをした方が良いかもしれない。

Stage 4 未満のプロポーザルのパッケージがリネームされた

Stage 4 未満のプロポーザルのパッケージは以下のように-proposal-が付いたものにリネームされた。

  • @babel/plugin-transform-function-bind(Stage 0) -> @babel/plugin-proposal-function-bind
  • @babel/plugin-transform-class-properties(Stage 3) -> @babel/plugin-proposal-class-properties

プロポーザルの Stage が 4 に移行したら、パッケージ名がリネームされるので留意しておく。

(と書いておきながら、Stage 4 であるObject Rest/Spread Properties@babel/plugin-proposal-object-rest-spreadで提供されているので、ドキュメントを読み間違えているかもしれない。)

パッケージ名から ECMASCript の Edition は削除された

いくつかのプラグインは名前に-es3-、または-es2015-が付いていたが、以下のように削除された。

  • @babel/plugin-transform-es2015-classes -> @babel/plugin-transform-classes

@babel/polyfill が非推奨になった(Babel 7.4.0 から)

以下は@babel/polyfillのページから抜粋したもの。

As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features) and regenerator-runtime/runtime (needed to use transpiled generator functions):

そのため、@babel/polyfillの代わりに、core-jsregenerator-runtime/runtimeを利用する。

Babel 7

import '@babel/polyfill';

Babel 7.4.0

import 'core-js/stable';
import 'regenerator-runtime/runtime';

core-js@babel/polyfillも利用している polyfill。利用が推奨されているのは v3 で@babel/polyfillが利用しているのは v2。

regenerator-runtimeは async/await を利用するために必要な polyfill。

@babel/preset-env の useBuiltIns を利用して、core-js@3 から必要な polyfill のみを import できるようになった(Babel 7.4.0 から)

以下のようにcore-jsなどを import すると、利用していない不要な polyfill も import されてしまい、トランスパイル後のファイルサイズが非常に大きくなってしまう。

import 'core-js/stable';
import 'regenerator-runtime/runtime';

Babel 7.4.0 では@babel/preset-envuseBuiltInsという設定を利用し、必要な polyfill のみを import できる。

以下はそのサンプル(webpack と併用)。

hira777/webpack-with-babel7

サンプルの babel.config.js で指定している Babel の設定は以下の通り。

babel.config.js
module.exports = function(api) {
  api.cache(true);

  const presets = [
    [
      // プリセットに @babel/preset-env を指定する
      '@babel/preset-env',
      {
        // サポートするブラウザ、この設定に応じて、必要な polyfill のみが import される
        targets: {
          edge: '13'
        },
        // 必要な polyfill のみを import させたい場合、'usage'を指定する(必須)
        useBuiltIns: 'usage',
        // polyfill を利用する core-js のバージョンを指定する(指定しないとバージョン2が利用され警告が出力される)
        corejs: 3,
        // trueにすると利用しているポリフィルなどの情報が出力される
        // polyfill が import されているかどうかを確認するためのものなので必須ではない
        debug: true
      }
    ]
  ];

  return {
    presets
  };
};

targetsで指定したブラウザをサポートするために必要な polyfill が import される。useBuiltIns: 'usage'corejs: 3の指定は必須。

また、エントリーポイントである src/app.js は以下の通り。

src/app.js
[1, 2, 3].includes(2);

利用されているArray.prototype.includes()は、Edge 14 からサポートされている。

今回、targetsedge: '13'を指定しているため、Edge 13 をサポートするためにArray.prototype.includes()の polyfill が import される。

上記のように Babel の設定をすれば、import '@babel/polyfill';import 'regenerator-runtime/runtime';を記述せずに polyfill を import できるが、core-jsregenerator-runtime自体はインストールしておく必要があるので注意する。

Stage 4 未満のプロポーザルの polyfill も import する(Babel 7.4.0 から)

Babel 7.4.0 から、@babel/preset-envuseBuiltInsで、Stage 4 未満のプロポーザルの polyfill も import できるようになった。

以下のようにcorejs: { version: 3, proposals: true }を指定すれば Stage 4 未満のプロポーザルの polyfill も import される。

babel.config.js
module.exports = function(api) {
  api.cache(true);

  const presets = [
    [
      // プリセットに @babel/preset-env を指定する
      '@babel/preset-env',
      {
        // サポートするブラウザ、この設定に応じて、必要な polyfill のみが import される
        targets: {
          edge: '14'
        },
        // 必要な polyfill のみを import させたい場合、'usage'を指定する(必須)
        useBuiltIns: 'usage',
        // core-js のバージョンを指定する(指定しないとバージョン2が利用され警告が出力される)
        // corejs: 3,
        // Stage 4 未満のプロポーザルの polyfill も import される
        corejs: { version: 3, proposals: true },
        // trueにすると利用しているポリフィルなどの情報が出力される
        // polyfill が import されているかどうかを確認するためのものなので必須ではない
        debug: true
      }
    ]
  ];

  return {
    presets
  };
};

上記の状態で、以下のような Stage 2(2019/06/20 時点)のString.prototype.replaceAllを利用したコードをトランスパイルすると、自動で polyfill が import される。

const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replaceAll('+', ' ');
console.log(withSpaces); // => q=query string parameters

(補足)@babel/polyfill と core-js@3 で、「どのブラウザでどの polyfill を import する必要があるかを判別するために利用するデータ」が異なる

それぞれが利用するデータは以下の通り。

そのため、@babel/preset-env@babel/polyfillを利用した場合と@babel/preset-envcore-js@3を利用した場合で import される polyfill が異なる時がある

たとえば、targetsedge: '18'を指定して、以下のようなPromise.prototype.finally()が含まれるコードをトランスパイルすると

Promise.resolve().finally();

@babel/preset-env@babel/polyfillを利用した場合はPromise.prototype.finally()の polyfill が import されないが、@babel/preset-envcore-js@3core-js-compatが v3.1.4 の時点)を利用した場合は Promise.prototype.finally()の polyfill が import される。

そのため、「targetsの指定はそのままで@babel/preset-envで利用する polyfill を@babel/polyfillからcore-js@3に変更してトランスパイルしたら、import される polyfill が無茶苦茶変わった...!!なぜ...!?」といった状況が発生する可能性はあるが、バグではない。

なぜ core-js@3 では core-js-compat を利用しているのか?

compat-tableだと細かい点で不備があり、完全に正しいわけではないから(らしい)。

くわしくはこちらを参照。

core-js-compat は常に最新のものを利用した方が良い(かもしれない)

現時点では core-js-compat が頻繁に更新されているため、バーションが上がると polyfill の import のされ方が結構異なる気がした。

そのため、常に最新の core-js-compat を利用した方が良い(かもしれない)。

TypeScript のトランスパイルが可能になった

@babel/preset-typescriptを利用して、TypeScript のトランスパイルが可能になった(型チェックはできないため、型チェックするためには TypeScript が必要)。

※トランスパイルの仕方は別の記事に書きましたので、こちらを参考にしてください。

webpack 4 + Babel 7 + TypeScript + TypeScript EsLint + Prettier の開発環境を構築する

終わり

本記事で記載した変更点は、ざっくり分類すれば以下の通りです。

  • 様々なパッケージがリネームされた
  • @babel/polyfillの変更点
  • @babel/preset-envuseBuiltInsの利用方法

上記を理解すれば、とりあえず Babel 7 へのマイグレーションはできると思います。

変更点をより詳しく知りたい方は以下をご参照ください。


Viewing all articles
Browse latest Browse all 25

Trending Articles