WeScale 定位爲音樂訓練小程序,初期規劃了基礎音階的三個訓練,以及他們的鏡像模式。css
後期看狀況更新追加其餘訓練。html
掃描下方小程序碼或在微信小程序中搜索 WeScale,便可使用。前端
Myou Aki:明神,北漂前端,總有奇奇怪怪的想法想要實現,適合作產品的前端vue
Dr.Chan:老陳,後端、前端通吃,長得帥說話又好聽的茂名吃貨node
Jackliu:大堅,產品、僞前端,不想作前端的產品不是好司機css3
明神每晚都要練着他的電吉他,敲着他的木魚,忽然一道光在腦海中閃過,機智的他迅速捕獲到,當晚凌晨三點作完了此次小程序的原型。git
以前和老陳搞了個 A股股票助手 — stock-helper ,此次有明神帶路,咱們都想積累點小程序開發的經驗,因而我和老陳就上車了。(滴~~學生卡)github
恰逢美團剛剛開源了 mpvue,短短几周就迅速得到幾千個 star,在 mpvue 開源前,最流行的應該是 wepy 。聽說用 mpvue,可以像德芙同樣順滑地使用 vue 寫微信小程序,因而咱們開始了踩坑之路。web
預計一週完成,畢竟是你們都有正經事要作,硬是拖到了兩週才完成。四個分支,總計提交 51次,越到 deadline 提交越多,目前已發佈 v1.0.0 版本,已審覈上線。 vuex
這個坑很常見,微信小程序不支持本地引用圖片、音頻、視頻,因此須要外鏈。對於圖片還可使用 Base64 編碼,直接在 html 或 css 中引用。根據圖片根據圖片體積或者可維護性考慮,酌情使用外鏈或者Base64編碼。
這個是 mpvue 的問題。常見問題能夠發現。解決的方法就是手動 npm run dev 一下。
mpvue 是兼容微信小程序的生命週期與 vue 的生命週期,也就是 vue 實例會接管小程序 Page 實例的生命鉤子,所以須要使用到小程序的生命週期鉤子時,可將相應的鉤子方法定義在 vue 實例中,如定義當前Page的分享標題內容圖片:
new Vue({
data () {
return {
score: ''
}
},
onShareAppMessage (res) {
return {
title: '我得到 ' + this.score + ' 分,快來一塊兒掌握基礎音階知識吧!',
path: '/pages/index/index',
imageUrl: 'https://wechat.dddog.com.cn/static/wescale.jpg'
}
}
})
複製代碼
這個不知道如何描述,大體是非當前頁面的 create() 會在當前頁面執行,解決方法,用小程序的 onload()/ vue 的 mounted(),遇到問題看圖就好:
不支持 vue 官方文檔:Class 與 Style 綁定中的 classObject 和 styleObject 語法。 暫不支持在組件上使用 Class 與 Style 綁定
不支持就不用咯~
mpvue 使得開發者可使用標準 html、css 去編寫小程序,當咱們查看 mpvue 項目中的 dist 文件夾時能夠發現,編寫的 html、css 被解析成了小程序的 wxml、wxss ,當然小程序的運行環境也就是非標準的 WebView 了。所以咱們web開發進行常用的 browser、navigator 實例天然是沒法使用了,取而代之的是使用小程序瀏覽器提供的API —— wx實例去操做native元素。至於 DOM 操做,即便在vue中也是不建議使用的,仍是用數據驅動去轉化吧。也就是說全部關於 BOM / DOM 的操做都不行。用 vue 第三方 UI庫時要注意, Dom 和 Bom 相關的 API 操做都沒法實現。 解決方案: 這塊主要是動畫不能用,那就用 css3 咯~
試着寫一個 swicth 的組件,發現渲染結果不對,查了緣由才發現,微信小程序也有個 switch 的組件。 解決方案: 更名字啊。命名規範!
按正常的套路去使用小程序的 api —— wx.createInnerAudioContext() 是沒法建立多聲道的。本次技術的難點也在於如何建立微信小程序的多聲道。查了一圈的資料,關於這點的資料甚少。查到一篇博客,經過建立多個 innerAudioContext 實例化對象,輪流調用的方式。對於原做者說小程序只能同時存在5個音頻實例這必定,不敢苟同。畢竟我直接建立了 30個都沒問題,哈哈
const audioContextNum = 30
let globalAudioContext = Array.from({ length: audioContextNum },
(v, k) => wx.createInnerAudioContext())
複製代碼
如何尋找當前可用的聲道,也是個難點,大體的思想是,把正在播放的實例封鎖,待實例的 onEnded() 回調執行時取消封鎖,使用時須要遍歷全部實例,尋找當前可用的實例,看實例代碼(與實際代碼有刪改):
// 自動尋找一個當前可用的 audioContext 實例
export function playedMusic (url) {
let contextList = store.getters.globalAudioContext
while (contextList !== store.getters.audioContextStatus.map(item => item === false).length) {
let audioContextStatus = store.getters.audioContextStatus
let index = store.getters.currentAudioIndex
// 若是當前可用,封鎖
if (audioContextStatus[index]) {
store.commit('setAudioContextStatus', {index, status: false})
break
} else {
// 不然 ++index
store.commit('setCurrentAudioIndex', ++index)
}
}
const resultPromise = new Promise((resolve, reject) => {
contextList[index].onPlay(() => {})
contextList[index].onError((res) => {
reject(res)
})
contextList[index].onEnded((res) => {
reset(resolve)
})
})
return resultPromise
}
複製代碼
實際開發過程當中發現。若是不預先對音頻進行緩存,實際播放時會有必定的延遲,視網絡狀況。解決方案是先預加載,而後存在小程序的緩存中,官網介紹緩存有 10 M,足夠用了。 首先是下載文件 wx.downloadFile(),獲得 tempFilePath,再把臨時文件保存爲本地文件 wx.saveFile(),獲得 savedFilePath,再將本地文件的的路徑保存在緩存中 wx.setStorage()。這麼多異步操做,固然用 Promise 再封裝一下啦。
多文件的下載、保存、緩存, 回調、遞歸的思想:
// 加載資源, 加載完隱藏loading
_load(0, () => {
// 更改Audio.js的config對象屬性。
config.musicUrl = JSON.parse(musicUrlTemp)
const temp = JSON.parse(musicUrlTemp)
temp.tempVerison = tempVerison
wx.setStorage({key: 'musicUrl', data: temp})
wx.hideLoading()
})
function _load (index, callback) {
if (!musicUrlArr[index]) {
callback()
} else {
downloadFile(musicUrlArr[index]).then((tempFilePath) => {
saveFile(tempFilePath).then((savedFilePath) => {
musicUrlTemp = musicUrlTemp.replace(
musicUrlArr[index],
savedFilePath
)
index++
_load(index, callback)
})
})
}
}
複製代碼
緩存是否存在及緩存版本的判斷:
// 判斷是否已有緩存且緩存版本正確
if (temp && temp.tempVerison === tempVerison) {
return false
}
複製代碼
遇到不少須要全局變量,特別是狀態的,最好統一管理。vue 的 vuex 是專爲 Vue.js 應用程序開發的狀態管理模式。使用過程遇到的坑是沒法使用它的輔助函數 mapState、 mapGetters、 mapActions、 mapMutations 等。看下 mpvue 的 issue 感受是 mpvue 的問題。 解決方案: 用最原始的 store.commit()、 store.getter
調用微信小程序的網絡請求 wx.request()、 wx.downloadFile() 之類 都須要 https 協議。 調微信的數據分析還要隔兩個小時獲取 access_token, 這些就是要服務器端的配置了。
條件: 域名及域名證書、服務器
獲取 token 及 服務器寫接口返回靜態文件及微信的數據分析接口 能夠參考這個, node.js 寫的,寫的很隨意,隨便看看。
參考博客:
// b.js
export let counter = {
count: 1
}
setTimeout(() => {
console.log('b.js-1', counter.count)
}, 1000)
// a.js
import { counter } from './b.js'
counter = {}
console.log('a.js-1', counter)
// Syntax Error: "counter" is read-only
複製代碼
雖然不能將 counter 從新賦值一個新的對象,可是能夠給對象添加屬性和方法。此時不會報錯。這種行爲類型與關鍵字 const 的用法。
// a.js
import { counter } from './b.js'
counter.count++
console.log(counter)
// 2
複製代碼
致謝全部參與產品、開發、測試,貢獻出創意想法與建議的小夥伴。
咱們有個小團隊,自嘲爲「鹹魚科技」,誰說鹹魚不能有夢想,哈哈。咱們還須要 UI、運營等,若是你有想法、有創意、有技能能夠加入咱們的小團隊!2333~