博客 / 詳情

返回

基於React全家桶開發「網易雲音樂PC」項目實戰(四)

前言

前言

hello大家好我是「風不識途」,如果首次閲讀本系列請點擊,正在學習React的小夥伴可以克隆該項目,參考學習,嘗試做一些小功能,下面我們開始完成本系列最重要的音樂播放器列表▶需要完成內容如下↓;

項目預覽和源碼

  • 在線預覽地址👉:點我跳轉到雲音樂
  • 項目Gihub地址👉:Musci 163 (如果覺得項目還不錯的話 👏,就給個 star ⭐ 鼓勵一下吧~)

    • 沒有外網的小夥伴👉:Gitee倉庫

最近更新

更新功能

  • 登錄功能:

    • 暫時只支持“163郵箱”或“手機號”登錄
    • 每日推薦歌單(只有登錄成功才能查看)
    • 個人主頁 & 個人收藏歌單 & 評論歌曲 & 點讚歌曲評論 & 創建歌單
  • 本地存儲歌曲列表:

    • 不管之後是否刷新瀏覽器,只要在歌曲列表中就會持久化存儲
    • (刷新瀏覽器,歌曲列表依然存在)
  • 歌曲列表:

    • 對歌曲列表支持拖拽排序,並會對播放順序進行改變
  • 搜索音樂框:

    • 優化在搜索歌曲時,支持鍵盤"↑"+"↓"來切換搜索歌曲內容
  • 頭部進度條:

    • 在頁面路由跳轉&網絡請求時"添加頭部進度條"顯示
  • 404頁:

    • 添加404頁,在路由沒有匹配的頁面時,會顯示404頁面

修改BUG&ToDoList

點擊查看👉近期優化調整

點擊查看👉TO-DO-LIST

界面功能展示(新開發)

歌曲搜索(↑↓選擇)

支持對歌曲列表進行拖拽排序

登錄演示

每日推薦

image-20210530083650349

個人主頁

image-20210530083718485

音樂列表

下面開始完成本節(稍複雜),歌曲列表播放組件,支持功能如下:

  • 支持點擊某一項播放歌曲
  • 歌詞實時滾動
  • 和首頁的歌詞組件互斥效果
  • 清除全部歌曲
  • 拖拽更改順序

1.頁面佈局搭建

  1. 頁面佈局搭建

  1. 點擊按鈕顯示和隱藏(播放列表) 和 添加過渡效果

  1. 和歌詞展示控件互斥.(歌詞列表顯示,關閉歌詞展示)

  1. Conten內容搭建

  1. 實現點擊一項,播放對應的音樂,並且背景高亮

    • 在點擊當前項後,將id傳遞函數,根據id派發action,請求播放列表詳情信息
    • 將播放音樂函數傳遞給子組件
    • 實現當音樂播放結束或手動點擊上一首或後下一首:

      • 對應item背景高亮跟着切換 (根據保存在redux中的currentSongIndex)

  1. 實現點擊刪除按鈕,阻止事件冒泡和清除點擊播放歌曲
  2. 點擊清除全部按鈕,清除全部音樂,並播放下一首音樂

2.歌詞高亮效果

歌詞接口

  • 請求接口:

    • http://39.102.36.212:3000/lyr...
    • http://39.102.36.212:3000/lyr...
  • 對請求下來的歌詞進行解析

    • 在上一節咱們已經存放到redux當中,參考👉歌詞格式化
  • 實現當前播放的歌詞高亮顯示,獲取當前是否播放狀態滾動歌詞

3.歌詞滾動效果

  • 根據當前播放歌詞的 index,實現滾動效果
  • 獲取DOM,計算scrollTop,實現滾動

音樂列表拖拽排序sortablejs

説明

  • <font color='red'>當需要對某些表格的行或者列進行拖拽排序時</font>

    • 並不侷限表格
    • 只要是你想拖拽的某一行都可以

使用拖拽排序步驟

  1. 安裝
npm install sortablejs --save
yarn add sortablejs
  1. 導入
import Sortable from 'sortablejs';
  1. 配置參數
import React, { useRef } from 'react';

// other hook
const playlistRef = useRef();// 用於獲取DOM元素

useEffect(() => {
  // 獲取列表項父元素
  const el = playlistRef.current.querySelector('.main-playlist');
  new Sortable(el, {
    sort: true,
    animation: 200,
    onEnd: function (evt) {  // 拖拽結束髮生該事件
      let tempPlayList = playList
      tempPlayList.splice(evt.newIndex, 0, playList.splice(evt.oldIndex, 1)[0]);
      // 更改播放列表順序
      dispatch(changePlayListAction(tempPlayList))
    },
  });
}, [playList, dispatch]);
  1. 完成效果

本地存儲音樂列表

考慮immutableredux-persise相互不能兼容,主要原因是immutable的數據格式,redux-persist不認識,導致我們想把redux中保存的狀態不能進行本地存儲,嘗試了redux-persist-transform-immutable進行轉換還是不行,可能使用的姿勢不對,有使用過的大佬請指導一下,搞了兩三次,最後只能撤銷代碼,自己還是太菜了,哭了😥

那就換一種思路,手動的只將歌曲列表進行本地存儲(只存儲歌曲id),將歌曲歌曲列表中所有歌曲id保存到local Storage

默認歌曲列表

在我們每次請求歌曲的時候,其實只需要知道歌曲id就可以了,這樣我們只需要將在第一次初始化的的時候將歌曲id進行存儲在本地就可以了,不過需要對本地存儲的歌曲id進行去重就可以了(重複的歌曲id不進行本地存儲)

實現效果

歌曲順序(異步問題)

一個小問題:在我們遍歷歌曲列表數組id發送網絡請求時,由於發送網絡請求是異步的,導致我們的音樂列表的歌曲順序並不是按照發送數組設定好的順序,有沒有一種辦法可以只有上一次網絡請求成功之後再進行發送這次網絡請求呢?

答案是肯定的,首先想到promise的小夥伴加雞腿🍗,當然也可以使用async await

思路

(1)解決方案:promise + setinterval(定時器)
(2)可能有同學會問,為什麼使用定時器呢?(不一定使用要使用定時器+promise這種方案)
(3)這是因為在咱們發送ajax時,不能很好的進行控制,使用一個標識變量來進行控制ajax是否發送(默認為true)
(4)在每次開始定時器時,首先判斷標識變量是否為true如果為true就發送ajax,
  在本次請求ajax時設置標識變量為false(即在定時器中不會再發送網絡請求),在本次ajax完成時(即異步操作成功時),改變標識變量為true
  這樣就能進行很好的控制,簡單的總結一下:就是必須控制本次ajax發送請求成功時,才能進行下一次ajax
  (核心在於使用標識變量,來控制ajax請求,且只有上次ajax請求成功,才能進行下一次ajax)

代碼

export const getSongDetailArrayAction = (listId) => {
  return (dispatch, getState) => {
    // 1.獲取歌曲列表
    const playList = getState().getIn(['player', 'playList'])
    let i = 0
    let timer = null
    let excuteRun = true // 控制ajax
    timer = setInterval(() => {
      let idx = listId[i]
      new Promise((resolve, reject) => {
        excuteRun &&
          getSongDetail(idx).then((res) => {
            excuteRun = false
            // console.log(res.songs[0])
            // (0)歌曲ID添加到本地存儲
            addPlaylistId(idx)
            const song = res.songs && res.songs[0]
            // console.log(song)
            if (!song) return
            // (1)添加到播放列表中
            playList.push(song)
            dispatch(changePlayListAction(playList))
            // (2)更改當前播放的索引
            const songIndex = playList.length - 1
            dispatch(changeSongIndexAction(songIndex))
            // (3)更改當前播放歌曲
            dispatch(changeCurrentSongAction(song))
            // (4)請求歌曲的歌詞
            dispatch(getLyricAction(idx))
            // (5)更新歌曲數量
            dispatch(changePlayListCount(playList.length))
            resolve(i)
          })
      }).then((value) => {
        excuteRun = true
      })
      i++
      if (i >= listId.length) {
        clearInterval(timer)
      }
    })
  }
}

效果

  • 這下不管刷新多少次,都會按照咱們的本地存儲中歌曲列表id數組順序進行請求了

以優化

  • 支持記憶歌曲列表

    • 刷新頁面後,音樂播放列表可以記憶上次播放順序
  • 記憶在關閉頁面前播放的音樂

    • 刷新頁面後,關閉頁面前記憶當前播放的歌曲,再次打開時默認歌曲是關閉前播放的歌曲

到現在我們已經完成「網易雲音樂PC」基本功能,相信你對React全家桶已經是比較熟練了,接下來想往哪方面擴展可以自行補充完善功能;

如果文章中有哪部分不明白的或寫的不好或是有什麼建議歡迎大家提出🤗,希望和大家共同進步;

感謝

  • 非常感謝王紅元老師的React核心技術實戰讓我學習到很多 react 的知識。
  • 非常感謝後台提供者Binaryify,接口很穩定,文檔很完善
user avatar nanian_5cd6881d3cc98 頭像 chouchou_5f11814d4719d 頭像 yangon 頭像 careteenl 頭像 mengyuhang4879 頭像 sky124380729 頭像
6 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.