@babel/preset-envの導入に伴い@babel/polyfillを撲滅した話
この記事がとても分かりやすかったのですが、@babel/preset-env
では
- 新しい構文への変換(
arrow function
など) - 新しい機能の追加(
Promise
Array.prototype.flatMap
など )
の様な事を行ってくれます。本記事では後者について、@babel/polyfill
の利用を辞めて @babel/preset-env
でPolyfillを行わせる上で確認を行った事について書いて行きます。
背景
過去にbabelをバージョンアップする際に @babel/preset-env
の導入は行っていたのですが、プロダクトが大きすぎてどのPolyfillを使いたいのかの調査が行えず、 Polyfillについては 元々導入していた @babel/polyfill
を引き続き利用していました。
ただ、 @babel/polyfill
はすでにdeprecatedです。
プロダクトの依存ライブラリのアップデートをする中で、重い腰をあげて @babel/preset-env
に変える事にしたのでした。
本題
@babel/polyfill
は、es6で期待される機能群を一通り組み込むライブラリであり、 @babel/preset-env
は環境に応じて組み込む対象の機能を変更する事ができるライブラリです。
たとえば、
- ソースコード上で利用されているもののみ追加
- chrome、safariなど特定のブラウザの特定バージョン以降で必要なもののみ追加
- シェア0.5%以上、最新の3バージョンなどの指定も可能。
などの様な事が行えます。この時、 @babel/polyfill
を単純に @babel/preset-env
に置き換えて良いのか。どういう機能差異があるのかがわからない点でした。
もちろんサポート対象のブラウザは定めているため、@babel/preset-env
を用いてサポートブラウザでのみ動くようにしても良いのですが、弊プロダクトは現状サポートブラウザと実際利用されているブラウザに大きな差があります。また、比較的レガシーな環境下で利用され続けるケースも見られます。
さすがにIE11のサポートまでは考えていませんが、ある程度どういう影響があるか(全く問題が無いのか、ブラウザによっては動かない可能性があるのか)を前もって知っておく必要がありました。
という事で、両者の内容の違いについてみていく事にしました。
両者の違い
まぁやることは力技なのですが、それぞれがどういう機能を提供していくか確認します。
@babel/polyfill
利用しているbabel@7.4.5では、これだけのモジュールimportされています。
importされているモジュールをそれぞれを展開していくと・・・
たとえば require("core-js/es6");
はこんな風になっています。
require('../modules/es6.symbol');
require('../modules/es6.object.create');
require('../modules/es6.object.define-property');
require('../modules/es6.object.define-properties');
require('../modules/es6.object.get-own-property-descriptor');
// 超長いので省略
require('../modules/es6.reflect.own-keys');
require('../modules/es6.reflect.prevent-extensions');
require('../modules/es6.reflect.set');
require('../modules/es6.reflect.set-prototype-of');
このように、順次展開していくと @babel/polyfill
が拡張してくる機能の一覧を確認できます。
@babel/preset-env
さて次に@babel/preset-env
です。まず、対象となるブラウザの指定を行います。
> 0.5%
last 3 versions
Firefox ESR
not dead
これは、browserslistで確認すると、以下のようなブラウザが対象となります。
@babel/preset-env
ではimport
文を、対象のブラウザについて必要なモジュールのrequire
に置き換える形となるため、以下の様なjsを作り、babelの変換をかけてみます。
import 'core-js/stable';
import 'regenerator-runtime/runtime';
babel index.js
すると、
require("core-js/modules/es.symbol");
require("core-js/modules/es.symbol.description");
require("core-js/modules/es.symbol.async-iterator");
require("core-js/modules/es.symbol.has-instance");
require("core-js/modules/es.symbol.is-concat-spreadable");
require("core-js/modules/es.symbol.iterator");
require("core-js/modules/es.symbol.match");
// 超長いので省略
require("core-js/modules/web.immediate");
require("core-js/modules/web.queue-microtask");
require("core-js/modules/web.timers");
require("core-js/modules/web.url");
require("core-js/modules/web.url.to-json");
require("core-js/modules/web.url-search-params");
require("regenerator-runtime/runtime");
と、指定したブラウザで必要とされる一覧を確認する事ができます。
結果
かなり力技ですが、あとは差分が無いか比較するのみです。
結論からいうと、今回のブラウザ指定であれば、元々あった実装は全て含める形になっているようでした。
唯一、 Reflect.enumerate というものが含まれていませんでしたが、、まぁ大丈夫でしょう。
Removed in ECMAScript 2016 (ES7).
となっていますし、モダンなブラウザでは基本的にサポートされていない実装の様です。
と、いうようにして、 @babel/polyfill
から @babel/preset-env
へ、ブラウザ依存でのダウングレードの可能性について、根拠を持って移行を進める事ができそうです。
Discussion