≡
  • 网络编程
  • 数据库
  • CMS技巧
  • 软件编程
  • PHP笔记
  • JavaScript
  • MySQL
位置:首页 > 网络编程 > vue.js

mpvue开发音频类小程序踩坑和建议分析

人气:525 时间:2019-04-15

这篇文章主要为大家详细介绍了mpvue开发音频类小程序踩坑和建议分析,具有一定的参考价值,可以用来参考一下。

感兴趣的小伙伴,下面一起跟随四海网的小编两巴掌来看看吧!

这是我第一次开发小程序,开发的产品是音频类的,在大佬的建议下采用了 mpvue ,一周时间把功能都做出来,由于不太熟悉mpvue和微信小程序,足足用了一周时间来改bug才出来一个能用的版本,在这里整理分享下我开发时遇到的一些问题和给出一些建议。

【图片暂缺】

在 Linux 上开发小程序

在公司电脑装了双系统,日常用的是 Ubuntu 系统,Linux或Mac的开发环境对前端相对来说会友好一些。微信小程序官方的开发者工具只有 Windows 和 Mac 版本,所以这就尴尬了。

不过还好,发现已经有大神在GitHub上做了Linux的支持,推荐给大家: Linux微信web开发者工具 。 根据教程安装使用即可,使用时就用 ./bin/wxdt 命令打开。不过用了几天后面觉得不太方便,就索性切回Windows系统用官方最新的版本了。

 

封装wx.request为Promise

 

wx.request 用于发起http请求,但平时习惯了Promise的写法,所以还是封装一下这个方法为Promise的形式。 我看很多小程序会使用fly 这个库。

但个人觉得发起请求不需要那么强大的功能,小程序本身就应该是一个轻量级的东西,引入一个库可能会导致项目打包变大,可能让小程序更卡,所以本着能自己写就自己写吧的心态,索性自己封装一下算了。

在 src/utils ,新建一个 request.js :

代码如下:


const apiUrl = 'https://your server.com/api/'
const request = (apiName, reqData, isShowLoading = true) => {
 // 某些请求可能不需要显示loading
 if (isShowLoading) {
 wx.showLoading({
  title: '正在努力加载中',
  mask: true
 })
 }

 return new Promise(function (resolve, reject) {
 wx.request({
  url: apiUrl + apiName,
  method: 'POST',
  data: reqData,
  header: {
  'content-type': 'application/json' // 默认值
  },
  success (res) {
  if (res.data.code === 0) {
   // 与后端约定code=0时才是正常的
   resolve(res)
  } else {
   reject(res)
  }
  },
  fail (err) {
  reject(err)
  },
  complete (res) {
  wx.hideLoading()
  }
 })
 })
}

export default request

当然这是个简化版的,我实际项目中还会在初始化时加入一些 token 之类的参数,大家能看明白是这样封装成Promise的就可以啦。

 

使用vant-weapp

 

小程序已经支持了npm安装,但不太会弄。还是按网上方法,将项目clone下来放进static目录下。

代码如下:


git clone https://github.com/youzan/vant-weapp.git

然后将 vant-weapp 的 dist 目录拷贝到项目的static目录下(尽可能精简,删掉一些奇奇怪怪的如 .github 的东西,所以直接使用dist目录),改名为 vant (也可以不改名)。全局使用时,可以在 app.json 引入:

代码如下:


 "usingComponents": {
 "van-button": "/static/vant/button/index",
 "van-field": "/static/vant/field/index"
 },

注意:需要打开微信开发者工具中的ES6转ES5功能

一开始以为使用起来和web端的没啥差别,但没想到那么麻烦。比如:在vue中是可以使用 v-model 的,但在mpvue中的小程序中不能使用,只能

代码如下:


<van-field :value="password" type="password" @change="pwdChange" input-class="myClass" />

而且不能随意灵活添加class修改组件的样式,需要vant组件支持提供外部样式才可修改,比如上面的 van-field 是通过 input-class 来添加样式控制的,很不方便。而且某些内部样式由于没有外部样式表,根本改不了。

综上: 在微信小程序使用第三方组件库不太方便,样式修改比较麻烦,如果产品是有UI设计时, 尽量不使用 ,有时候自己实现样式可能更快,而且项目体积更小。

 

使用vuex

 

mpvue官方的快速模板中是将vuex放在 counter 这个page目录下,可能习惯了vue官方写法的很多同学(包括我)不太喜欢,所以最好就改为vuex官方的写法。

在src目录下建一个 store 的文件夹,分别建以下文件:

【图片暂缺】

项目不太复杂时不建议使用modules,使用起来比较麻烦。

贴一下 index.js 的代码,其他的 actions.js , getters.js 按官方的写法就好啦。

代码如下:



import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'

export default new Vuex.Store({
 actions,
 getters,
 state,
 mutations,
 strict: debug,
 plugins: debug ? [createLogger()] : []
})

vuex/dist/logger 是vuex在开发环境可以自动打印日志的工具,debug比较方便,建议使用。 然后在 src/main.js 引入:

代码如下:



import Vue from 'vue'
import App from './App'
import store from '@/store'

Vue.config.productionTip = false
App.mpType = 'app'

Vue.prototype.$store = store

const app = new Vue({
 store
})
app.$mount()

这样就可以在项目中正常使用啦,完全支持 mapState , mapActions , mapGetters 的写法,比如在 pages/index/index.vue 中使用:

代码如下:



<script>
import { mapState, mapActions } from 'vuex'
export default {
 computed: {
 ...mapState(['myAudio'])
 },
 methods: {
 ...mapActions(['myActions'])
 },
 created () {
 this.myActions() //调用vuex中的方法
 }
}
</script>

 

踩坑指南

 

其实大多数坑可能是mpvue的,很多情况也是自己不熟悉小程序生命周期导致的一些奇奇怪怪的bug。

 

mpvue是支持小程序原生组件的

 

mpvue会将 div 编译为小程序中的 view 。一开始我不了解,以为用了mpvue后就不能使用小程序原生支持的组件了,比如 swiper , scroll-view 等,小程序是支持的,可以放心使用哈哈。

 

npm run build后样式丢失

 

本来在开发环境正常的,然后准备发版 npm run build 后发现样式丢失了。然后重新 npm start 排查问题,样式还是丢失的。内心此时是mmp的:npm run build丢失就算了,我没改什么东西重新npm start后为什么还是丢失,之前还是正常的呀?

刚开始怀疑是缓存什么的问题,删掉的dist目录,重启开发者工具,甚至重启电脑都试了一下,这是我遇到的超级诡异的问题之一。

冷静下来想到:之前的版本是正常的,一定是新版本引入了什么导致了打包样式的丢失。于是回滚版本一个个build排查问题,最后找到了原因: 在一个page中引入了其他page,即在页面中import另一个页面。

在我这里的具体例子是:我在 pages/index/index.vue 中想做底部共用一个tabbar,页面根据tabbar的值来显示对应的子级页面: pages/page1/index.vue 和 pages/page2/index.vue 。

所以我将这两个页面当做子组件来引入了: import Page1 from '@/pages/page1' ,一开始没有问题,等重启项目,或者build后就发现样式丢失了。

这可能是mpvue打包机制的一个限制,即 页面不能将另一个页面当子组件来引用 ,否则会导致样式丢失。

 

背景音频的src无法读取

 

项目中希望用户退出小程序后依然能播放音频,所以用到了背景音频的api: wx.getBackgroundAudioManager()。

代码如下:



this.audio = wx.getBackgroundAudioManager()
this.audio.src = 'http://ws.stream.qqmusic.qq.com/M500001VfvsJ21xFqb.mp3?guid=ffffffff82def4af4b12b3cd9337d5e7&uin=346897220&vkey=6292F51E1E384E061FF02C31F716658E5C81F5594D561F2E88B854E81CAAB7806D5E4F103E55D33C16F3FAC506D1AB172DE8600B37E43FAD&fromtag=46' 
this.audio.title = '此时此刻' //注意必填
this.audio.epname = '此时此刻'
this.audio.singer = '许巍'
this.audio.coverImgUrl = 'http://y.gtimg.cn/music/photo_new/T002R300x300M000003rsKF44GyaSk.jpg?max_age=2592000'

title 和 src 赋值后会直接播放音频,后面的几个属性建议也填上,因为播放背景音频时微信是有个界面需要封面图和歌手名称等的。

如果想要获取当前正在播放的音频src,本来以为通过 this.audio.src 来获取就可以了但是有bug。

在开发者工具中是可以正常获取的,即开发时是没问题的,但在真机上返回的是 undefined ,因此不能用 this.audio.src 来获取当前播放的音频url,得用一个变量来存这个数据。

 

直接使用音频的currentTime可能渲染不及时

 

currentTime用于显示当前的播放进度,但我用在子组件中时经常更新不及时,打印是正常的,但试图渲染不及时,有时候需要点击一下才能重新渲染,这可能是mpvue使用时才会遇到。

所以建议还是项目自身维护一套背景音频的变量比较好一点,比如放在 vuex 中。监听 BackgroundAudioManager.onTimeUpdate() 方法每次赋值到自身维护的变量中。

 

音频的onCanplay方法不一定每个音频都会触发

 

一开始我监听在 onCanplay 方法,将音频的时长信息 duration 赋值到vuex中存起来,但发现 onCanplay 有时候是不会触发的,比如重新赋值src播放下一首时,很尴尬。

所以不要太依赖onCanplay这个方法,还好目前直接使用 audio.duration 好像不会出现像上面的 currentTime 渲染不及时的问题,所以就这样用着先。

 

音频播放结束,即onStop后,不能再通过audio.play()的方法重新播放,得重新赋值src

 

正常来说,音频播放结束后,音频的src是不变的,再次 play() 应该是可以的。但在小程序中偏偏不行,得重新赋值src才能重新播放,这应该是小程序的一个bug。。。

所以需要判断一下 暂停 和 停止 的情况,用不同的办法播放。正常来说,音频暂停时 currentTime 是不为0的,而结束时 currentTime 会为0。

所以可以通过 currentTime (最好是自己维护的变量)来判断暂停和停止的情况: 如果currentTime不为0,表示是暂停的情况,可以用 play() ,如果小于等于0,则重新赋值src播放 :

代码如下:



if (currentTime) {
 this.audio.play()
} else {
 this.audio.src = 'xx.mp3'
}

 

mpvue不支持直接在template上直接绑定函数

 

这个是mpvue文档上有写的,不过一开始并不是很理解,也踩坑了,所以在这里提一下,避免不知道的同学踩坑找半天。

代码如下:



<template>
 <div v-for="(item, index) in list" :key="index">{{ formatItem(item) }}</div>
</template>
 
<script>
export default {
 data () {
 return{
  list: [1, 2, 3]
 }
 },
 methods: {
 formatItem (item) {
  return `我是${item}`
 }
 }
}
</script>

上面的代码应该是日常vue中比较常用的,就是将数据传参给方法做一些处理,这个在mpvue中是不支持的,会被编译成一个空字符串。

 

小程序中可放心使用css3的一些特性

 

比如高斯模糊

代码如下:



filter: blur(50px);

如果要使用动画,尽量用 css 动画代替 wx.createAnimation

在实际使用时, wx.createAnimation 做动画其实很卡,性能很差,所以在需要使用动画时,建议尽量使用css做动画。

在小程序中是支持css动画的, transition , animation , @keyframes 这些特性都支持。

比如做一个div一直旋转的动画,大家可以对比一下两个版本:

wx.createAnimation 版本

原理:通过setInterval()不断更新div的旋转位置

代码如下:



<template>
 <div class="cover" :animation="animationData"></div>
</template>

<script>
export default {
 data () {
 return {
  animationData: '',
  animation: '',
  rotateCount: 0,
  timer: ''
 }
 },
 components: {

 },
 methods: {
  startRotate () {
  this.timer = setInterval(() => {
   this.rotateAni(++this.rotateCount)
  }, 100)
  },
  rotateAni (n) {
  if (!this.animation) {
   return
  }
  // 每100毫秒旋转10度
  this.animation.rotate(10 * n).step()
  this.animationData = this.animation.export()
  }
 },
 onShow () {
  // 页面从隐藏到显示时才执行
  if (!this.animation) {
  this.animation = wx.createAnimation()
  this.startRotate()
  }
 },
 onReady () {
  // 第一次初始化时会执行
  if (!this.animation) {
  this.animation = wx.createAnimation()
  this.starRotate()
  }
 },
 onHide () {
 // 页面隐藏时会执行,避免频繁的setData操作,将定时器停掉
 this.timer && clearInterval(this.timer)
 },
 beforeDestroy () {
 // 页面卸载,也停掉定时器
 this.timer && clearInterval(this.timer)
 }
}
</script>

<style scoped lang="scss">
 .cover {
 left: 20px;
 bottom: 70px;
 border-radius: 50%;
 background: #fff;
 position: absolute;
 width: 50px;
 height: 50px;
 background: rgba(0, 0, 0, 0.2);
 box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.2);
 border: 1px solid rgba(255, 255, 255, 0.5);
 overflow: hidden;
 z-index: 10000;
 }
</style>

使用css的 @keyframes 做旋转动画

代码如下:



<template>
 <div class="cover" :style="coverStyle"></div>
</template>

<script>
export default {
}
</script>

<style scoped lang="scss">
 // 定义一个动画名为 rotate
 @keyframes rotate {
 0%,
 100% {
  transform: rotate(0deg);
 }
 100% {
  transform: rotate(360deg);
 }
 }
 .cover {
 left: 20px;
 bottom: 70px;
 border-radius: 50%;
 background: #fff;
 position: absolute;
 width: 50px;
 height: 50px;
 background: rgba(0, 0, 0, 0.2);
 box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.2);
 border: 1px solid rgba(255, 255, 255, 0.5);
 overflow: hidden;
 z-index: 10000;
 // 使用动画
 animation: rotate 4s linear infinite;
 }
</style>

用js写的动画需要控制好setInterval的间隔时间和旋转角度,比较难调。而用css写动画很简单,性能比js好,代码量也很少。

 

使用css动画时建议开启硬件加速

 

为了动画更流畅,想尽办法做优化,虽然不知道有没效果,反正用了再说[手动滑稽]。

可以用will-change和transform: translate3d(0,0,0) 开启硬件加速。我也不太会用,具体用法大家自行百度Google。

代码如下:



will-change: auto;
transform: translate3d(0, 0, 0);

 

iPhoneX需要底部导航条预留34px(68rpx)的高度

。

 

由于小程序中不能设置 viewport-fit=cover ,所以也就没有web中的安全区域说法,目前主流的做法是通过 wx.getSystemInfoSync() 判断是否是ipx,若是则给页面底部撑高34px。

代码如下:



const res = wx.getSystemInfoSync()
if (res.model.indexOf('iPhone X') >= 0) {
 this.isIpx = true
}

注意是用 res.model.indexOf('iPhone X') ,在开发者工具的iPhone X中,model是全等于 iPhone X 的,但在真机中往往拿到的值是 iPhone X GZxxx ,即后面可能会带一串东西,所以用 indexOf 才是比较稳的,而且对 iPhone XR 等机型也适用。

由于还有其他安卓机的全面屏,不太可能一一判断,而且某些安卓全面屏是没有用iPhone底部的工具条的(不存在冲突的情况),所以我们只判断iPhone X的情况就可以了,其他全面屏就不需要给底部预留了。

至于全面屏布局的适配,需要用 flex 布局或者获取屏幕宽高来慢慢调了,建议最好用flex布局自适应处理。

 

for循环中的子组件click事件无法触发

 

Page -> 父组件 -> 子组件 ,在子组件click后 $emit 一个事件出来,发现无法触发。

这个bug一开始没有出现,但偶然 npm run build 出现的,然后排查原因,后面即使回滚所有版本再npm start也还会出现。好像不触发则已,一发就不可收拾,这又是一个大坑,搜issue和加群问人,当晚下班回家研究到1点多都没有解决。

第二天继续研究,感觉可能是框架的原因,最后尝试升级一下mpvue版本,没想到就正常了。直接使用quick-strat项目的 mpvue 版本是 2.0.0, mpvue 和 mpvue-template-compiler 升级到最新 2.0.6 就解决了。

事后查看mpvue版本记录,果然是框架本身原因,并且找到了 issue 。

 

npm run build后代码报错,再build一次可能报另一些错

 

解决: 没找到原因,可能是引入vant导致的,打包时丢失了部分文件。多build几次,或者重启下小程序开发者工具就正常了。

mpvue中created() 钩子会在页面初始化时全部一起触发,尽量不要用 小程序生命周期的理解

进入已销毁的page组件时依次触发: onl oad,onShow,onReady,beforeMount,mounted

第一次进入已销毁的子组件时依次触发: onl oad,onReady,beforeMount,mounted

第二次进入已销毁的子组件时依次触发: onl oad,onShow,onReady

再次进入 未被销毁的page组件、子组件时只触发: onShow

mpvue文档中建议尽量不要使用小程序的生命周期,这个应该是为了让项目更好地适应支付宝小程序和头条小程序等,所以才这样建议大家尽量不要使用某一个小程序自身的api。

如果你们的小程序只是微信小程序(不考虑兼容其他平台小程序),我建议 直接用小程序的生命周期 ,而不要用mpvue的生命周期,坑太多了。比如mpvue的created周期,初始化时所有的page都会执行,所以created这个周期是不能用了。

 

onUnload不触发

 

小程序中与平常web开发不同的是,它的页面会被缓存。举个例子:

  1. 从 page1 跳转到 page2 ,再从 page2 返回 page1 ,此时的 page1 还没销毁,不会触发 onLoad 再重新渲染,而是直接使用之前的数据。从性能上来说,单纯的返回不应该再请求api获取数据重新渲染,这是对的,符合我们的预期。
  2. 而有时候,从 page2 返回 page1 时,我们希望 page1 是重新获取数据渲染的。比如在 page2 做了一个退出登录的操作,此时再返回 page1 时,还是会看到之前的数据。实际上我们的预期是:由于已经退出登录了, page1 的数据应该被销毁了。

在平常的web开发中,遇到上面的问题,我们可能是不管缓存,每次返回 page1 都再次请求api渲染最新的数据,牺牲掉部分性能从而保证逻辑的正确性。

在mpvue中我也尝试这样干了:想在 page1 的 onUnload() 生命周期中销毁数据,但是没有成功。即使在 page2 退出登录时,采用 wx.reLaunch() 重新刷一遍, page1 的 onUnload() 生命周期也没有执行。所以 onUnload() 是有可能不执行的,建议慎用。

最后还是得想办法做到 在 page2 控制 page1 的数据销毁或保留 。想到这里, vuex 就不自觉浮现在眼前了,如果page1的数据是通过vuex来控制的,那么我在page2就可以用vuex来灵活管理其他页面的数据了。

如果page2做退出登录操作时,就让page1的数据销毁,如果是不退出登录正常返回,page1的数据还是正常,做到灵活控制。

个人平时web开发很少用 vuex ,因为项目比较简单不用那么复杂的全局数据传递。但在小程序中,建议全局使用 vuex 来控制所有数据(当然是得根据需求来用)。

 

总结

 

第一次开发小程序就直接上了mpvue,可能有些坑已经很多同学总结过了,有些坑可能是不熟悉而导致的,但自己没有去踩过一遍可能不够深刻。

有两种坑会比较难啃:

框架本身的问题,如mpvue2.0.0出现的子组件无法触发事件的问题。 开发者工具和真机运行环境不一致导致的坑。

遇到真机和开发者工具不一致的情况,可按以下步骤排查:

  • 有可能是缓存,可以杀掉之前的版本再跑起来
  • 手机微信版本太低,可能api不支持,用wx.canIUse打印一下
  • 手机端某些属性不支持读取,比如上面的this.audio.src,可以在真机打印调试一下
  • 代码在手机端运行有报错,可以在手机端开启调试,看一下log
  • 微信设计上的坑,百度下是否有相关的案例和解决办法

而遇到mpvue框架的问题可以:

  • 去搜一下mpvue的issue看有没相关解决办法
  • 尽量使用最新版本的框架,可能某些问题已经修复了的。实在解决不了的,建议想办法绕过,换一种方法来实现。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持四海网。

本文来自:http://www.q1010.com/184/6836-0.html

注:关于mpvue开发音频类小程序踩坑和建议分析的内容就先介绍到这里,更多相关文章的可以留意四海网的其他信息。

关键词:vue.js

您可能感兴趣的文章

  • 从0到1构建vueSSR项目之node以及vue-cli3的配置
  • 浅析vue中的MVVM实现原理
  • 用VueJS写一个Chrome浏览器插件的实现方法
  • 分析基于iview-ui的导航栏路径(面包屑)配置
  • vuex实现及简略解析(小结)
  • vue基础之data存储数据及v-for循环用法示例
  • Vue 事件处理操作实例分析
  • vue基础之v-bind属性、class和style用法分析
  • 从0到1构建vueSSR项目之路由的构建
  • Vue项目引发的「过滤器」使用教程
上一篇:分析基于vue-cli3快速发布一个fullpage组件
下一篇:从0到1构建vueSSR项目之node以及vue-cli3的配置
热门文章
  • Vue 报错TypeError: this.$set is not a function 的解决方法
  • vue实现动态添加数据滚动条自动滚动到底部的示例代码
  • vue项目设置scrollTop不起作用(总结)
  • vue项目中使用vue-i18n报错的解决方法
  • iview实现select tree树形下拉框的示例代码
  • 分析关于element级联选择器数据回显问题
  • vue项目打包后打开页面空白解决办法
  • 解决element ui select下拉框不回显数据问题的解决
  • element-ui table span-method(行合并)的实现代码
  • element-ui 设置菜单栏展开的方法
  • 最新文章
    • 理解vue ssr原理并自己搭建简单的ssr框架
    • vue favicon设置以及动态修改favicon的方法
    • vue-router启用history模式下的开发及非根目录部署方法
    • 从零开始在NPM上发布一个Vue组件的方法步骤
    • Element input树型下拉框的实现代码
    • Vue 报错TypeError: this.$set is not a function 的解决方法
    • Vue.js组件高级特性实例分析
    • 浅谈VueJS SSR 后端绘制内存泄漏的相关解决经验
    • 分析Vue.js自定义tipOnce指令用法实例
    • 浅谈vuex actions和mutation的异曲同工

四海网收集整理一些常用的php代码,JS代码,数据库mysql等技术文章。