您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“mpvue如何開發音頻類小程序”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“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" :></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組件時依次觸發: onLoad,onShow,onReady,beforeMount,mounted
第一次進入已銷毀的子組件時依次觸發: onLoad,onReady,beforeMount,mounted
第二次進入已銷毀的子組件時依次觸發: onLoad,onShow,onReady
再次進入 未被銷毀的page組件、子組件時只觸發: onShow
mpvue文檔中建議盡量不要使用小程序的生命周期,這個應該是為了讓項目更好地適應支付寶小程序和頭條小程序等,所以才這樣建議大家盡量不要使用某一個小程序自身的api。
如果你們的小程序只是微信小程序(不考慮兼容其他平臺小程序),我建議 直接用小程序的生命周期 ,而不要用mpvue的生命周期,坑太多了。比如mpvue的created周期,初始化時所有的page都會執行,所以created這個周期是不能用了。
onUnload不觸發
小程序中與平常web開發不同的是,它的頁面會被緩存。舉個例子:
從 page1
跳轉到 page2
,再從 page2
返回 page1
,此時的 page1
還沒銷毀,不會觸發 onLoad
再重新渲染,而是直接使用之前的數據。從性能上來說,單純的返回不應該再請求api獲取數據重新渲染,這是對的,符合我們的預期。
而有時候,從 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看有沒相關解決辦法
盡量使用最新版本的框架,可能某些問題已經修復了的。實在解決不了的,建議想辦法繞過,換一種方法來實現。
以上是“mpvue如何開發音頻類小程序”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。