こうこく
作 ▸
改 ▸

TypeScript + React 17 + Webpack 5 をIE11に対応させる

  • Babelは不要なはず。
  • コード本体をES5にトランスパイルする設定と、Webpackでバンドル時に生成されるコードをES5にする設定が必要。
  • コード中で使用してるnpmパッケージの中にES5じゃないものが含まれてるかも!
react 17.0.1typescript 4.1.2webpack 5.21.2
もくじ

この記事のサンプルで使用した package.json

サンプルとかどうでもいい人は次のセクションからどうぞ。

ソースコードは以下本文中に記載。

package.json
{
  "name": "react-typescript-ie11",
  "version": "1.0.0",
  "description": "",
  "dependencies": {
    "array-move": "^3.0.1",
    "core-js": "^3.9.1",
    "html-webpack-plugin": "^5.3.1",
    "react": "^17.0.1",
    "react-app-polyfill": "^2.0.0",
    "react-dom": "^17.0.1"
  },
  "devDependencies": {
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "ts-loader": "^8.0.11",
    "typescript": "^4.1.2",
    "webpack": "^5.21.2",
    "webpack-cli": "^4.5.0"
  }
}

手順1. ポリフィル導入

IE11に無い関数などを補うためのポリフィルが要る。

以下コマンドで core-jsreact-app-polyfill をインストール。

npm install core-js react-app-polyfill

そしたら、エントリポイント (webpack.config.jsentry に指定しているファイル) の先頭で core-jsreact-app-polyfill をインポートするようコードを修正。

以下のような感じ。

例) index.tsx
import 'core-js/stable';
import 'react-app-polyfill/ie11';

import React from 'react';
// 以下略

手順2. ビルド設定

tsconfig.jsonmoduleamdtargetes5 を設定する。

これをすると、トランスパイルの結果がブラウザ用のES5 (IE11がサポートしてる最後のJSのバージョン) の構文になる。

tsconfig.json
{
  "include": ["src"],
  "compilerOptions": {
    "module": "amd",  // ここと
    "target": "es5",  // ここ
    "outDir": "dist",
    "allowJs": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "strictNullChecks": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "jsx": "react"
  }
}

次に、webpack.config.jstarget: ['web', 'es5'] を追加する。

これをすると、Webpackがバンドル用に生成するコードがブラウザ用のES5の構文になる。

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  target: ['web', 'es5'],  // ここ
  entry: './src/index.tsx',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.(tsx?|jsx?)$/,
        exclude: /node_modules/,
        use: {
          loader: 'ts-loader',
          options: {
            transpileOnly: true,
            configFile: 'tsconfig.json',
          },
        },
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.ejs',
      filename: 'index.html',
      inject: 'body',
      hash: true,
    }),
  ],
};

TypeScriptの設定でコード本体をES5に変換して、Webpackの設定でそれをバンドルするためのコードもES5で生成してもらうということ。

手順3. ビルドする

ここでは例として以下のコードを src ディレクトリ以下に用意した。

アロー関数など、いかにもIE11で動かなさそうなものを使ってる。(※array-move というnpmパッケージについては後述)

src/index.tsx
import 'core-js/stable';
import 'react-app-polyfill/ie11';

import React from 'react';
import ReactDOM from 'react-dom';
import { sum } from './jslib';
import arrayMove from 'array-move';

// 自分で書いたアロー関数を含むモジュール (js)
console.log(sum(1,2,3), 'js arrow function ok');

// npmでインストールしたアロー関数を含むモジュール
console.log(arrayMove([1,2,3], 0, 1), 'node_modules arrow function ok');

// アロー関数
const love = (): string => 'arrow function ok';
console.log(love());

// string の startsWith
console.log('kiriukun'.startsWith('k'), 'startsWith ok');

// fetch (※IE11より新しいブラウザではCORSのあれでレスポンス時にエラーになるがリクエストが出来ればOK)
fetch('https://note.kiriukun.com')
  .then(res => {
    console.log('fetch ok');
    res.text().then(text => {
      console.log(text);
    });
  })
  .catch(e => console.error(e));

// async
(async () => {
  console.log('async ok');
})();

ReactDOM.render(<div>ok</div>, document.getElementById('app-root'));
src/jslib.js
/**
 * 引数を合計して返す
 * @param  {...number} args 
 * @returns {number}
 */
module.exports.sum = (...args) => {
  let res = 0;
  for (const num of args) {
    res += num;
  }
  return res;
};
src/index.ejs
<!doctype html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>example</title>
  </head>
  <body>
    <div id="app-root"></div>
  </body>
</html>

これらのコードを用意して以下コマンドを実行すると、バンドルされたJavaScriptファイル bundle.js とHTMLファイル index.htmldist ディレクトリ以下に生成される。

npx webpack

これでIE11で index.html を開けば動く……

と思うかもしれないがちょっと待って。

手順EX. バンドル結果にIE11で動かないコードが残っちゃう場合

上記のサンプルに含まれている array-move というnpmパッケージは、コード内にIE11で動かないアロー関数が含まれてる!

試しにIE11で index.html を開いてみると、開発者ツールのコンソールに構文エラーが表示されるはず。当該部分のコードを読んでみると、array-move のものであろうことが分かる。

array-move のアロー関数で構文エラー
array-move のアロー関数で構文エラー

このように node_modules 内にES5で動かないものがある場合、それらも変換してやる必要がある。

上記の webpack.config.js では ts-loader の対象から node_modules 以下をまとめて除外してしまっていた。なので ts-loader のところを以下の通り include を使うよう書き換えて、トランスパイル対象を細かく指定できるように変更する。

webpack.js (抜粋)
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 省略...
  module: {
    rules: [
      {
        test: /\.(tsx?|jsx?)$/,
        include: [
          // ↓src以下とnode_modules/array-move以下を名指しでトランスパイル
          path.resolve(__dirname, 'src'),
          path.resolve(__dirname, 'node_modules', 'array-move')  // node_modules/array-move
        ],
        use: {
          loader: 'ts-loader',
          options: {
            transpileOnly: true,
            configFile: 'tsconfig.json',
          },
        },
      },
    ],
  },
  // 省略...
};

これで再度以下コマンドを実行して……

npx webpack

生成された dist/index.html をIE11で開き、開発者ツールのコンソールを見てみると、以下のように全てのコードが動作したことが確認できる。

以上。

この記事に何かあればこちらまで (非公開)