Turbolinksによる絶え間ないWeb音楽プレーヤーの実現
8651 ワード
Webページの切り替え時に音楽プレーヤーの無停止再生を実現するには、SPA、pajax、iframeといういくつかのソリューションがよくあります.
以上、何種類も使ったことがないので、今日は自分がプロジェクトで試したソリューションをTurbolinksで紹介します.このスキームは、従来の非前後端分離ウェブサイトに使用することができる.
Demo
プロジェクトアドレス:GitHub
音楽と画像はhey-Audioから来て、感謝します!
曲は網易雲音楽mp 3アドレスから変換された.歌の情報は交流鑑賞にのみ提供されます.網易雲音楽は試聴のみで、商用には使用できません.
Notes
Turbolinks
まずTurbolinksテクノロジーを理解してみましょう.Railsプロジェクトに付属しているテクノロジーですが、JavaScriptのライブラリなので、他のバックエンド言語でも使用できるはずです.バックエンドにも対応する論理がありますが、後で説明します.
一言でその役割を要約すると、
そのメリットは何でしょうか.比較してみましょう.
Turbolinksがない場合、ページAで
一般的に、ページAとBのhtml headコードは同じであり、含まれるJavaScript/CSSも同様であることを意味し、これらのファイルはページAのロード時にダウンロードされ、ブラウザにキャッシュされているため、ページBをロードする際、再ダウンロードに時間がかかる可能性はあまりないが、JavaScriptを再解析するのに時間がかかる(profileを待つ).
ただし、JavaScript/CSSを再ダウンロードして解析する時間はあまり長くはありませんが、ページAのメモリ状態をページBに保つことができないという問題もあります.ページBがページAを置き換えると、メモリ内のページAのデータが完全に空になります.例えば、ページAのJavaScriptコードには、グローバル変数
クッキーやlocalStorageでページAのいくつかの状態をページBに伝えることができると思いますが、ほとんどの場合は可能ですが、私たちが実現したいオーディオの無停止再生ではだめです.
Turoblinksは上記の3つの問題を解決することができます. JavaScript/CSS/Assets は再ダウンロードされません. JavaScript/CSS は再解析されません.現在のページのメモリ は消去されません.
△完全に正確ではないが、大体論理はそうである.
スクリーンショットを2枚見てみましょう.
最初のスクリーンショットは、Turoblinksを使用していない場合、リンクをクリックするたびに、さまざまなリソースが再ダウンロードされます.
2枚目のスクリーンショットは、Turbolinksを使用した場合、チェーンをクリックすると、ダウンロードしたリソースを再ダウンロードすることはありません.
RailsプロジェクトではTurbolinksはデフォルトで使用されていますが、使いたくない場合はアプリケーションから使用します.jsから削除します.
インプリメンテーション
まず、Rails+React+webpacker+react-railsを使用して、従来のサービス側レンダリングの非前後端分離サイトを実現します.どのように実現するか知りたいなら、このリンクを見てください.RailsでReactを使用してSSRを実現する実践を見ることができます.もちろん、Reactを使わなくてもいいです.私はここで一つの考えを述べます.
最初のスクリーンショットアニメーションに示すように、このサイトには2つのページがあり、1つのページは曲のリストで、1つのページは1つの曲の詳細ページで、この2つのページの下部には音楽プレーヤーがあります.どの曲のプレイボタンをクリックしても、この曲が再生され、ページ間が切り替わると、プレーヤーが途切れないことを期待します.
Turbolinksは、ページを切り替えるときにメモリの状態を維持するのに役立つので、audio elementと現在の状態をwindowグローバルオブジェクトに保存するだけで、新しいページにジャンプしてから読み取ります.that's all.
AudioPlayerのコードは以下の通りです.
特別な場合
Turbolinksは を受信できなくなる.フォームFormによって開始されたGET要求 フォームFormによって開始するPOST要求 私たちはそれらを解決する方法を考えなければなりません.
まず、第1の状況を見ると、
コード:
効果の比較:
詳細は、
2つ目のケースでは、フォームFormによって開始されたGETリクエストについて、この例では、titleによって対応する曲を検索する検索フォームを作成します.
2つの解決策があります.上記のように、FormのonSubmitイベントでは、呼び出し GETタイプのFormを に変換する.
サンプルコード:
効果の比較:
3つ目のケースでは、フォームFormによって開始されたPOSTリクエストについて、この例では曲titleを変更するフォームを作成します.解決策は異常に簡単だが、背後にある原理は言う価値がある.
まず解決策を見ると、formの属性に
効果の比較を見てみましょう.
なぜ
しかし、これは第1のステップにすぎず、一般的にPOST要求の結果は302応答であり、ブラウザは応答ヘッダからターゲットアドレスを取り出し、この新しいアドレスにアクセスする.
サービス側のコード:
ここで、通常のPOST要求に対する応答:
私たちのajaxリクエストもこのような応答を得た場合、ジャンプは自動的に実現できません.幸いなことに、Railsはインテリジェントで、POST要求がajaxによって開始されたことを検出し、Turbolinksが有効になった場合、
ajaxリクエストはこの応答を得た後、応答タイプがtext/javascriptであるため、このJavaScriptコードを実行し、このJavaScriptはTuroblinksのvisit APIを呼び出したが、このAPIは私たちが前に何度も使ったことがあり、実際にHTML 5 history APIを呼び出してリフレッシュのないジャンプを実現した.
これも前述したTurbolinksはJavaScriptライブラリですが、バックエンドの協力が必要です.
以上、何種類も使ったことがないので、今日は自分がプロジェクトで試したソリューションをTurbolinksで紹介します.このスキームは、従来の非前後端分離ウェブサイトに使用することができる.
Demo
プロジェクトアドレス:GitHub
音楽と画像はhey-Audioから来て、感謝します!
曲は網易雲音楽mp 3アドレスから変換された.歌の情報は交流鑑賞にのみ提供されます.網易雲音楽は試聴のみで、商用には使用できません.
Notes
Turbolinks
まずTurbolinksテクノロジーを理解してみましょう.Railsプロジェクトに付属しているテクノロジーですが、JavaScriptのライブラリなので、他のバックエンド言語でも使用できるはずです.バックエンドにも対応する論理がありますが、後で説明します.
一言でその役割を要約すると、
ラベル(明示的に必要としない限り)のクリックイベントをすべて引き継ぎ、クリックするとデフォルトのhtmlリクエストをajaxリクエストに変更します.実際には伝統的なサイトをスパに近いものにしましたが、元の書き方をほとんど変えず、極めて侵入性が低いです.そのメリットは何でしょうか.比較してみましょう.
Turbolinksがない場合、ページAで
リンク(ページBと仮定)をクリックすると、ブラウザは通常のhtml要求を開始し、サーバからページBのhtmlコードを取得し、このhtmlコードに含まれるJavaScript/CSS/Assetsのファイルを解析し、ダウンロードし、JavaScriptコードを解析して実行し、ページBを表示します.一般的に、ページAとBのhtml headコードは同じであり、含まれるJavaScript/CSSも同様であることを意味し、これらのファイルはページAのロード時にダウンロードされ、ブラウザにキャッシュされているため、ページBをロードする際、再ダウンロードに時間がかかる可能性はあまりないが、JavaScriptを再解析するのに時間がかかる(profileを待つ).
ただし、JavaScript/CSSを再ダウンロードして解析する時間はあまり長くはありませんが、ページAのメモリ状態をページBに保つことができないという問題もあります.ページBがページAを置き換えると、メモリ内のページAのデータが完全に空になります.例えば、ページAのJavaScriptコードには、グローバル変数
window.globalA = 'haha'
が保存されており、ページBがロードされると、この値は消去される.クッキーやlocalStorageでページAのいくつかの状態をページBに伝えることができると思いますが、ほとんどの場合は可能ですが、私たちが実現したいオーディオの無停止再生ではだめです.
Turoblinksは上記の3つの問題を解決することができます.
リンクをクリックすると、ajaxリクエストが開始され、サーバからhtmlコードが取得され、head部分が現在のページと同じ場合、head部分が解析されます.△完全に正確ではないが、大体論理はそうである.
スクリーンショットを2枚見てみましょう.
最初のスクリーンショットは、Turoblinksを使用していない場合、リンクをクリックするたびに、さまざまなリソースが再ダウンロードされます.
2枚目のスクリーンショットは、Turbolinksを使用した場合、チェーンをクリックすると、ダウンロードしたリソースを再ダウンロードすることはありません.
RailsプロジェクトではTurbolinksはデフォルトで使用されていますが、使いたくない場合はアプリケーションから使用します.jsから削除します.
// app/assets/javascripts/application.js
//= require rails-ujs
//= require activestorage
//= require turbolinks // ---> remove this line
//= require_tree .
インプリメンテーション
まず、Rails+React+webpacker+react-railsを使用して、従来のサービス側レンダリングの非前後端分離サイトを実現します.どのように実現するか知りたいなら、このリンクを見てください.RailsでReactを使用してSSRを実現する実践を見ることができます.もちろん、Reactを使わなくてもいいです.私はここで一つの考えを述べます.
最初のスクリーンショットアニメーションに示すように、このサイトには2つのページがあり、1つのページは曲のリストで、1つのページは1つの曲の詳細ページで、この2つのページの下部には音楽プレーヤーがあります.どの曲のプレイボタンをクリックしても、この曲が再生され、ページ間が切り替わると、プレーヤーが途切れないことを期待します.
Turbolinksは、ページを切り替えるときにメモリの状態を維持するのに役立つので、audio elementと現在の状態をwindowグローバルオブジェクトに保存するだけで、新しいページにジャンプしてから読み取ります.that's all.
AudioPlayerのコードは以下の通りです.
componentDidMount() {
if (!window._globalAudioEl) {
window._globalAudioEl = document.createElement('audio')
}
this.audioElement = window._globalAudioEl
this.audioElement.addEventListener('canplay', this.onCanPlay)
this.audioElement.addEventListener('play', this.onPlay)
this.audioElement.addEventListener('pause', this.onPause)
window.addEventListener('play-audio', this.playAudioEventHandler)
// recover state
if (window._globalAudioState) {
this.setState(window._globalAudioState, () => {
this.state.playing && this._setInterval()
})
}
}
componentWillUnmount() {
// save current state to window before it is unmounted
window._globalAudioState = this.state
window.removeEventListener('play-audio', this.playAudioEventHandler)
this._clearInterval()
this.audioElement.removeEventListener('canplay', this.onCanPlay)
this.audioElement.removeEventListener('play', this.onPlay)
this.audioElement.removeEventListener('pause', this.onPause)
// don't pause it
// this.audioElement.pause()
this.audioElement = null
}
特別な場合
Turbolinksは
リンクのみを処理でき、Turbolinksは
ではなくclick event listenerを最上位レベルのwindowオブジェクトにバインドするため、次のような状況でTurbolinksは処理できません.
のclick handlerでevent.stopPropagation()
が実行すると、イベントのバブルが阻止され、Turbolinksはこのイベントまず、第1の状況を見ると、
event.stopPropagation()
を呼び出してイベントのバブルを阻止した後、要求はデフォルトのhtml要求に戻り、event.preventDefault()
を呼び出してデフォルトの操作を阻止し、Turbolinks.visit(url)
APIを呼び出してajax要求を手動で発行することができる.コード:
songClick1 = (e) => {
// will stop event propagate to window, so turbolinks can't handle this link
e.stopPropagation()
}
songClick2 = (e) => {
e.stopPropagation()
e.preventDefault()
window.Turbolinks.visit(e.target.getAttribute('href') + '?click')
}
This link execute event.stopPropagation() :
{firstSong.title}
Resolution :
{firstSong.title}
効果の比較:
詳細は、
event.stopPropagation()
でTuroblinksが無効になったことは当然のことだと思いますが、実際にはReact合成イベント(SytheticEvent)のバブルではなく、オリジナルイベント(NativeEvent)のバブルではなく、Turbolinksがオリジナルイベントを処理しています.理由を知りたいなら、この文章を見てください.DropdownのReact実装から学びました.2つ目のケースでは、フォームFormによって開始されたGETリクエストについて、この例では、titleによって対応する曲を検索する検索フォームを作成します.
2つの解決策があります.
event.preventDefault()
がデフォルトのコミットをブロックし、その後、呼び出しTurbolinks.visit(url)
APIがajax要求を手動で送信する.
リンクサンプルコード:
queryUrl = () => {
return `/songs?q=${this.state.queryStr}`
}
submitQuery = (e) => {
e.preventDefault()
window.Turbolinks.visit(this.queryUrl())
}
render() {
...
Speical Case 2 - Get Form
Origin Get Form :
Resolution 1 : use window.Turoblinks.visit API
Resolution 2 : convert form to link
this.setState({queryStr:e.target.value})}>
search
...
}
効果の比較:
3つ目のケースでは、フォームFormによって開始されたPOSTリクエストについて、この例では曲titleを変更するフォームを作成します.解決策は異常に簡単だが、背後にある原理は言う価値がある.
まず解決策を見ると、formの属性に
data-remote
(またはdate-remote={true}
)を付けるコードは1行しかありません.全体のコードは次のとおりです.Resolution: data-remote Form
効果の比較を見てみましょう.
なぜ
data-remote
の属性を付けてOKになったのか、不思議な感じではありませんか.ここにはもう一つのJavaScriptライブラリが機能しています.rails-ujsもrailsがデフォルトで有効になっています.このライブラリは多機能で、ここではdata-remote
属性がtrueのformフォームのすべてのリクエストをajaxリクエスト(xhrはリクエストがajaxであることを示す)に変換する役割を果たしていますが、このときTurbolinksは参加していません.前に言ったことを覚えていますか.
チェーンだけで、他のことは気にしません.しかし、これは第1のステップにすぎず、一般的にPOST要求の結果は302応答であり、ブラウザは応答ヘッダからターゲットアドレスを取り出し、この新しいアドレスにアクセスする.
サービス側のコード:
# songs_controller.rb
def update
@song = Song.find params[:id]
@song.update(title: params[:title])
redirect_to @song
end
ここで、通常のPOST要求に対する応答:
私たちのajaxリクエストもこのような応答を得た場合、ジャンプは自動的に実現できません.幸いなことに、Railsはインテリジェントで、POST要求がajaxによって開始されたことを検出し、Turbolinksが有効になった場合、
redirect_to @song
では302ではなくJavaScriptコードを返します.Turbolinks.clearCache()
Turbolinks.visit("http://localhost:3000/songs/21", {"action":"replace"})
ajaxリクエストはこの応答を得た後、応答タイプがtext/javascriptであるため、このJavaScriptコードを実行し、このJavaScriptはTuroblinksのvisit APIを呼び出したが、このAPIは私たちが前に何度も使ったことがあり、実際にHTML 5 history APIを呼び出してリフレッシュのないジャンプを実現した.
これも前述したTurbolinksはJavaScriptライブラリですが、バックエンドの協力が必要です.