document.writeでes6 modulesをロードしてはいけない(戒め)
introduction
状況説明
最近(18/5/9)、**Firefoxがes6 modulesに対応**しました。
これで、主要なブラウザがすべてes6 modulesに対応したことになるといえるでしょう。
その流れで、手元の既存のjsをmoduleに対応させようと動かしてみたところ、type=module
なスクリプトが想定通りに読み込まれないというバグが現れました。
ブラウザによって、「ロードはされるが実行されない」という動きであったり、「ロードされず、スクリプトのGETリクエストも飛ばない」という状態だったりとまちまちです。
その動きの原因は、とあるフレームワーク内で使用されていたdocument.write()
でした。
es6 modulesとの相性が悪いのか、ブラウザごとに異なる挙動をするという状態です。
(エラーは出ません。なぜか、エラーは出ません)
そこで、このdocument.write()
を試してみました。
テストに使用したコード
テストに使用したソースコードは以下の通りで、これを適当なサーバーで動かします。
Chrome、Firefox、Edgeでアクセスし、コンソールを確認しました。
<html>
<head>
<script src="./index.js"></script>
</head>
<body>
Hello World!
</body>
</html>
console.log('...')
$(document).ready(() => {
console.log('...2')
document.open()
document.write('<scr' + 'ipt type=module>console.log("...3-1")</scr' + 'ipt><scr' + 'ipt>console.log("...3-2")</scr' + 'ipt>')
document.write('<scr' + 'ipt>console.log("...3-3")</scr' + 'ipt>')
document.write('<scr' + 'ipt type=module>console.log("...3-4")</scr' + 'ipt>')
document.close()
console.log('...4')
})
result
なお、個人的な想定としては以下のようなものでした。
- すべて実行される
-
type=module
であるスクリプトは後から実行される(defer
)
Chrome 67.0
...1
...2
...3-2
...3-3
...4
type=module
であるスクリプトは呼ばれませんでしたが、index.js
内のconsole.log()
も動作しています。
type=module
なスクリプトだけ、全く無視されるというのも、それはそれで想定外なのですが…。
Firefox 60.0
...3-2
...3-3
...3-1
...3-4
document.write()
で指定したスクリプトしか動作していません。
そもそもdocument.write()
自体非推奨になっているということもあり、あまり仕様を確認していませんでしたが、これは想定外です…。
Edge 42
...3-2
...3-3
...3-1
...3-4
Edgeの動作はFirefoxと同様でした。
手元のブラウザだと2:1で、どうやらChromeの動きが少数派なようです。
Conclusion
テストについて
(注:個人の感想です)
document.write()
に対する知識の不足もあり、想定していた結果に一致するブラウザが存在しないという結果になりました。
type=module
なスクリプトも実行されるブラウザと実行されないブラウザで、処理が全く異なることが想像できます。
個人的に問題だったのが、ChromeとFirefoxの動作の違いです。
先述の「ロードはされるが実行されない」ブラウザがchromeで、「ロードされず、スクリプトのGETリクエストも飛ばない」ブラウザがFirefoxだったのですが、納得です。
全く違う結果です。
が、バグに納得できても仕様には納得できるものではありません。
特にChromeとFirefoxは「IE系よりも先に対応させておけ」と言われるようなブラウザですし、なんというかもう少し、その、同じような動きになっていただけませんか…?
document.write()について
結論として何が言いたいかというと、非推奨になるには相応の理由があるのでちゃんと従ってくださいということです。
この件、特にFirefoxについて、es6 modules対応が実装されたばかりということもあってか、issueトラッカーへの蓄積も少なく、document.write()
の動きに行きつくところまでが大変でした。
先述の通り、そもそもdocument.write()
自体、HTML5で非推奨ということもあって、type=module
なスクリプトをdocument.write()
でロードするなどという奇特なことをやっているサンプル・バグなど見つかるはずもありません。
しかし、スクリプトの動的な読み込みの方法の一つとしてdocument.write()
の利用を提案しているWebサイトは多くあります。
また、今回の例のように、直接的に使用していなくてもフレームワーク内で使用しているという場合はまだまだあるでしょう。
その際のおかしな動きについて、「このような場合もあるのだ」程度にでも覚えていただけると幸いです。