ツイート
シェア
LINEで送る
B! はてぶでブックマーク
Pocketでブックマーク
RSSフィード

webpack4, @babel, IE11でforEachがエラー

Node.js image
イラストダウンロードサイト【イラストAC】
の画像をもとに加工しています。

@babelでトランスパイルしているのにIE11でNodeList.forEach()が使えない。

なんで? ...そうか、ポリフィルが必要だった。

ポリフィルはブラウザの種類やバージョンによって追加されたり変更・削除されたコードを補完するものです。

トランスパイル(transpile)

プログラムを、べつのバージョンや、べつの言語のプログラムに変換すること。

一種のコンパイラ。トランスコンパイラともいう。

JavaScriptは最新の仕様で開発して、任意のバージョンにトランスパイルする手法が一般的になっている。

(新しい仕様のほうがシンプルな記述で読み書きしやすいため。)

@babelが有名。

トランスパイルは文法を変換するが新しいAPI(関数やオブジェクト)までは対応できない。

古いもので確実に動かすためにはポリフィルも必要。

ポリフィル(Polyfill)

プログラムのバージョン違いでAPIが不足するところを補うこと。

プログラム言語はバージョンが上がると新しいAPIを追加する。APIは関数やクラス・オブジェクトのこと。

古いバージョンで動かすときは新しいAPIと同じ機能がないと動かない。それを作るのがポリフィル。

ポリフィルは、新しいAPIを移植するのではなく、古いバージョンのプログラムで実装できるAPIに作り直して提供する。

jQueryのmigrateはjQueryのポリフィル・パッケージ。

トランスパイルで有名な@babelにもポリフィルの拡張機能がある。

Windows8のサポートはすでに終わってます。今サポートされているのはバージョン8.1と10のふたつ。

IEに対応しないといけないか? と言われれば、作業量を考えればほとんどないでしょう。

ただし、社内のイントラネットなど特定の環境では需要があり、Windows10ではEdgeが標準ですがIE11も残しています。

今回の話は、近い将来必ず不要になることに注意してください。

(Win10からIEが消えたら確実にいらない。)

ちなみに最新のEdgeの中身はChromeです。マイクロソフト、ついにやめたか...

forEach()は文法ではなくAPI

自分のサイトを見ている人の11%はIEを使ってます。そのうちの99%以上はIE11。

ということでJavaScriptをIE11でも動くようにしようと思ったのですがうまくいきません。

(IE11以外は対象外。)

SCRIPT438: オブジェクトは 'forEach' プロパティまたはメソッドをサポートしていません。

エンジニア
@babel使ってるんだけど? IE11も入ってるはずだけど?

そう思って自分は悪くないっ!てしてましたが、そういうとき、悪いのはたいがい自分です。

ここでも100%自分がまちがってた。トランスパイルはしているけどポリフィルをしていません。

そりゃそうだ。forEach()はトランスパイルではムリだもん。

@babelのポリフィル

ということで、@babelのポリフィルを設定します。

パッケージバージョン
webpack
webpack-cli
4.43.0
3.3.11
@babel/core
@babel/preset-env
babel-loader
7.10.2
7.10.2
8.1.0

まずは変更前の設定から。babel専用の.babelrc.jsを使っています。

webpack.config.jsに設定してもいいですが、.babelrc.jsは@babel専用の設定でwebpack以外でも使えるのでこちらのほうがいいです。

.babelrc.js(変更前)
module.exports = {
  presets: ['@babel/preset-env'],
  plugins: [
    '@wordpress/babel-plugin-import-jsx-pragma',
    '@babel/transform-react-jsx',
  ],
};
.babelrc.js(変更後)
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        useBuiltIns: "usage",
      },
    ],
  ],
  plugins: [
    "@wordpress/babel-plugin-import-jsx-pragma",
    "@babel/transform-react-jsx",
  ],
};

@babel/preset-envのuseBuiltInsオプションを使います。

pluginsのオプションは無視してください。WordPressとreactで@babelを使うためのもので、ポリフィルとは関係ありません。

@babelには@babel/polyfillという、いかにもなパッケージがあります。

が、これはbabel7.4.0以上から非推奨です。preset-envを使いましょうになってます。

core-jsが必要

まだ作業は終わりません。いまwebpackを実行するとこんなエラーが出ます。

WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.

You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:

  npm install --save core-js@2    npm install --save core-js@3
  yarn add core-js@2              yarn add core-js@3

(省略)

ERROR in ./src/***/***.js
Module not found: Error: Can't resolve 'core-js/modules/es6.array.for-each' in '***\***\js'
resolve 'core-js/modules/es6.array.for-each' in '***\***\js'
  Parsed request is a module

(省略)

警告: core-jsのバージョンを宣言せずにuseBuiltInsオプションを使っています。今のところ、バージョンが指定されていないときは2.xを想定しています。

このデフォルトバージョンは将来のBabelで変更される可能性が高いので、corejsオプションで明示的に指定することをおススメします。

(以下省略)

筆者訳

core-jsのインストールコマンドが表示されてるのでコピペで実行しましょう。

(もちろんバージョン3)

パッケージバージョン
core-js3.6.5

設定も修正します。

.babelrc.js
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        useBuiltIns: "usage",
        corejs: { version: 3, proposals: true },
      },
    ],
  ],
  plugins: [
    "@wordpress/babel-plugin-import-jsx-pragma",
    "@babel/transform-react-jsx",
  ],
};

設定の注意点

usageにはふたつの設定があるようで、

when using useBuiltIns: "usage" you have two different alternatives:

("usage" を使用する場合、次の2つの選択肢があります。)

set the shippedProposals option to true. This will enable polyfills and transforms for proposal which have already been shipped in browsers for a while.

(shippedProposals オプションを true に設定します。これにより、しばらくの間、ブラウザで既に出荷されているプロポーザル用のポリフィルやトランスフォームが有効になります。)

use corejs: { version: 3, proposals: true }. This will enable polyfilling of every proposal supported by core-js.

(これにより、core-jsでサポートされているすべてのプロポーザルのポリフィルが可能になります。)

筆者訳

shippedProposalはブラウザに影響されるようなので、corejsオプションだけを採用。

'entry' と 'usage'のちがい

useBuiltInsには'entry' も指定できますが、これは自分でcore-jsをimportをしないといけません。

import "core-js";

しかも、すべてのポリフィルをimportするので無駄なコードまでも入ります。

さらに、個別にimportしてコードを最小にできますが、importするパッケージを覚えないといけないのが面倒。

一方、'usage' は、importが不要です。

自動で使うパッケージだけを最小限でimportします。

それでもまだ動かない

これでいけるか? と思いましたがまだダメでした。設定が不十分です。

結果から言うとこの設定です。

.babelrc.js
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: {
          ie: 11,
          esmodules: true,
        },
        useBuiltIns: "usage",
        corejs: { version: 3, proposals: true },
      },
    ],
  ],
  plugins: [
    "@wordpress/babel-plugin-import-jsx-pragma",
    "@babel/transform-react-jsx",
  ],
};

targetsのesmodulesがtrueじゃないとダメです。また、esmodulesを設定するとブラウザのバージョン指定(targets.browsers)が無視されるので、ieオプションが必要です。

(もちろん .browserslistrc も無視される。)

ターゲットブラウザはieだけじゃなく、その他のブラウザのオプションも用意されています。くわしくは、上のリンクを見てください。

結論! IE11は捨てる。

長々と説明してきてそれは無いわ~、ですが、結果的にIE11は捨てることにしました。

理由は、

  • jsファイルのサイズが大きくなる。
  • @babelのターゲット・ブラウザのバージョン指定が面倒。
  • CSSでプレフィックス以外の修正が必要。

ほかのブラウザ、スマホ・タブレットなどの中小型デバイスに対応するのに加えIE11まで対応するのは、時間はかかるのに割に合いません。

IEの利用者は減っていくことは確実なので。Edgeで見てくれってことですね?

(スマホ・タブレットを最優先しているというのもある。)

今回の話をすべて否定はしません。IE11と他のブラウザの共存を諦めただけで。

もちろんですけど、お金と時間がふんだんにあるのならやります。

前の投稿
webpack4, html-webpack-exclude-assets-pluginは使えない話。
webpack4, postcss-loaderのバージョンアップでエラー
次の投稿

JavaScriptの本

post-cta-image

『自分は向いていない』『やってみたけど挫折した』『プログラマだけどjavaScriptは未経験』『フロントエンドエンジニアを目指したい』など、いろいろなタイプに合わせた書籍を集めました。

どうしてもネットで自分で調べるのが苦手という人におすすめです。

将来的には、書籍を買わずにネット上の公式ドキュメントで情報収集できるようなものを選んでいます。

コメントを残す

*