[フロントエンド] Reactを古いブラウザに対応させようと色々と苦労したけど、なんとか対応できた話
こんにちは、Reactに戯れ中の@yoheiMuneです。
仕事で担当しているアプリケーションについて、一部React化した際に、古い端末たちでも動く対応をしたので、それをブログに残しておきたいと思います(これは絶対に今後も参照しそうな気がします、個人的に)。
これらでも動くようにするために、いろいろと試行錯誤した結果、最終的には動くようになりました。後述の3つでした。
またh、必要に応じてHTML5のポリフィルも導入します(必要あれば)。
これをどう対応するかを調べていると、以下のようにコードを書けば動くことがわかりました。
babel-preset-es2015-loose | npm
これを導入して、
1つ目は、アプリケーションのサポート対象をこまめに見直していく必要があるということ。今回は全て対応できた(と思いたい)ですが、今後同じような技術バージョンアップをする際に、必ずや足かせになる時があるなと感じています。技術的な負債で、サービスの成長を止めないようにしていけたらと思います。
2点目は、技術を大きく切り替える時には、20個くらいの環境の違いでテストした方がいいなと思いました。iOS7系でエラーが出るとは思っていなかったです。
いろいろと反省点はありますが、学ぶところも多かったなと感じています。
最後になりますが本ブログでは、フロントエンド・Swift・Python・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSやTwitterをフォローして頂けると幸いです ^ ^。
最後までご覧頂きましてありがとうございました!
仕事で担当しているアプリケーションについて、一部React化した際に、古い端末たちでも動く対応をしたので、それをブログに残しておきたいと思います(これは絶対に今後も参照しそうな気がします、個人的に)。
目次
今回対応した古い端末たち
Reactはモダンな環境で動くように実装されているので、何も対応しないと以下のような古い端末で起動すらしないという状況でした。- iOS6やiOS7のSafari
- Android4.0の標準ブラウザー
- HTCなど一部の古めの端末
これらでも動くようにするために、いろいろと試行錯誤した結果、最終的には動くようになりました。後述の3つでした。
ES5のポリフィル導入
まずはこんな感じのエラーが出ました。TypeError: Result of expression 'Object.freeze' [undefined] is not a function ?これはReactの説明(日本語)でも記載されていることですが、古いブラウザ対応のためにShimを読み込む必要があります。具体的には、以下のような機能を使えるようにするために、
Array.isArray Array.prototype.every Array.prototype.forEach Array.prototype.indexOf Array.prototype.map Date.now Function.prototype.bind Object.keys String.prototype.split String.prototype.trim Object.create Object.freeze以下のように、各種ポリフィルを読み込みます(詳細は上記リンクを参照ください)。
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-sham.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.34.2/es6-shim.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.34.2/es6-sham.min.js"></script> <script src="https://wzrd.in/standalone/es7-shim@latest"></script>余談ですが、
es5-shim.min.js
とes5-sham.min.js
は別物なんですね。最初見た時は同じ行が2つあるぞー、と思って少し惑わされましたw。またh、必要に応じてHTML5のポリフィルも導入します(必要あれば)。
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>これでReactが動くと思いきや、動かない端末たちがありました。具体的にはiOS6や一部のAndroid4.0系です。残念><。。
Promiseのポリフィルを導入する
なぜ動かないのかを、iOSサファリのデバッガで確認したところ、Promise
がなくてエラーになっていることがわかりました。Uncaught ReferenceError: Promise is not definedなので、Promiseのポリフィルを必要に応じて読み込むようにしました。
if (window.Promise === undefined) { document.write('<scr'+'ipt src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/3.2.2/es6-promise.js"></scr'+'ipt>'); }この対応で古いiOS系は動くようになりました。しかし、それでも一部動かないAndroid端末たちがあり、次の対応を行いました。
Babelのlooseコンパイル
主にAndroid4.0の標準ブラウザで発生していたエラーですが、以下のようなJSエラーが発生していました。Uncaught TypeError: Cannot assign to read only property '__esModule' of #<Object>これをよくよく調べていくと、(Babelでコンパイルしたソースコードで)React内の以下の箇所でエラーになっていることがわかりました。
Object.defineProperty(exports, "__esModule", { value: true });
defineProperty
メソッドで、__esModule
という変数は追加できないぞ、というエラーのようです。Object.defineProperty | MDN(英語)によるとwritable
をtrue
にしないとできないようで、その部分のエラーとのことでした。これをどう対応するかを調べていると、以下のようにコードを書けば動くことがわかりました。
exports.__esModule = true;ただ、上記のコードはBabelでコンパイルした時にできたものなので、今回は書き換えることはできません。Babelで対応するにはどうしたらいいのか調べたら、以下のlooseコンパイルできるものを利用する方法がありました(Babel自体にもlooseオプションはありましたが、v6.0.0から削除されました)。
babel-preset-es2015-loose | npm
これを導入して、
$ npm install --save-dev babel-preset-es2015-looseそして、
.babelrc
を以下のようにすることで、ルーズコンパイルができるようになりました。{ "presets": ["es2015-loose"] }この結果、無事に問題を起こしていたAndroid端末でも動くようになりました。
今回学んだ教訓
大きく2点学びました。1つ目は、アプリケーションのサポート対象をこまめに見直していく必要があるということ。今回は全て対応できた(と思いたい)ですが、今後同じような技術バージョンアップをする際に、必ずや足かせになる時があるなと感じています。技術的な負債で、サービスの成長を止めないようにしていけたらと思います。
2点目は、技術を大きく切り替える時には、20個くらいの環境の違いでテストした方がいいなと思いました。iOS7系でエラーが出るとは思っていなかったです。
いろいろと反省点はありますが、学ぶところも多かったなと感じています。
最後に
今回はReactにおける、古い環境への対応についてブログを書きました。いろいろな試行錯誤ができてなかなか楽しいものでもありました。途中、中国人のブログに答えがあった時とかは、インターナショナルをなぜか感じてましたw。最後になりますが本ブログでは、フロントエンド・Swift・Python・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSやTwitterをフォローして頂けると幸いです ^ ^。
最後までご覧頂きましてありがとうございました!