nuxt.js框架中 audio标签的坑

2019-12-04 09:49:05

爬坑地址 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>


  • 2017-02-16 15:14:14

    动态加载js和css

    开发过程中经常需要动态加载js和css,今天特意总结了一下常用的方法。

  • 2017-02-17 17:11:24

    mysql插入数据后返回自增ID的方法c

    产生的ID 每次连接后保存在服务器中。这意味着函数向一个给定客户端返回的值是该客户端产生对影响AUTO_INCREMENT列的最新语句第一个 AUTO_INCREMENT值的。这个值不能被其它客户端影响,即使它们产生它们自己的 AUTO_INCREMENT值。这个行为保证了你能够找回自己的 ID 而不用担心其它客户端的活动,而且不需要加锁或处理

  • 2017-02-21 07:59:45

    不会被 iOS 停掉的网页定时器

    其实这个标题略微有点标题党:iOS 中,除了少数服务(如播放音乐),大部分 App 在用户按了 Home 键之后,过不了多久就会被完全冻结,这对 Safari 同样适用。本文不考虑这样情况,只考虑 Safari 运行时,怎样让定时器持续工作。

  • 2017-02-24 15:04:10

    PHP 中的 9 个魔术方法

    这个标题有点牵强因为php有不只9种魔术方法, 但是这些将会引导你使用php魔术方法一个好的开始。它可能魔幻,但是并不需要魔杖。 这些'魔术'方法拥有者特殊的名字,以两个下划线开始,表示这些方法在php特定事件下将会被触发。这可能听起来有点自动魔法但是它真的很酷的,我们已经看过一个简单的例子在 last post,即我们使用一个构造器-使用这个作为我们第一个例子

  • 2017-02-24 15:06:18

    PHP 中的 9 个魔术方法

    这个标题有点牵强因为php有不只9种魔术方法, 但是这些将会引导你使用php魔术方法一个好的开始。它可能魔幻,但是并不需要魔杖。 这些'魔术'方法拥有者特殊的名字,以两个下划线开始,表示这些方法在php特定事件下将会被触发。这可能听起来有点自动魔法但是它真的很酷的,我们已经看过一个简单的例子在 last post,即我们使用一个构造器-使用这个作为我们第一个例子

  • 2017-02-24 15:07:05

    PHP 中的 9 个魔术方法

    这个标题有点牵强因为php有不只9种魔术方法, 但是这些将会引导你使用php魔术方法一个好的开始。它可能魔幻,但是并不需要魔杖。 这些'魔术'方法拥有者特殊的名字,以两个下划线开始,表示这些方法在php特定事件下将会被触发。这可能听起来有点自动魔法但是它真的很酷的,我们已经看过一个简单的例子在 last post,即我们使用一个构造器-使用这个作为我们第一个例子

  • 2017-02-24 15:53:02

    PHP中__get()和__set()的用法实例详解

    在PHP5中,预定义了两个函数“__get()”和“__set()”来获取和赋值其属性,对每个字段进行set和get的操作。只需要加上两个魔术方法即可