WeScale 技術篇 —— mpvue 與微信小程式的火花

Jackliu007888發表於2018-04-08

介紹

專案介紹

WeScale 定位為音樂訓練小程式,初期規劃了基礎音階的三個訓練,以及他們的映象模式。

  • 數字簡譜
  • 字母簡譜
  • 數字簡譜對字母簡譜

後期看情況更新追加其他訓練。

產品展示

掃描下方小程式碼或在微信小程式中搜尋 WeScale,即可使用。

WeScale 技術篇 —— mpvue 與微信小程式的火花

人員介紹

  • Myou Aki:明神,北漂前端,總有奇奇怪怪的想法想要實現,適合做產品的前端

  • Dr.Chan:老陳,後端、前端通吃,長得帥說話又好聽的茂名吃貨

  • Jackliu:大堅,產品、偽前端,不想做前端的產品不是好司機

緣起

明神每晚都要練著他的電吉他,敲著他的木魚,突然一道光在腦海中閃過,機智的他迅速捕獲到,當晚凌晨三點做完了這次小程式的原型。

之前和老陳搞了個 A股股票助手 — stock-helper ,這次有明神帶路,我們都想積累點小程式開發的經驗,於是我和老陳就上車了。(滴~~學生卡)

恰逢美團剛剛開源了 mpvue,短短几周就迅速獲得幾千個 star,在 mpvue 開源前,最流行的應該是 wepy 。據說用 mpvue,能夠像德芙一樣順滑地使用 vue 寫微信小程式,於是我們開始了踩坑之路。

專案統計

預計一週完成,畢竟是大家都有正經事要做,硬是拖到了兩週才完成。四個分支,總計提交 51次,越到 deadline 提交越多,目前已釋出 v1.0.0 版本,已稽核上線。

踩到的坑

  • 微信小程式不能使用本地資源

這個坑很常見,微信小程式不支援本地引用圖片、音訊、視訊,所以需要外鏈。對於圖片還可以使用 Base64 編碼,直接在 html 或 css 中引用。根據圖片根據圖片體積或者可維護性考慮,酌情使用外鏈或者Base64編碼。

  • 新增頁面需要 npm run dev

這個是 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(),遇到問題看圖就好:

lifecycle

  • Class 與 Style 繫結

不支援 vue 官方文件:Class 與 Style 繫結中的 classObject 和 styleObject 語法。 暫不支援在元件上使用 Class 與 Style 繫結

不支援就不用咯~

  • 沒有 BOM/DOM 操作

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 寫的,寫的很隨意,隨便看看。

  • ES6 的模組動態引用

參考部落格:

  1. ES6 模組中的值屬於【動態只讀引用】。只說明一下複雜資料型別。
  2. 對於只讀來說,即不允許修改引入變數的值, import 的變數是隻讀的,不論是基本資料型別還是複雜資料型別。當模組遇到 import 命令時,就會生成一個只讀引用。等到指令碼真正執行時,再根據這個只讀引用,到被載入的那個模組裡面去取值。
  3. 對於動態來說,原始值發生變化, import 載入的值也會發生變化。不論是基本資料型別還是複雜資料型別。
// 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~



相關文章