完整效果:

歌手界面.gif

封装axios.all方法

在此界面中由于要获取众多歌手(A-Z)的歌手信息,又因为接口中没有直接提供(A-Z)排列的接口数据,需要A-Z逐个去获取一遍,如果就简单地提供接口,使用的时候会很不方便,造成代码冗余。所以将axios中的all方法进行了封装定制。在原来的network类中新增了一个all方法,用于返回批量promise网络请求的结果。

  1. export default {
  2. get (path = '', data = {}) {
  3. return new Promise((resolve, reject) => {
  4. axios.get(path, {
  5. params: data
  6. })
  7. .then(response => {
  8. resolve(response.data)
  9. })
  10. .catch(error => {
  11. reject(error)
  12. })
  13. })
  14. },
  15. post (path = '', data = {}) {
  16. return new Promise((resolve, reject) => {
  17. axios.post(path, data)
  18. .then(response => {
  19. resolve(response.data)
  20. })
  21. .catch(error => {
  22. reject(error)
  23. })
  24. })
  25. },
  26. all (list) {
  27. return new Promise((resolve, reject) => {
  28. axios.all(list)
  29. .then(axios.spread((...result) => {
  30. resolve(result)
  31. }))
  32. .catch(err => {
  33. reject(err)
  34. })
  35. })
  36. }
  37. }

再在具体的接口类中,定义获取热门歌手和A-Z歌手的请求方法,最后提供一个方法将两组请求的数据拼接在一起返回。

  1. // 获取前五位热门歌手
  2. export const getHotArtists = () => {
  3. return new Promise((resolve, reject) => {
  4. Network.get('top/artists?offset=0&limit=5')
  5. .then(data => {
  6. resolve(data.artists)
  7. })
  8. .catch(err => {
  9. reject(err)
  10. })
  11. })
  12. }
  13. // 根据字母获取歌手
  14. export const getLetterArtists = (letter) => {
  15. return new Promise((resolve, reject) => {
  16. const letterArtists = []
  17. Network.all([
  18. // 华语男、女、乐队
  19. Network.get(`artist/list?offset=0&limit=5&area=7&type=-1&initial=${letter}`),
  20. // 欧美男、女、乐队
  21. Network.get(`artist/list?offset=0&limit=5&area=96&type=-1&initial=${letter}`)
  22. ])
  23. .then(data => {
  24. data.forEach(item => {
  25. // 将指定字母的华语和欧美歌手全部解构到一个数组
  26. letterArtists.push(...item.artists)
  27. })
  28. resolve(letterArtists)
  29. })
  30. .catch(err => {
  31. reject(err)
  32. })
  33. })
  34. }
  35. // 将热门歌手、(A-Z)歌手数据统一拼接后返回
  36. export const getAllArtists = () => {
  37. return new Promise((resolve, reject) => {
  38. // keys用于存放歌手序列:热、A、B、C —— Z
  39. const keys = ['热']
  40. // list用于存放所有歌手数据
  41. const list = [getHotArtists()]
  42. for (let i = 65; i <= 90; i++) {
  43. const char = String.fromCharCode(i)
  44. keys.push(char)
  45. list.push(getLetterArtists(char))
  46. }
  47. Network.all(list)
  48. .then(data => {
  49. resolve({
  50. keys: keys,
  51. list: data
  52. })
  53. })
  54. .catch(err => {
  55. reject(err)
  56. })
  57. })
  58. }

歌手详情

歌手详情界面同样只需要添加detail子路由,添加类别和id传入即可
步骤参考首页跳转歌单详情,也是在歌手界面调用一下this.$router.push(/singer/detail/singer/${id})并把id传给detail路由就可以了。Detail界面在created方法中判别一下路由的类型参数,根据不同类型调用不同接口,渲染不同数据

  1. // Detail.vue
  2. created () {
  3. switch (this.$route.params.type) {
  4. case 'personalized':
  5. getPlayList({ id: this.$route.params.id })
  6. .then((data) => {
  7. this.playList = data.playlist
  8. })
  9. .catch((err) => {
  10. this.playList = {
  11. name: '啊喔,网络开了点小差 ~'
  12. }
  13. console.log(err)
  14. })
  15. break
  16. case 'albums':
  17. getAlbum({ id: this.$route.params.id })
  18. .then((data) => {
  19. this.playList = {
  20. name: data.album.name,
  21. coverImgUrl: data.album.picUrl,
  22. tracks: data.songs
  23. }
  24. })
  25. .catch((err) => {
  26. this.playList = {
  27. name: '啊喔,网络开了点小差 ~'
  28. }
  29. console.log(err)
  30. })
  31. break
  32. case 'singer':
  33. getArtistsSongs({ id: this.$route.params.id })
  34. .then((data) => {
  35. this.playList = {
  36. name: data.artist.name,
  37. coverImgUrl: data.artist.picUrl,
  38. tracks: data.hotSongs
  39. }
  40. })
  41. .catch((err) => {
  42. this.playList = {
  43. name: '啊喔,网络开了点小差 ~'
  44. }
  45. console.log(err)
  46. })
  47. break
  48. }
  49. }

image.png

$nextTick的使用

这是第一次在项目中使用到这个方法,之所以会用到这个方法是因为在歌手界面中要做一个歌手类别吸顶栏的效果,需要获取到各类别标题距离文档顶部的距离。但是由于歌手中的数据全部都是动态获取添加的,如果单单只是在watch上监听歌手数据list的变化是不够的,因为list数据发生变化只是代表通过axios已经获取到了数据,但还不一定渲染到界面上,所以这时候获取dom会失败。这时候就可以在监听list的函数中使用$nextTick方法,在$nextTick方法内部去获取就一定能获取到DOM元素的信息。
至于此方法的详细过程可以参考以下文章:
Vue中的nextTick与updated生命周期比较

image.png