Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In Vue 2.5 ,the use of MessageChannel in nextTick function will lead to audio can not play in some mobile browsers. #7109

Closed
ustbhuangyi opened this issue Nov 22, 2017 · 4 comments

Comments

@ustbhuangyi
Copy link

ustbhuangyi commented Nov 22, 2017

Version

2.5.8

Reproduction link

https://codesandbox.io/s/vpomxl5p3

Steps to reproduce

Click the 'click me' button and the audio will play.

What is expected?

It can play well in all of the modern browsers that support audio.

What is actually happening?

In PC browsers, it can play well. However, in some mobile browsers, such as iOS Safari、iOS Wechat Webview、Android Chrome,it can not play.


In Vue 2.4 or below, the nextTick function use microTask, like MutationObserver or Promise at first,the click callback and audio play function are in the same tick,so it can play well in all browsers.

Howerver, in Vue 2.5 ,macroTask will be used in DOM action, so the click callback and audio play function are not in the same tick. If we just use setTimeout 0, it will be fine, but actually MessageChannel will be firstly used because of the priority, which lead to the audio can not play in some mobile browsers mentioned above.

@yyx990803
Copy link
Member

This is because Mobile browsers only allow programmatic audio play in the same task tick of a user interaction:

Failed to execute 'play' on 'HTMLMediaElement': API can only be initiated by a user gesture.

The change indeed causes this behavior, but IMO it's somewhat expected because watchers are asynchronous by design.

Instead of using a watcher to react to url changes, why not just call a method that sets the url and start playing in the same function? I don't really see a point of using a watcher especially when you need the audio.play() to be executed in the same event handler tick.

@ustbhuangyi
Copy link
Author

ustbhuangyi commented Nov 23, 2017

@yyx990803, thanks for your response.

This is just a simple demo to show the reason why it can not play in mobile browsers.

In our music app, we design it using vuex, set playlistcurrentIndex to vuex state,and currentSong is a getter, which calculate by playlist and currentIndex, like this:

// state.js
const state = {
  playlist: [],
  currentIndex:0
}
// getters.js
export const currentSong = (state) => {
  return state.playlist[state.currentIndex] || {}
}

Because audio is in player component, and we may play the song by clicking some button outside this component(like playlist component), so we just commit the mutation to set the playlist and currentIndex, and watch the currentSong in player component, and call audio play when the currentSong changes.

// player.vue
watch : {
   currentSong(newSong,oldSong) {
      if (!newSong.id || !newSong.url || newSong.id === oldSong.id) {
          return
       }
       this.$refs.audio.src = newSong.url
       this.$refs.audio.play()
   }
}

You may say 'why not use sync watcher?',I have tried this, but it cause another problem, because there is a feature that we can set the playMode(sync、random or loop),when the mode changes from sync to random, I must change the playlist and currentIndex by committing mutations. If I use sync watcher here, the currentSong handler will be called twice, which is not expected. If I use async watcher(default), the currentSong handler will be called only once in next tick.

Currently I fixed this problem by setting the global MessageChannel function to a noop function before Vue.js init, so macroTask in nextTick will use setTimeout 0, it works but it's a very hack way, so I wish Vue.js core may consider this kind of situation?

@whatwg6
Copy link

whatwg6 commented Dec 12, 2017

m

@Rainmen-xia
Copy link

m

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants