会社で新しいプロジェクトを私のほうで独自に技術選定して開発することになり、プロジェクトの要件は英語の製品紹介サイトを作ることでした。プロジェクトは比較的シンプルで、期間もそれほど長くなかったため、最終的に Nuxt フレームワークを使用して開発することにしました。

開発はおおむね順調でしたが、途中で遭遇したあるバグにかなりの時間を費やしました。ネット上で長い時間探しても、関連する内容はごくわずかしか見つからず、いずれも Vue の GitHub リポジトリにある issues でした。最終的には問題の原因を見つけることができませんでした。もちろん、これはバグではなく、フレームワーク自体がそのように設計されている可能性もあります。

以下は私なりの解決方法です。もし別の見解があれば、ぜひご指摘・ご交流ください。

問題の現象

製品紹介サイトのトップページには全画面表示の video 要素があり、動画はデフォルトで自動再生されます。現在の多くのモダンブラウザの最新バージョンでこの機能を実現するには、video 要素に autoplay 属性を追加し、さらにデフォルトでミュート再生するための muted 属性も設定する必要があります。

初回のページ読み込み時には正常に自動再生されており、この時点では DOM 構造も Vue テンプレートに記述したコードと同じであることがわかります。

ここで一度ルートを製品ページに切り替え、その後トップページに戻ると問題が発生します。もともと video 要素にあった muted がなぜか消えてしまい、その結果、動画の自動再生も機能しなくなります。

問題の分析

最初は Nuxt のルート切り替えに関する問題だと思い、Nuxt が提供する navigateTo() メソッドと、Vue Router が提供する $router.push() メソッドを使ってテストしましたが、どちらでも同じ問題が発生しました。

<div class="flex items-center justify-center">
  <img
    class="logo h-9 w-72 cursor-pointer"
    src="/logo.png"
    @click="$router.push('/')"
  />
</div>

2回目は、ページに keepalive: true を設定してルートをキャッシュしてみましたが、それでも問題は解決しませんでした。さらに別のバグも引き起こされたため、詳しく追究することはせず、ひとまずここでは触れないことにします。

definePageMeta({
  layout: 'full-screen',
  keepalive: true,
})

3回目は、video 要素に v-bind を使って muted 属性を動的にバインドしてみました。ref で定義した値を使う場合でも、直接 :muted="true" とバインドする場合でも、問題は解決できませんでした。

<video
  src="car.mp4"
  class="w-full h-full object-cover"
  autoplay
  loop
></video>

Nuxt の問題ではないかと疑い始めたため、4回目は別の純粋な Vue プロジェクトを使ってテストしました。その結果、初回の読み込み時から muted 属性が失われました。しかし奇妙なことに、動画は依然として自動再生できました。この不可解な問題は、次第にますます複雑になっていきました。

最終的に Vue の GitHub リポジトリで、まだ解決されていない issues を見つけました。そこでは私と同じ問題が発生していました。

これは 2022 年 10 月 12 日に作成された issues で、現在に至るまでまだクローズされていません。注目すべき点として、下部には「Vue は本当にネイティブブラウザの機能を壊すべきではない」と指摘する返信がありましたが、これは実際に Vue.js のバグなのかもしれません。

問題の解決

最終的に私は問題の原因にこだわるのをやめ、解決策の準備に取りかかりました。動的に属性をバインドしても問題を解決できないのであれば、最も原始的な方法を使うしかありません。つまり、DOM を操作して要素の再生イベントを発火させます。

onMounted(() => {
  const videos = document.getElementsByTagName('video')
  Array.from(videos).forEach((video) => {
    video.play()
  })
})

Vue のマウントが完了した後、document.getElementsByTagName('video') を使ってトップページにあるすべての動画要素を取得し、それぞれ再生イベントを発火させます。これにより、ルートを切り替えてからトップページに戻った場合でも、動画の自動再生を維持できるようになります。

この方法でも簡単に問題を解決することはできますが、これが最終的な解決策ではないように感じます。問題の核心はやはりフレームワークにあるはずなので、詳しく研究してくださる方がいればと思います。また、私にもぜひご教示いただければ幸いです。