云開發(fā)練手項(xiàng)目:音樂播放器(上)

本文目錄:

  • 1.云開發(fā)的優(yōu)勢(shì)
  • 2.云開發(fā)的五大基礎(chǔ)能力
  • 3.基本結(jié)構(gòu)分析:
  • 4.寫輪播圖
  • 5.組件化開發(fā)流程
  • 6.數(shù)據(jù)監(jiān)聽器observers
  • 7.異步操作解決方案
  • 8.小程序中怎樣使用async函數(shù)
  • 9.第一個(gè)云函數(shù)getPlaylist
  • 10.小程序端調(diào)用云函數(shù)
  • 11.云函數(shù)獲取數(shù)據(jù)庫(kù)中的大于100條的數(shù)據(jù)
  • 12.小程序端調(diào)用云函數(shù)
  • 13.tcb-router
  • 14.本地?cái)?shù)據(jù)存儲(chǔ)
  • 15.音樂播放的控制
  • 16.如何實(shí)現(xiàn)組件間傳值
  • 17.給小程序設(shè)置全局屬性和方法

1.云開發(fā)的優(yōu)勢(shì)

正常的開發(fā)分為前端和后端
傳統(tǒng)的小程序開發(fā)完成之后需要進(jìn)行一個(gè)上線部署,而傳統(tǒng)部署的基本步驟有:購(gòu)買服務(wù)器、域名,備案,網(wǎng)絡(luò)防護(hù),負(fù)載均衡,監(jiān)控警告等。這些事情非常的繁瑣,讓人頭疼。


傳統(tǒng)模式.png

小程序云開發(fā),弱化了后端和運(yùn)維的工作,不需要搭建服務(wù)器。
云開發(fā)賦予了開發(fā)者穩(wěn)定,安全的讀取數(shù)據(jù),上傳文件的能力。


云開發(fā)模式.png

serverless=》無服務(wù)開發(fā)=》小程序的云開發(fā)

在云開發(fā)的核心理念中,函數(shù)即服務(wù),依托騰訊端提供的后端服務(wù),我們通過函數(shù)就可以實(shí)現(xiàn)調(diào)用,從而實(shí)現(xiàn)serverless

2.云開發(fā)的五大基礎(chǔ)能力

  • 1.云函數(shù):在云端運(yùn)行代碼,并且具有天然的鑒權(quán)機(jī)制。
  • 2.云數(shù)據(jù)庫(kù):既可以在云函數(shù)端操作,又可以在小程序端操作的非關(guān)系型json數(shù)據(jù)庫(kù)(類似moongodb)
  • 3.云調(diào)用:基于云函數(shù)免鑒權(quán)使用小程序開放接口的能力
  • 4.HTTP API:可以讓第三方服務(wù)很方便的在已有服務(wù)器上訪問云資源,實(shí)現(xiàn)與云開發(fā)的互通。
  • 5.云存儲(chǔ):在云端存儲(chǔ)文件,可以在云端控制臺(tái)可視化管理

我們可以通過云函數(shù)去定期的去第三方數(shù)據(jù)服務(wù)器拿數(shù)據(jù),然后更新到云數(shù)據(jù)庫(kù)中

1.什么是小程序的云開發(fā)?

傳統(tǒng)模式:小程序端展現(xiàn)的數(shù)據(jù)是發(fā)請(qǐng)求給后端拿到的

云開發(fā)模式:小程序端提供的原生接口可以直接去操作遠(yuǎn)程的云數(shù)據(jù)庫(kù),云函數(shù),云存儲(chǔ)。而我們根本根本不知道后端部署在那里。

2.什么是serverless?

打破前端和后端的物理隔離

當(dāng)我們使用后端服務(wù)的時(shí)候,不需要關(guān)注后端的ip地址是什么等等

在小程序官網(wǎng)上注冊(cè)賬號(hào),然后下載開發(fā)者工具,打開開發(fā)者工具,創(chuàng)建一個(gè)新項(xiàng)目。
需要注意一點(diǎn),APPID是每一個(gè)小程序的唯一標(biāo)識(shí),這個(gè)ID在官網(wǎng)賬號(hào)中的“開發(fā)”界面可以查看到。
進(jìn)入項(xiàng)目之后,第一次使用云開發(fā)的用戶需要點(diǎn)擊界面上方的“云開發(fā)”按鈕去開通服務(wù)。
首先可以選擇創(chuàng)建一個(gè)test作為開發(fā)環(huán)境的云服務(wù),等到項(xiàng)目上線再創(chuàng)建一個(gè)生產(chǎn)環(huán)境進(jìn)行使用。

3.基本結(jié)構(gòu)分析:

cloudfunctions=》云函數(shù)部分

miniprogram=》前端部分代碼

  • images圖片資源
  • pages創(chuàng)建小程序的時(shí)候自帶文件夾和文件(可以全部刪除)
  • style創(chuàng)建小程序的時(shí)候自帶樣式(可以全部刪除)
項(xiàng)目文件結(jié)構(gòu).png

app.js全局js文件
onLaunch:function(){}
當(dāng)小程序啟動(dòng)的時(shí)候觸發(fā)的鉤子函數(shù)
wx.cloud.init({ env:"在此處填入環(huán)境ID",
這個(gè)地方填入的是哪個(gè)ID,小程序自動(dòng)連接的就是對(duì)應(yīng)的環(huán)境,先填開發(fā)環(huán)境,上線的時(shí)候把這個(gè)地方改成生產(chǎn)環(huán)境就可以了。
traceUser: true
設(shè)置為true的時(shí)候,每一個(gè)訪問過我們的小程序的用戶都會(huì)被記錄,并且以倒序的順序進(jìn)行顯示
})

app.json全局配置文件

pages
//文件的路徑
window
//窗口的一些配置(頁面的最上方)
“sitemapLocation”:sitemap.json
//小程序開放的內(nèi)部搜索,對(duì)應(yīng)的配置文件sitemap.json,決定了我們的小程序界面是否能被搜索到,在小程序優(yōu)化中可以用到
tabBar
//小程序封裝的一個(gè)對(duì)象,有color,selectedColor,list等常用屬性。
//對(duì)應(yīng)的小程序頁面下方的導(dǎo)航,最少兩個(gè),最多五個(gè)

app.wxss全局樣式
README.md小程序的開發(fā)說明
project.config小程序項(xiàng)目的配置文件,突出為整個(gè)項(xiàng)目
iconfont下載圖標(biāo)很方便,可以自由選擇格式,大小,以及顏色(點(diǎn)擊下載,直接下載到本地就可以直接使用了)

把style文件夾中的guide.wxss和app.wxss中的小程序自帶樣式都去除掉,樣式都是我們自定義
寫最初的幾個(gè)主頁面:
先在app.json中的“pages”中把框架自帶的頁面全部刪除,然后加上自己要寫的幾個(gè)頁面路徑,保存之后在對(duì)應(yīng)的路徑就會(huì)自動(dòng)生成頁面文件。(手動(dòng)把框架自帶的頁面文件刪除掉)

4.寫輪播圖

小程序原生自帶swiper組件,里面的項(xiàng)目swiper-item,在swiper-item里放image標(biāo)簽
小程序自帶的block標(biāo)簽,建議把wx:for寫在block上面,block不會(huì)真實(shí)渲染,在輪播圖這里我們?cè)趕wiper-item外面包裹一層block,然后wx:for渲染寫在block標(biāo)簽上

swiper的常用屬性:

  • indicator-dots="true",顯示導(dǎo)航的小點(diǎn),默認(rèn)為flase
  • autoplay=“true”自動(dòng)播放
  • interval=“2000”自動(dòng)播放的間隔是2000ms
  • duration=”1000“滑動(dòng)播放時(shí)長(zhǎng)為1000ms

小程序自帶的image標(biāo)簽的常用屬性

  • mode=”scaleToFill“,保證圖片完全覆蓋當(dāng)前image容器,這種縮放模式下圖片非常有可能會(huì)產(chǎn)生變形,實(shí)際效果不好
  • mode=”aspectFit“,讓圖片能夠完整的顯示在容器中,缺點(diǎn)是有可能會(huì)讓容器留白
  • mode=”widthFix“,讓圖片能夠完全覆蓋容器,同時(shí)保持圖片的寬高比不變,同時(shí)給image標(biāo)簽增加width100%height100%的樣式,保證image能夠完整覆蓋父元素。這個(gè)縮放模式是實(shí)際項(xiàng)目中最常用的。

5.組件化開發(fā)流程

組件:在用戶界面開發(fā)領(lǐng)域,組件是一種面向用戶的,獨(dú)立的,可復(fù)用的交互元素的封裝。

組件的組成:
結(jié)構(gòu)=》wxml
邏輯=》js
樣式=》wxss

組件的設(shè)計(jì)原理:高內(nèi)聚,低耦合,單一職責(zé),避免過多參數(shù)
封裝第一個(gè)組件:歌曲列表組件
1.創(chuàng)建
首先在項(xiàng)目的miniprogram文件夾下新建一個(gè)文件夾components,然后在components中右鍵創(chuàng)建對(duì)應(yīng)的組件(這里是playlist)
2.在頁面中引用和使用
在頁面的json中進(jìn)行引用

{
  "usingComponents": {
    "x-playlist": "/components/playlist/playlist"
  },
  "enablePullDownRefresh":true
}

在頁面的wxml中進(jìn)行使用

3.傳遞數(shù)據(jù),這里想把頁面中的歌曲數(shù)據(jù)playlist傳遞給組件

<view class="playlist-container">
  <block wx:for="{{playlist}}" wx:key="_id">
    <x-playlist playlist="{{item}}"></x-playlist>
  </block>
</view>

4.組件接收頁面?zhèn)鬟f過來的數(shù)據(jù)
在組件的js文件中使用properties進(jìn)行接收(需要指定所接收到的數(shù)據(jù)的類型,不指定的話會(huì)報(bào)錯(cuò))

  properties: {
    // 接收父組件傳遞的參數(shù),并規(guī)定參數(shù)的類型
    playlist: {
      type: Object
    }
  },

5.組件在wxml就可以使用接收到的數(shù)據(jù)(渲染數(shù)據(jù))
<image src="{{playlist.picUrl}}" class="playlist-img"></image>
在小程序中的背景圖片只能使用本地圖片,不允許使用網(wǎng)絡(luò)圖
如何把小圖片轉(zhuǎn)換成base64?在百度上搜索“在線制作base64”,很多網(wǎng)站都可以轉(zhuǎn)換

6.數(shù)據(jù)監(jiān)聽器observers

監(jiān)聽對(duì)象下的屬性,和接受父組件傳遞參數(shù)的properties在同一級(jí)。
['對(duì)象.屬性']
監(jiān)聽到的值不能直接用this.setData賦給對(duì)象的屬性本身,這樣會(huì)陷入數(shù)據(jù)監(jiān)聽死循環(huán)
解決方法:在組件的內(nèi)部重新定義一個(gè)數(shù)據(jù)進(jìn)行賦值。

  // 數(shù)據(jù)監(jiān)聽器
  observers: {
    // 監(jiān)聽對(duì)象下面的屬性
    ['playlist.playCount'](count) {
      this.setData({
        _count: this._tranNumber(count, 2)
      })
    }
  },

巧妙的去除數(shù)字后面的小數(shù)點(diǎn)
let numStr = num.toString().split('.')[0]
小程序?qū)τ趙x:for循環(huán)提供了一個(gè)wx:key=“*this”,其中*this代表的就是元素本身=====>對(duì)于循環(huán)的純數(shù)組而言,如果循環(huán)的是對(duì)象數(shù)組,則可以直接綁定對(duì)象中的唯一屬性,如id,在不寫別名的情況下,小程序會(huì)自動(dòng)識(shí)別循環(huán)的每個(gè)對(duì)象下的id屬性,并且進(jìn)行綁定。
注意:在循環(huán)出來的對(duì)象不會(huì)動(dòng)態(tài)變化的情況下,key值可以綁定的隨意些,否則必須要綁定足夠有辨識(shí)度的唯一標(biāo)識(shí),否則小程序無法識(shí)別元素的動(dòng)態(tài)變化。

7.異步操作解決方案

傳統(tǒng)的回調(diào)地獄式異步編程寫法:

setTimeout(() => {
  console.log(1)
  setTimeout(() => {
    console.log(2)
    setTimeout(() => {
      console.log(3)
    }, 3000);
  }, 2000);
}, 1000);

promise是es6的異步操作解決方案,字面意思就是承諾,promise有三個(gè)狀態(tài),pending代表等待,fulfilled代表成功,rejected代表失敗,狀態(tài)一旦改變,則無法回退。
上面的異步操作的promise版本寫法

new Promise((resolve,reject)=>{
  setTimeout(() => {
    console.log(1)
    resolve()
  }, 1000);
}).then((res)=>{
  setTimeout(() => {
    console.log(2)
    resolve()
  }, 2000);
}).then((res)=>{
  setTimeout(() => {
    console.log(3)
    resolve()
  }, 3000);
})

8.小程序中怎樣使用async函數(shù)

es7的異步操作解決方案:async和await
云函數(shù)默認(rèn)支持es7語法,但是小程序開發(fā)環(huán)境還不行,所以要想在小程序端歡快的使用es7語法,則首先需要解決環(huán)境問題。
把 regenerator/runtime.js 文件引用到有使用 async/await 的文件當(dāng)中。
import regeneratorRuntime from '../../utils/runtime.js'
注意:regeneratorRuntime必須叫這個(gè)名字,不能自定義
普通函數(shù)沒有寫renturn則沒有返回值,而async函數(shù)的返回值是一個(gè)promise對(duì)象

onLoad:function(options){
    this.foo()
}
async foo(){
    console.log('foo')
    //await一定要async函數(shù)里面才能發(fā)揮正常作用
    let res = await this.timeout()
    console.log(res)
},
timeout(){
    return new Promise((resolve,reject) => {
        setTimeout(()=>{
            console.log(1)
            resolve('resolved')
        },1000)
    })
}

執(zhí)行結(jié)果
foo
1
resolved

9.第一個(gè)云函數(shù)getPlaylist

getPlaylist這個(gè)云函數(shù)我們首先需要安裝三個(gè)依賴,發(fā)請(qǐng)求拿數(shù)據(jù)用的
在cloudfunctions文件夾中新建一個(gè)getPlaylist文件夾,然后在這個(gè)文件夾下打開終端命令行工具,輸入npm init -y將getPlaylist初始化為一個(gè)npm管理下的項(xiàng)目,然后一次安裝下面這三個(gè)依賴

npm install --save request
npm install --save request-promise
npm install --save wx-server-sdk@latest

在getPlaylist文件夾下引入
const rp = require('request-promise')
然后開始寫發(fā)請(qǐng)求的代碼

exports.main = async(event, context) => {
  const playlist = await rp(URL).then((res) => {
    return JSON.parse(res).result
  })
}

如果要在云函數(shù)中打印一些數(shù)據(jù)用來調(diào)試,但是這個(gè)打印信息不會(huì)顯示在調(diào)試器中,因?yàn)檎{(diào)試器屬于前端工具,而云函數(shù)屬于后端部分的代碼。所以我們可以先上傳并部署:云端安裝依賴(不上傳node_modules)
云函數(shù)調(diào)試位置:云開發(fā)=》云函數(shù)=》云端測(cè)試,當(dāng)前這個(gè)請(qǐng)求不需要參數(shù),把默認(rèn)的參數(shù)清空,然后“運(yùn)行測(cè)試”就可以在下面看到測(cè)試的結(jié)果了。
返回結(jié)果是null是因?yàn)楫?dāng)前的getPlaylist云函數(shù)并沒有寫返回值。
注意:在云函數(shù)中的任何一處修改要想生效 都需要進(jìn)行上傳和部署。

至此,我們已經(jīng)在云函數(shù)中拿到了想要的歌單信息數(shù)據(jù)playlist ,接下來要把這個(gè)數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中,首先需要在數(shù)據(jù)庫(kù)中“創(chuàng)建集合”,集合名稱定義為playlist,往數(shù)據(jù)庫(kù)中插入數(shù)據(jù)只能一條一條的插入。

10.云函數(shù)往數(shù)據(jù)庫(kù)插入不重復(fù)的數(shù)據(jù)

給數(shù)據(jù)庫(kù)插入信息之前需要先在getPlaylist云函數(shù)中初始化數(shù)據(jù)庫(kù)
const db = cloud.database()
接下來調(diào)用數(shù)據(jù)庫(kù),往playlist集合中插入數(shù)據(jù),并且同時(shí)插入一個(gè)cerateTime字段,記錄數(shù)據(jù)產(chǎn)生的時(shí)間。
serverDate獲取當(dāng)前服務(wù)器的時(shí)間

  for (let i = 0,len = playlist; i < len; i++) {
    await db.collection('playlist').add({
      data: {
        ...playlist[i],
        createTime: db.serverDate(),
      }
    }).then((res) => {
      console.log('插入成功')
    }).catch((err) => {
      console.error('插入失敗')
    })
  }

上面這段代碼有一個(gè)明顯的問題,那就是當(dāng)多次讀取數(shù)據(jù)的時(shí)候,重復(fù)的歌單信息就會(huì)被多次添加,所以每次讀取歌單信息都應(yīng)該和數(shù)據(jù)庫(kù)當(dāng)前已有的歌單信息進(jìn)行對(duì)比,相同的信息不會(huì)被重復(fù)添加。

首先定義一個(gè)list,先獲取歌單信息已有的信息,存儲(chǔ)在list變量中
const list = await db.collection('playlist').get()
然后將list和playlist對(duì)比去重,將不重復(fù)的數(shù)據(jù)放置到newData變量中。
定義一個(gè)flag,true代表默認(rèn)的“不重復(fù)”

  const newData = []
  for (let i = 0, len1 = playlist.length; i < len1; i++) {
    let flag = true
    for (let j = 0, len2 = list.data.length; j < len2; j++) {
      if (playlist[i].id === list.data[j].id) {
        flag = false
        break
      }
    }
    if (flag) {
      newData.push(playlist[i])
    }
  }

這樣一開始插入的數(shù)據(jù)也要從playlist變?yōu)楝F(xiàn)在的不重復(fù)數(shù)據(jù)形成的數(shù)組newData

11.云函數(shù)獲取數(shù)據(jù)庫(kù)中的大于100條的數(shù)據(jù)

現(xiàn)在還遺留一個(gè)問題就是在云函數(shù)中獲取數(shù)據(jù)中的信息,只能獲取100條,在小程序代碼中最多只能獲取到20條。所以現(xiàn)在我們需要突破100條這個(gè)限制。
解決思路:假如有210條數(shù)據(jù),分三次請(qǐng)求,最后再把這三次請(qǐng)求拿到的數(shù)據(jù)進(jìn)行匯總,就可以獲得全部的數(shù)據(jù)。
全部數(shù)據(jù)list不能通過const list = await db.collection('playlist').get()簡(jiǎn)單獲得,需要進(jìn)行下面的優(yōu)化:
首先需要獲得當(dāng)前數(shù)據(jù)總的條數(shù)
const countResult = await db.collection('playlist').count()
countResult拿到的是一個(gè)對(duì)象,其中的total屬性對(duì)應(yīng)的數(shù)據(jù)的數(shù)量。
·const total = countResult.total·
定義每次取數(shù)據(jù)的數(shù)量
const MAX_LIMIT = 100
求出應(yīng)該取幾次數(shù)據(jù)
const batchTimes = Math.ceil(total / MAX_LIMIT)
需要等待幾次拿數(shù)據(jù)的請(qǐng)求完成完成后才能拼裝出真正完整的數(shù)據(jù)。

  const tasks = []
  for (let i = 0; i < batchTimes; i++) {
    let promise = db.collection('playlist').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
    tasks.push(promise)
  }
  let list = {
    data: []
  }
  if (tasks.length > 0) {
    list = (await Promise.all(tasks)).reduce((acc, cur) => {
      return {
        data: acc.data.concat(cur.data)
      }
    })

  }

剛才所寫的從指定的音樂接口拿數(shù)據(jù),然后將數(shù)據(jù)插入數(shù)據(jù)庫(kù)的操作,我們希望能夠在云函數(shù)中定時(shí)自動(dòng)觸發(fā)。
需要在對(duì)應(yīng)的云函數(shù)文件夾中新建一個(gè)config.json文件

配置好之后一定要右鍵云函數(shù)文件夾,“上傳觸發(fā)器”,這樣觸發(fā)器才能生效。
config.json文件名是規(guī)定好的,不能改。myTrigger是自定義的觸發(fā)器名稱。

{
  "triggers":[
    {
      "name":"myTrigger",
      "type":"timer",
      "config":"0 0 10,14,16,23 * * * *"
    }
  ]
}

12.小程序端調(diào)用云函數(shù)

接下來就是小程序端讀取數(shù)據(jù),并且把數(shù)據(jù)渲染到頁面上。
我們新創(chuàng)建一個(gè)music云函數(shù)“新建Nodejs云函數(shù)”,然后在這個(gè)music云函數(shù)中寫調(diào)用數(shù)據(jù)庫(kù),獲取數(shù)據(jù)的邏輯代碼。
skip和limit方便我們獲取指定條數(shù)的數(shù)據(jù)以及進(jìn)行分頁。
orderBy表示排序,第一個(gè)參數(shù)是排序依據(jù)的字段名稱,第二個(gè)參數(shù)‘desc’代表逆序。

return await cloud.database()
    .collection('playlist')
    .skip(event.start).limit(event.count)
    .orderBy('createTime','desc')
    .get().then((res)=>{return res})

接下來在小程序界面的js文件中請(qǐng)求云函數(shù)music,取第0-15條數(shù)據(jù)

wx.cloud.callFunction({
   name:'music',
   data:{
     start:0,
     count:15
   }
}).then((res) => {
      this.setData({
        playlist: this.data.playlist.concat(res.result.data)
      })
      wx.stopPullDownRefresh()
    })

如果實(shí)現(xiàn)觸底下拉,請(qǐng)求更多15條的歌單信息?
微信自帶onReachBottom屬性,監(jiān)聽頁面觸底,我們把剛才請(qǐng)求歌單的方法進(jìn)行一下優(yōu)化,并封裝在_getPlaylist方法中,當(dāng)觸底時(shí)自動(dòng)觸發(fā),并將請(qǐng)求的新數(shù)據(jù)拼接給playlist鼻變量。

  _getPlaylist() {
    wx.showLoading({
      title: '加載中',
    })
    wx.cloud.callFunction({
      name: 'music',
      data: {
        start: this.data.playlist.length,
        count: MAX_LIMIT,
        $url:'playlist'
      }
    }).then((res) => {
      this.setData({
        playlist: this.data.playlist.concat(res.result.data)
      })
      wx.stopPullDownRefresh()
      wx.hideLoading()
    })
  }

調(diào)用云函數(shù)的data參數(shù)中的$url:'playlist'代表要調(diào)用的對(duì)應(yīng)云函數(shù)中對(duì)應(yīng)的router
當(dāng)用戶下拉整個(gè)頁面的時(shí)候,怎么實(shí)現(xiàn)頁面刷新,并重置當(dāng)前已加載的歌單信息數(shù)據(jù)?
在當(dāng)前頁面的json文件中增加屬性"enablePullDownRefresh":true,這代表允許當(dāng)前頁面下拉刷新,同時(shí)在頁面的js文件中有一個(gè)onPullDownRefresh屬性,是監(jiān)聽用戶下拉動(dòng)作的

  onPullDownRefresh: function() {
    this.setData({
      playlist:[]
    })
    this._getPlaylist()
  },

微信暫時(shí)無法知道用戶的下拉動(dòng)作是什么時(shí)候結(jié)束的,所以可以在請(qǐng)求數(shù)據(jù)結(jié)束的時(shí)候增加wx.stopPullDownRefresh()用來停止下拉刷新的動(dòng)畫。

13.tcb-router

一個(gè)用戶在一個(gè)云環(huán)境中只能創(chuàng)建50個(gè)云函數(shù)
相似的請(qǐng)求歸類到同一個(gè)云函數(shù)處理
tcb-router是一個(gè)koa風(fēng)格的小程序云開發(fā)云函數(shù)輕量級(jí)類路由庫(kù),主要用于優(yōu)化服務(wù)端函數(shù)處理邏輯。
在對(duì)應(yīng)的云函數(shù)中進(jìn)行安裝
npm install --save tcb-router
在對(duì)應(yīng)的云函數(shù)的js文件中引用
const TcbRouter = require('tcb-router')
在云函數(shù)的js文件中的入口函數(shù)中進(jìn)行使用,app可以創(chuàng)建當(dāng)前TcbRouter服務(wù),這樣TcbRouter就會(huì)自動(dòng)的處理event參數(shù)和路由轉(zhuǎn)發(fā),在結(jié)束的時(shí)候別忘了通過app.serve()把當(dāng)前的服務(wù)返回。
···
exports.main = async(event, context) => {
const app = new TcbRouter({
event
})
return app.serve()
}
···
用ctx.body把數(shù)據(jù)返回給小程序端

  app.router('playlist', async(ctx, next) => {
    ctx.body = await cloud.database().collection('playlist')
      .skip(event.start)
      .limit(event.count)
      .orderBy('creatTime', 'desc')
      .get()
      .then((res) => {
        return res
      })
  })

小程序端在調(diào)用云函數(shù)時(shí),還要在data中增加一個(gè)屬性
`$url:'xxx``
xxx對(duì)應(yīng)的是name對(duì)應(yīng)云函數(shù)中詳細(xì)的路由。

頁面之間的跳轉(zhuǎn)

wx.navigateTo({
    url:`../../pages/musiclist/musiclist?playlistid=${this.properties.playlist.id}`,
})

上面的properties寫成data貌似也不會(huì)有什么問題。
這里問號(hào)后面的動(dòng)態(tài)路由是要告訴musiclist頁面我要進(jìn)入的是哪一個(gè)歌單,
musiclist頁面的onload生命周期函數(shù)中的options就可以獲取到這個(gè)傳遞過來的數(shù)據(jù),然后依據(jù)這個(gè)id去調(diào)用云函數(shù),獲取對(duì)應(yīng)歌單的歌曲列表。

  onLoad: function(options) {
    console.log(options)
    wx.showLoading({
      title: '加載中',
    })
    wx.cloud.callFunction({
      name: 'music',
      data: {
        playlistid: options.playlistid,
        $url: 'musiclist'
      }
    }).then((res) => {
      console.log(res)
      const pl = res.result.playlist
      this.setData({
        musiclist: pl.tracks,
        listInfo: {
          coverImgUrl: pl.coverImgUrl,
          name: pl.name,
        }
      })
      this._setMuscilist()
      wx.hideLoading()
    })
  },

當(dāng)小程序開發(fā)的頁面層級(jí)比較多的時(shí)候,每次保存都會(huì)讓小程序從主頁開始,這樣很不方便,可以在上方的編譯模式中新建一個(gè)編譯模式,啟動(dòng)頁面定位為想要的頁面,另外比如有啟動(dòng)參數(shù),則需要填寫上,如playlistId = 28171112148

實(shí)現(xiàn)循環(huán)的歌曲列表,當(dāng)前點(diǎn)擊的歌曲動(dòng)態(tài)添加playing高亮樣式。
在小程序中,所有自定義的屬性都用data-開頭

<block wx:for="{{musiclist}}" wx:key="id">
  <view class="musiclist-container {{item.id===playingId?'playing':''}}" bind:tap="onSelect" data-musicid="{{item.id}}" data-index="{{index}}">
  </view>
</block>

實(shí)現(xiàn)原理:如果當(dāng)前點(diǎn)擊事件觸發(fā)的歌曲id,和自定義屬性musicid是相等的,則可以判定當(dāng)前歌曲為用戶點(diǎn)擊的。
點(diǎn)擊觸發(fā)的onSelect事件,當(dāng)前點(diǎn)擊事件的event參數(shù)有兩個(gè)屬性,一個(gè)是target,另一個(gè)是currentTarget,而綁定的自定義屬性是在currentTarget上面。原因如下:
關(guān)于事件的幾個(gè)要素:

  • 事件源,觸發(fā)事件的真正的元素
  • 事件處理函數(shù)
  • 事件對(duì)象,事件處理函數(shù)的默認(rèn)參數(shù)event,event中有target屬性和currentTarget屬性,target對(duì)應(yīng)的是事件源,currentTarget指的是綁定事件的元素
  • 事件類型
    onSelect(event) {
      const musicid = event.currentTarget.dataset.musicid
      this.setData({
        playingId: musicid
      })
      wx.navigateTo({
        url: `../../pages/player/player?musicid=${musicid}&index=${event.currentTarget.dataset.index}`,
      })
    }

小程序的自帶組件就是一些標(biāo)簽,我們通過給標(biāo)簽配置不同的屬性,就可以實(shí)現(xiàn)不同的效果

組件中的properties和data都是用來定義組件數(shù)據(jù)的,它們的差別:
properties:調(diào)用方傳給給組件的
data:組件內(nèi)部使用的數(shù)據(jù)

14.本地?cái)?shù)據(jù)存儲(chǔ)

將數(shù)據(jù)保存到storage中

wx.setStorage({
  key: 'musiclist',
  data: this.data.musiclist,
})

對(duì)于不需要進(jìn)行頁面操作和顯示的數(shù)據(jù),我們可以不定義在data中,直接定義一個(gè)全局變量就行,這樣的話進(jìn)行賦值也會(huì)更加的方便。
let musiclist = []
運(yùn)用同步方法去給這個(gè)變量賦值,因?yàn)楂@取到值需要直接進(jìn)行下一步的邏輯處理
musiclist = wx.getStorageSync('musiclist')

動(dòng)態(tài)的設(shè)置頁面上方的導(dǎo)航標(biāo)題

wx.setNavigationBarTitle({
  title: music.name,
})

給容器動(dòng)態(tài)綁定一個(gè)鋪滿全部的背景圖片
<view class="player-container" style="background:url({{picUrl}}) center/cover no-repeat"></view>

在項(xiàng)目中使用iconfont
進(jìn)入官網(wǎng)iconfont.cn,將想要的圖標(biāo)點(diǎn)擊購(gòu)物車圖標(biāo)=>加入購(gòu)物車
新建項(xiàng)目=>加入項(xiàng)目,比如:demo,點(diǎn)擊fontclass =>點(diǎn)擊查看在線鏈接,生成代碼,會(huì)生成一個(gè)css文件的鏈接地址,可以下載到本地,然后將css文件修改成wxss文件(也可以不下載,直接拷貝鏈接中的代碼放入項(xiàng)目中)

此時(shí)圖標(biāo)wxss文件是放在項(xiàng)目的根目錄下的,我們要在app.wxss文件中進(jìn)行引用,然后在項(xiàng)目中就可以通過class進(jìn)行使用。
@import "iconfont.wxss";

15.音樂播放的控制

小程序提供了一個(gè)wx.getBackgroundAudioManager()方法用來控制唯一背景音樂的播放,在要播放背景音樂的頁面的js文件首先定義一個(gè)變量,去獲取全局唯一的背景音頻管理器
const backgroundAudioManager = wx.getBackgroundAudioManager()
然后通過給backgroundAudioManager 的src屬性賦值,就可以實(shí)現(xiàn)背景音樂的額播放,同時(shí)注意,還需要同時(shí)設(shè)置title,否則會(huì)報(bào)錯(cuò)

backgroundAudioManager.src = JSON.parse(res.result).data[0].url
backgroundAudioManager.title = music.name

如果需要在任何界面都可以聽到這個(gè)背景音樂,則需要在app.json中配置(和pages同級(jí))

"requireBackgroundModes":[
    "audio"
],

同時(shí)還可以通過為頁面下方的mini播放器設(shè)置圖片、歌手和專輯名稱

backgroundAudioManager.coverImgUrl = music.al.picUrl
backgroundAudioManager.singer = music.ar[0].name
backgroundAudioManager.epname = music.al.name

背景音樂的暫停
backgroundAudioManager.pause()
背景音樂的播放
backgroundAudioManager.play()

在css中,animation-play-state: paused;可以讓動(dòng)畫停在當(dāng)前那一幀,只需要?jiǎng)討B(tài)的給做動(dòng)畫的元素添加上這個(gè)屬性,就可以實(shí)現(xiàn)動(dòng)畫播放的開始與暫停

backgroundAudioManager.duration=>獲取當(dāng)前背景音樂的時(shí)長(zhǎng),但是有時(shí)候獲取到的是underfined,解決辦法:

if(backgroundAudioManager.duration == undefined)
    //上面這樣樣判定是不合理的,因?yàn)閚ull==undefined也會(huì)是true
if(typepf backgroundAudioManager.duration != 'undefined')
    //應(yīng)該像上面那樣判定

怎么動(dòng)態(tài)的給data中的對(duì)象中某一個(gè)屬性賦值

this.setData({
    ['object.xxx']:'yyyyyy'
})

this.data.progress這樣給data中的數(shù)據(jù)賦值可以成功,但不會(huì)自動(dòng)響應(yīng)到頁面上

backgroundAudioManager有一些事件,我們需要在這些事件上綁定對(duì)應(yīng)的回調(diào)函數(shù),如:
背景音樂可以播放的時(shí)候:backgroundAudioManager.onPlay
backgroundAudioManager.seek()=》重新定義當(dāng)前背景音樂的正在播放的時(shí)間點(diǎn),參數(shù)為要跳轉(zhuǎn)的秒

子組件激活父元素的事件
this.triggerEvent('musicEnd')
父組件在調(diào)用子組件的標(biāo)簽中進(jìn)行接收,同時(shí)接收到響應(yīng)后去觸發(fā)自身的onNext事件
<x-progress-bar bind:musicEnd="onNext"></x-progress-bar>

進(jìn)度條的拖拽事件和backgroundAudioManager.onTimeUpdate事件是不能同時(shí)進(jìn)行的,否則會(huì)造成拖拽的時(shí)候進(jìn)度條會(huì)一直閃的畫面,這里的解決辦法是設(shè)置一個(gè)鎖:isMoving
當(dāng)拖拽開始的時(shí)候isMoving = true
拖拽結(jié)束的時(shí)候isMoving = false
當(dāng)isMoving = false的時(shí)候,onTimeUpdate里面的代碼才去執(zhí)行。

小程序控制組件的顯示與隱藏
hidden="{{flag}}" //flag為true時(shí)隱藏,為false顯示

接收父組件傳遞過來的數(shù)據(jù),如果接收的數(shù)據(jù),這個(gè)數(shù)據(jù)除了類型,還有其他的屬性,則需要寫成對(duì)象的形式,如果只需要聲明一個(gè)類型,則可以不用對(duì)象的形式

  properties: {
    isLyricShow: {
      type: Boolean,
      value: false
    },
    lyric: String
  },

16.如何實(shí)現(xiàn)組件間傳值

自組件給父組件傳值:
自組件:

this.triggerEvent('timeUpdata',{
   currentTime
})

調(diào)用這個(gè)子組件的父組件的頁面上對(duì)應(yīng)的標(biāo)簽
<x-process-bar bind:musicEnd="onNext" bind:timeUpdate="timeUpdata"></x-process-bar>
父組件通過觸發(fā)自定義事件接受到這個(gè)數(shù)據(jù)currentTime的同時(shí)想傳遞給另一個(gè)子組件,
通過定義事件處理函數(shù),通過給另一個(gè)需要接收數(shù)據(jù)的子組件標(biāo)簽上起名一個(gè)class

  timeUpdata(event){
    this.selectComponent('.lyric').update(event.detail.currentTime)
  },

這樣的話,另一個(gè)子組件就可以通過自身的update事件,成功接收到currentTime這個(gè)數(shù)據(jù)了。
注意:這時(shí)候子組件的update事件相當(dāng)于被父組件給調(diào)用了一次。

歌詞的滾動(dòng)是利用了<scroll-view>組件,scroll-top屬性可以規(guī)定這個(gè)容器里的內(nèi)容向上滾動(dòng)的距離,scroll-y屬性表示上下方向的滾動(dòng),scroll-with-animatio="true"表示開啟滾動(dòng)過渡動(dòng)畫。

scroll-top的屬性值只能是px,而我們?cè)O(shè)置的歌詞單行高度是64rpx,不同手機(jī)這個(gè)rpx代表的實(shí)際尺寸都不同,所有這里需要進(jìn)行一個(gè)換算

lifetimes: {
  ready() {
    wx.getSystemInfo({
      success: function(res) {
        lyricHeight = res.screenWidth / 750 * 64
      },
    })
  }
},

小程序?qū)挾仁?50rpx,把屏幕的寬度除以750,得到的就是1rpx

17.給小程序設(shè)置全局屬性和方法

app.js中的

    this.globalData = {
      playingMusicId:-1
    }
  },
  setPlayMusicId(musicId){
    this.globalData.playingMusicId = musicId
  },
  getPlayMusicId(){
    return this.globalData.playingMusicId
  }

在頁面獲取全局的屬性或者方法

const app =getApp()
//在播放的音樂的方法中設(shè)置屬性
app.setPlayMusicId(musicid)

在對(duì)應(yīng)的組件的頁面生命周期中,當(dāng)頁面展示的時(shí)候,去觸發(fā)方法獲取到全局變量

pageLifetimes: {
  show() {
    this.setData({
      playingId: parseInt(app.getPlayMusicId())
    })
  }
},

小程序下方自帶的mini控制面板的暫停和播放對(duì)應(yīng)的也就是背景音樂監(jiān)聽事件中的onPause和onPlay
backgroundAudioManager.onPlaybackgroundAudioManager.onPause

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近從1月25日到2月8日的兩周之內(nèi)抽空編寫了 小打卡 微信小程序,該產(chǎn)品主要是針對(duì)每日早起、健身、閱讀等習(xí)慣進(jìn)行...
    徐佳義閱讀 14,691評(píng)論 10 120
  • 子曰:“朝聞道,夕死可矣?!?孔子講:“如果早上能夠知曉了大道,晚上死掉也是心甘的?!?大道到底是什么?我想應(yīng)該是...
    哈皮波閱讀 802評(píng)論 0 0
  • 知行格-英雄之旅 日精進(jìn)第 14 日 日閱讀:《看見》九十章 日鍛煉:走了好多路,還沖了個(gè)次(跟舍友出去浪,到處逛...
    巖松不姓李不姓白閱讀 254評(píng)論 0 2
  • 是春天的第一縷風(fēng) 把枝頭含苞待放的那一枚火種點(diǎn)燃 一朵紅色的火焰燃起來了 風(fēng)剛一張口輕輕一吹 又一朵紅色的火焰燃起...
    指尖蝶舞的花園閱讀 325評(píng)論 0 4
  • 素未謀面的你 漸漸駛?cè)雰?nèi)心深處 笑淚、喜悲 與你共享人生百味 許久未見 每次 你的名字都會(huì)在我心中泛起漣漪 可回憶...
    我和你的青空下閱讀 206評(píng)論 0 3

友情鏈接更多精彩內(nèi)容