TypeScript + React 17 + Webpack 5 をIE11に対応させる
- Babelは不要なはず。
- コード本体をES5にトランスパイルする設定と、Webpackでバンドル時に生成されるコードをES5にする設定が必要。
- コード中で使用してるnpmパッケージの中にES5じゃないものが含まれてるかも!
この記事のサンプルで使用した 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-js
と react-app-polyfill
をインストール。
npm install core-js react-app-polyfill
そしたら、エントリポイント (webpack.config.js
の entry
に指定しているファイル) の先頭で core-js
と react-app-polyfill
をインポートするようコードを修正。
以下のような感じ。
import 'core-js/stable';
import 'react-app-polyfill/ie11';
import React from 'react';
// 以下略
手順2. ビルド設定
tsconfig.json
の module
に amd
、target
に es5
を設定する。
これをすると、トランスパイルの結果がブラウザ用のES5 (IE11がサポートしてる最後のJSのバージョン) の構文になる。
{
"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.js
に target: ['web', 'es5']
を追加する。
これをすると、Webpackがバンドル用に生成するコードがブラウザ用のES5の構文になる。
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パッケージについては後述)
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'));
/**
* 引数を合計して返す
* @param {...number} args
* @returns {number}
*/
module.exports.sum = (...args) => {
let res = 0;
for (const num of args) {
res += num;
}
return res;
};
<!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.html
が dist
ディレクトリ以下に生成される。
npx webpack
これでIE11で index.html
を開けば動く……
と思うかもしれないがちょっと待って。
手順EX. バンドル結果にIE11で動かないコードが残っちゃう場合
上記のサンプルに含まれている array-move
というnpmパッケージは、コード内にIE11で動かないアロー関数が含まれてる!
試しにIE11で index.html
を開いてみると、開発者ツールのコンソールに構文エラーが表示されるはず。当該部分のコードを読んでみると、array-move
のものであろうことが分かる。
このように node_modules
内にES5で動かないものがある場合、それらも変換してやる必要がある。
上記の webpack.config.js
では ts-loader
の対象から node_modules
以下をまとめて除外してしまっていた。なので ts-loader
のところを以下の通り include
を使うよう書き換えて、トランスパイル対象を細かく指定できるように変更する。
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で開き、開発者ツールのコンソールを見てみると、以下のように全てのコードが動作したことが確認できる。
以上。