IE でだけ、画面に何も表示されないんですけど?
いきなりこの知らせを受けたので、調査することに。フロントエンジニアにはよくある事なので落ち着いて IE 環境を modern.IE でセットアップしつつ状況を漁っていた。
結論
babel plugin で production ビルドのみ以下のoptimizationがロードされていて、それをオフにしたら動作した。
原因
IE には Symbol が実装されていない、かつ、 React の JSX は内部的に Symbol が使われている。基本的に React が createElement で生成したObjectであれば、そのタイミングで polyfill 化された Symbolが入っていて、同値比較と正当性チェックをパスするので問題ない。
問題があるのは、 babel-plugin-transform-react-inline-elements
で最適化オプションを付けてる時。
これは react がJSXで変換した結果を reactのcreateElement関数にするんじゃなくて、さらにアグレッシブにcreateElement関数の呼び出し結果のオブジェクトに変換してしまうことで関数呼び出しのコストを減らす、というもの。
呼び出し結果のオブジェクトは Symbol 化されたオブジェクトがそのまま生成されてしまう。これを babel が Polyfill するわけだが、その結果として、 ReactがcreateElementで生成したものとオブジェクトの結果が変わってしまう。
Symbol がつかえる環境であれば、 polyfillされたとしてもnativeのSymbolを利用するため、問題ない。問題があるのは IE 等の native Symbol が使えない環境。
native Symbol が使えない状況だと createElement で生成しているものと違うものになってしまうため、結果、 JSX が valid なオブジェクトとして評価されなくなる。
どうなるかというと、 render の時の isValidElement 関数がパスしなくなり、表示されずに弾かれてしまう。
わかりにくい要因はこの最適化オプションが production の時に基本的に採用されるものであるため、 production ビルド時にはエラーが suppress されてしまい、ただの『白い画面』になる点。
最適化オプションを入れた直後なら気づけるだろうが、基本的にモダンブラウザで実行していて、IEなどのレガシーブラウザを見るのはQA中以外にあまり積極的にやっていないと気づけない。
兎にも角にも IE11 との勝負には勝利した。
IE11との勝負に勝ったが、試合に勝って勝負に負けた気がしている。
— Yosuke FURUKAWA (@yosuke_furukawa) December 14, 2016