概要
今まで何も考えず</body>
前にJSを読み込ませてたのですが、最近のベストプラクティスとしてhead
タグに
// モダンブラウザ用
<script type="module" src="js/main.js"></script>
// IE11などレガシーブラウザ用
<script nomodule src="js/nomodule.main.js" defer></script>
のように読み込むというプラクティスを発見し、実践した。
その際にsafariでのみ読み込みエラーが発生したため、その解決方法の備忘録です。
解説
type="module"
での読み込みに関しては下記の記事の解説が分かりやすかったため
一読をおすすめします。
モダンブラウザに最適化した JavaScript を提供する
ライブラリ以外はとりあえず type="module" にしよう
nomodule
に関しては下記。
ES Modules への橋渡しとしての nomodule 属性
ひとまずざっくり説明すると、type="module"
はモダンブラウザのために最適化された読み込み方式(deferとかでタイミング調整しなくても良い)で、type="module"
に対応していないブラウザはnomodule
の方を自動的に読んでくれます。
逆に言えばnomodule"
のJSはtype="module"
対応ブラウザでは読み込まれないため、複数のpolyfillを読み込んで肥大化したファイルを読み込む必要がなくなります。便利。
なのでWebpackでそのあたりを出し分ければモダンブラウザで無駄のないJSファイルを配信できますね。
ハマったポイント
ここでタイトルの内容に入ります。
モダンブラウザに最適化した JavaScript を提供する
の記事内にありますが、type="module"
でJSを読み込む場合、CORS通信(ざっくり言うと外部通信的な)扱いとなってしまい、MacOS、iOSのsafariでのみ
Blocked https://example.com/js/hoge.js from asking for credentials because it is a cross-origin request.
とアラートが出て読み込みができない問題が発生しました。
解決方法
ご紹介記事の中にもありますが、.htaccess
などサーバーサイドでAccess-Control-Allow-Origin: https://example.com
のようにCORS通信を許可する方法があります。(セキュリティー上問題があるようなので、ワイルドカードでの指定はやめたほうが良さそう。)
ですが、実案件ではクライアントのサーバー設定をイジれないケースもあり、上記の方法では解決できませんでした。
また、Access-Control-Allow-Origin
の設定先を開発環境URLにしていて、公開したときに書き換え忘れて読み込めない!のような自体も想定できます。
いろいろ調べた結果、下記のように<script>
タグにcrossorigin属性をつけることでサーバーサイドをイジる必要なく解決しました。
// crossorigin属性を追加
<script type="module" src="js/main.js" crossorigin="use-credentials"></script>
crossorigin
の詳しい解説は[こちら]
(https://developer.mozilla.org/ja/docs/Web/HTML/CORS_settings_attributes)
crossorigin="anonymous"
としても読み込みをされますが、こちらは実際に外部のCDNなどから読み込む場合に使用したほうが良さそう?なのでcrossorigin="use-credentials"
としました。(『ちげぇよ馬鹿野郎!』というご指摘があれば教えていただけると助かります…)