爬坑地址 nuxt.js框架中 audio标签的坑
一、前言
上星期接到一个需求,业主说要在移动端的考试系统中弄一个背景音乐上去,让人听着做题更舒服,并且做对题目和做错题目都需要有不同的音乐提示(业主不当产品经理留在小公司折磨我这种小员工实属浪费)。于是就想到用html5的audio标签。但是后来做的时候发现,audio标签在IOS(苹果)端的微信浏览器中存在着很多坑,而且要整合到nuxt.js当中又踩了坑,决定把这些坑和解决办法记下来。
二、第一坑——nuxt.config.js的配置
本以为直接放audio标签进去.vue文件中就完事了,结果报了下图的错。
明知道是缺了某个loader,但是又不知道怎么配置,后来参照着vue-cli对mp3的配置,如下图
这里不作多说,参照着这个配置,我在nuxt.config.js文件中配置了对mp3文件的处理方法
build: {
/*
** You can extend webpack config here
*/
extend (config, ctx) {
// Run ESLint on save
const vueLoader = config.module.rules.find((loader) => loader.loader === 'vue-loader');
/* 把audio标签在编译时转成src属性 */
vueLoader.options.transformToRequire = {
audio: 'src'
};
/* 对mp3等格式的文件用url-loader进行处理 */
config.module.rules.push({
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: process.env.NODE_ENV === 'production'
? path.posix.join('./', 'media/[name].[hash:7].[ext]')
: path.posix.join('./', 'media/[name].[hash:7].[ext]')
}
});
if (ctx.isDev && ctx.isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
});
}
}
}
ok,这样就可以把mp3文件放到assets目录下了,你也可以在assets目录下新建media诸如此类的文件夹,然后把mp3文件塞进去。
千算万算没想到nuxt框架竟然没有帮忙配好mp3格式的编译,而只是配了mp4(video标签)的格式的loader。
好了,现在就可以像下图一样开心的写代码了
<audio id="audio" ref="audio" loop="loop" autoplay="autoplay">
<source src="@/assets/music/exam.mp3" type="audio/mpeg">
您的浏览器不支持 audio 元素。
</audio>
这样他就能识别出"@/assets/music/exam.mp3"是什么东西。
三、第二坑——IOS端在微信浏览器中不能自动播放音乐
这个也是弄了我很久,查阅了很多资料,但其实很简单,网上说IOS端为了防止恶意浪费流量所以禁止了自动播放音乐。但是微信浏览器中,可以通过监听WeixinJSBridgeReady和invoke的方法兼容IOS。
首先引入微信提供的js-sdk的js文件:(在nuxt.config.js中配置) (src: 'https://res.wx.qq.com/open/js/jweixin-1.1.0.js')
事不宜迟先上代码:
<script type="text/ecmascript-6">
export default {
computed: {
/* <audio ref="audio"></audio> */
audio () {
return this.$refs.audio;
}
},
mounted () {
this.autoPlayMusic();
},
methods: {
autoPlayMusic () {
// 监听微信h5自动播放bgm
if (window.WeixinJSBridge) {
window.WeixinJSBridge.invoke('getNetworkType', {}, () => {
this.audio.play();
}, false);
} else {
document.addEventListener('WeixinJSBridgeReady', () => {
window.WeixinJSBridge.invoke('getNetworkType', {}, () => {
this.audio.play();
});
}, false);
}
}
}
};
</script>
这样之后,就可以打开微信h5应用后就可以听到音乐了。
注意了,网上有些说法是
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener("WeixinJSBridgeReady", function () {
let audio = document.getElementById('audio')
audio.play()
}, false)
})
但是查了一下发现,DOMContentLoaded这个方法表示页面加载完成之后就会触发,就是说,对于单页应用来说,一旦首屏加载完成了,那么它就触发,之后就不会出发了。但是我这个播放音乐组件是要等到它进去某个页面才会开始加载的,所以一开始用了这个方法怎么样也不响。
噢对了,如果eslint报错WeixinJSBridge is not defined的话,那么在eslint.js中加上这段话:
这就没问题了,这个表示如果出现WeixinJSBridge这个时,不进行监测(代表它是全局变量)
四、总结
还以为移动端兼容性比浏览器更容易解决,没想到就这个bug搞了我很久,特别是ios竟然不能自动播放,幸好还是解决了,最后上一下我的音乐播放组件(其实很简单的慕课网很多教程,不过多年之后感觉慕课网那个一下子就能做出来)
<template>
<div>
<!-- ct-icon-music就是一个光碟的图标,用的是iconfont里面的,可以自己去找 -->
<span @click="handleClick" :class="isStop ? '' : 'active'"></span>
<audio id="audio" ref="audio" loop="loop" autoplay="autoplay">
<source src="@/assets/music/exam.mp3" type="audio/mpeg">
您的浏览器不支持 audio 元素。
</audio>
</div>
</template>
<script type="text/ecmascript-6">
export default {
data () {
return {
isStop: false
};
},
computed: {
audio () {
return this.$refs.audio;
}
},
mounted () {
this.autoPlayMusic();
},
methods: {
/* 点击光碟图标就会播放/暂停音乐 */
handleClick () {
this.isStop = !this.isStop;
if (this.isStop) {
this.audio.pause();
this.$emit('stop');
} else {
this.audio.play();
this.$emit('start');
}
},
autoPlayMusic () {
// 监听微信h5自动播放bgm
if (window.WeixinJSBridge) {
window.WeixinJSBridge.invoke('getNetworkType', {}, () => {
this.audio.play();
}, false);
} else {
document.addEventListener('WeixinJSBridgeReady', () => {
window.WeixinJSBridge.invoke('getNetworkType', {}, () => {
this.audio.play();
});
}, false);
}
}
}
};
</script>
<style scoped>
.audio-container {
position: fixed;
top: 45px;
right: .5rem;
text-align: right;
.ct-icon-music {
display: inline-block;
&.active {
animation: musicRotate 1.5s linear infinite;
}
}
}
@keyframes musicRotate {
from { transform: rotateZ(0); }
to { transform: rotateZ(360deg); }
}
</style>