先日、運用している Nuxt.js アプリケーションで「本番環境で特定の iOS 端末に限定してアプリケーションが動かない」という報告がきたのでその対処の話です。
tl;dr
- デフォルトの Nuxt.js では Mobile Safari 10 でバグる
- teaser のオプションに safari10 を追加しろ
Cannot declare a let variable twice 'e'
再現端末を用意してリモートデバッグしてみると、出ていたエラーは Cannot declare a let variable twice 'e'
というエラーでした。let 宣言の重複エラーみたいなので、「実はうまく変換されてない?」という疑問が生まれます。
大体こういうのは Babel 設定か Uglify の設定の問題なので、その辺りを検索ワードに含めつつ検索。
Mobile Safari 10 の ES6 実装のバグが原因
調べた結果、webpack-contrib/uglifyjs-webpack-plugin でドンピシャな Issue がありました。
Uglify 周りの設定でうまくいくかなと思ってみてみると、
SyntaxError: Cannot declare a let variable twice
This is an ES6 bug in Safari 10:
https://bugs.webkit.org/show_bug.cgi?id=171041
ということで、 WebKit にバグがあって、それが Safari 10 でそのまま再現してしまっていたことがわかりました。Uglify としてはこの挙動の対処方法として、オプションを渡してやるのでそのまま反映してやるとよさそうです。
Nuxt.js の内部的な Optimizer は Terser に変わっていた
Uglify での設定 (Nuxt v1.x では有効)
まずはじめに、 nuxt.config.js の build オプションに、調べて出てきた fallback のためのオプションを追加してみることにしました。
Nuxt.js v1.x 時代に Uglify の設定を書き換えたことはあるので、どうやると良いかは把握していました。
https://qiita.com/potato4d/items/2c89cf6433a3833e005c
import { NuxtConfiguration } from '@nuxt/vue-app'
const config: NuxtConfiguration = {
//
build: {
uglify: {
uglifyOptions: {
mangle: {
safari10: true
}
}
}
}
}
export default config
こんな感じで編集してみてビルド実行。しようと思ったのですが、 uglify が何故か TypeScript の型定義に怒られて config ファイルが invalid なものになっていました。
Terser での設定 (Nuxt v2.0 or higher で有効)
ということで調べ直してみると、 Nuxt.js の optimizer は今は Uglify ではなく Terser になっていた様子。当然 Uglify は設定の型定義からも drop されています。
Uglify は uglify-es を使わない限り ES2015+ に対応していないのですが、 Terser は対応しているので、最近は割と好まれる optimizer になっています。
一方で、 Uglify だと通る時点で ES5 であることが確実だったりするので、こういう問題は基本起きなくて安心だったりする(後ろ向きながらも)利点はあったりします。
terser に変わっていたので、設定もそれに合わせて書き直すように。
import { NuxtConfiguration } from '@nuxt/vue-app'
const config: NuxtConfiguration = {
//
build: {
terser: {
terserOptions: {
safari10: true
}
}
}
}
export default config
これで修正完了。デプロイして一件落着です。
おわりに
Mobile Safari のアクセス数は普通だと 0.01% にも満たないと思うのであまり気にすることはないかもしれませんが、アクセスの絶対数が多いと無視できない数字になったりします。
今回は iOS 10 の端末がなければ再現できないという点で手間取りましたが、古いブラウザの場合はこういったバグも少なからずあるので、必ず再現するものを用意して一つずつ確実に潰していく必要がありますね。