React模仿網(wǎng)易云音樂實現(xiàn)一個音樂項目詳解流程
一、項目功能說明
- 暫停、播放歌曲
- 切換上一首、下一首歌曲
- 拖動進(jìn)度條改變播放進(jìn)度
- 隨機(jī)播放、循環(huán)播放、單曲循環(huán)
- 實時展示歌詞
- 切換不同分類的歌單、歌手、電臺
- 實現(xiàn)推薦、排行榜、歌單、主播電臺、歌手、新碟上架板塊的展示
二、最終效果
首頁:
排行榜:
歌單:
主播電臺:
歌手:
新碟上架:
三、文件目錄結(jié)構(gòu)說明
- assets:存放共用的css、font圖標(biāo)、image
- common:存放共用的資源,如數(shù)據(jù)、常量
- components:存放多個頁面共享的組件
- pages:劃分各個頁面
- router:路由配置
- services:網(wǎng)絡(luò)請求
- store:合并所有reducer
- utils:一些js的工具
四、項目技術(shù)棧
- React 作為前端框架
- Ant Design 作為前端UI框架
- Redux 進(jìn)行狀態(tài)管理
- Axios 進(jìn)行網(wǎng)絡(luò)請求
- 通過調(diào)用網(wǎng)易云的API來獲取數(shù)據(jù)
- 使用 react-router-dom 的 Route, Switch 管理路由
- 使用普通css 及 styled-component 編寫 CSS
五、核心技術(shù)
- 配置項目別名:@craco/craco
craco還可以進(jìn)行wepback 進(jìn)行自定義配置、antd 組件按需加載、支持 less等操作。不選擇 npm run eject 是因為eject是不可逆操作。
安裝:npm i @craco/craco
在package.json中的配置:
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
根目錄下創(chuàng)建 craco.config.js
重啟項目
- 使用reset.css進(jìn)行 css 重置
通過 import './assets/css/reset.css'
引入。但 reset.css 存在一個很大的問題是它將所有的瀏覽器的默認(rèn)樣式清除,從而達(dá)到所有瀏覽器樣式的統(tǒng)一的目的,但這么操作會導(dǎo)致瀏覽器原本的默認(rèn)樣式失去意義。比起 reset.css 我更傾向于使用 normalize.css 來統(tǒng)一樣式, normalize.css 是在盡量保留瀏覽器的默認(rèn)樣式的基礎(chǔ)上,不進(jìn)行太多的重置,這樣保留了有價值的默認(rèn)值,它還可以模塊化引入、修復(fù)了瀏覽器的一些 bug、沒有復(fù)雜的繼承鏈。
- 使用CSS Sprites 精靈圖
本次項目很多地方使用了CSS Sprites 精靈圖技術(shù),可以有效減少圖片的請求次數(shù),優(yōu)化性能。
- 使用 memo 包裹函數(shù)式組件,減少渲染次數(shù)
React.memo是一個高階函數(shù),與類組件里面的 PureComponent類似,它傳遞一個組件進(jìn)去,返回一個可以記憶的組件,在 props 不變的情況下,這個被包裹的組件是不會重新渲染的,這樣就減少了 render 的渲染次數(shù),從而提高了性能。
export default memo(function App() { return ( <div className="App"> </div> ) })
- AppHeader以及AppFooter的布局實現(xiàn)
使用 styled-components 編寫css樣式,通過 import styled from 'styled-components'
引入css 文件
導(dǎo)航欄使用flex布局
導(dǎo)航欄前三項采用 路由跳轉(zhuǎn)、后三項是鏈接跳轉(zhuǎn)
搜索欄使用了 antd 的樣式,通過 import { Input } from 'antd'
引入
搜索圖標(biāo)使用了antd 的圖標(biāo)庫,通過import { SearchOutlined } from '@ant-design/icons'
引入
- 推薦頁輪播圖采用 antd 的 Carousel走馬燈組件完成
背景的模糊效果使用高斯模糊,通過改變請求的url ?imageView&blur=40x20
實現(xiàn),css可以用filter: blur(20px);
實現(xiàn)
- 自定義公共組件,實現(xiàn)組件的復(fù)用
以下內(nèi)容都被抽取到 components目錄
中。
- 對于圖片及播放次數(shù)也做了格式化處理
圖片的處理:在 utils 目錄的 format-utils 下編寫 getSizeImage
函數(shù),根據(jù)輸入的參數(shù)來決定圖片的大小
對數(shù)字的處理:在 utils 目錄的 format-utils 下編寫 getCount
函數(shù),格式化歌曲播放數(shù)量,讓用戶可以更直觀的知道播放次數(shù)
- 播放音樂板塊
播放/暫停音樂:采用了 html5 的 audio 標(biāo)簽實現(xiàn),通過點擊播放/暫停按鈕實現(xiàn)歌曲的播放或暫停,通過isPlaying來獲取當(dāng)前播放狀態(tài),從而實現(xiàn)狀態(tài)切換。
// 播放/暫停音樂 const playMusic = useCallback(() => { isPlaying ? audioRef.current.pause() : audioRef.current.play() setIsPlaying(!isPlaying) }, [isPlaying])
通過 antd 的 Slider滑動條來改變播放進(jìn)度:通過Slider自帶 value={progress}
屬性獲取當(dāng)前播放進(jìn)度,從而設(shè)置audio的播放進(jìn)度。
//進(jìn)度改變觸發(fā)onchange時間,調(diào)用該函數(shù) const sliderChange = useCallback((value) => { setIsChanging(true) const currentTime = value / 100 * duration / 1000 setCurrentTime(currentTime * 1000) setProgress(value) }, [duration])
獲取當(dāng)前狀態(tài),切換播歌類型:
// 獲取狀態(tài)中的播放類型 sequence: state.getIn(['player', 'sequence']), // 點擊切換播放類型 <button className="sprite_playbar btn loop" onClick={changeSequence}></button> // 改變播放類型 const changeSequence = () => { let currentSequence = sequence + 1 if (currentSequence > 2) { currentSequence = 0 } dispatch(changeSequenceAction(currentSequence)) }
使用 isChanging, setIsChanging = useState(false)
,來判斷當(dāng)前進(jìn)度條是否正在改變,以便于當(dāng)用戶正在播放音樂并滑動滾動條時,滾動條可以滑動
利用 antd 的 Slider組件 自帶的onChange和onAfterChange的屬性可以得到進(jìn)度條滑動的位置和滑動后結(jié)束的位置
使用useCallback減少渲染次數(shù):
原理:把函數(shù)以及依賴項作為參數(shù)傳入 useCallback,它將返回該回調(diào)函數(shù)的 memoized 版本,這個 memoizedCallback 只有在依賴項有變化的時候才會更新。
例如:
// 播放歌曲進(jìn)度條部分 // 滑動中的位置 const sliderChange = useCallback((value) => { setIsChanging(true) // 滑動滾動條時,實時更新時間的改變 const currentTime = value / 100 * duration / 1000 setCurrentTime(currentTime * 1000) setProgress(value) }, [duration]) // 滑動后的位置 const sliderAfterChange = useCallback((value) => { //滑動進(jìn)度條后的進(jìn)度條時間 const currentTime = value / 100 * duration / 1000 audioRef.current.currentTime = currentTime // 重新更新進(jìn)度條時間 setCurrentTime(currentTime * 1000) setIsChanging(false) //如果沒有播放音樂,當(dāng)滑動滾動條后開始播放音樂 if (!isPlaying) { playMusic() } }, [duration, isPlaying, playMusic])
- 歌詞的處理
實現(xiàn):展示歌詞部分使用的是 antd的 Message全局提示
原理:先獲取到這首歌的全部歌曲,在 utils/parse-lyric
下格式化歌詞,原理就是將字符串轉(zhuǎn)為數(shù)組,數(shù)組的每一項為 一個對象 { time, content }
包含了時間及該時間的歌詞。
六、遇到的問題
背景圖片不能正常顯示:
源代碼:
background-image: url(${require("@/assets/img/recommend-top-bg.png")});
解決方法:為圖片添加.default
background-image: url(${<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->require("@/assets/img/recommend-top-bg.png").default});
編寫代碼過程中無法將數(shù)據(jù)存儲到redux的state中,但是看了react devtools 發(fā)現(xiàn)代碼能夠正常執(zhí)行到action部分,在我編寫了兩遍 actionCreators.js 和 reducer.js 后發(fā)現(xiàn)是因為我沒有在總的 store 中合并 reducer,留下悲傷的淚水。
滑動播放音樂的進(jìn)度條時,進(jìn)度條會短暫的回彈到滑動前位置。錯誤原因:在獲取當(dāng)前音樂播放時間時,利用setCurrentTime(e.target.currentTime * 1000)
,但是e.target.currentTime
無法更加實時的獲得當(dāng)前滑動的數(shù)據(jù),所以出現(xiàn)回彈。 解決方法:在滑動結(jié)束后的回調(diào)函數(shù)沖重新更新進(jìn)度條時間。
const sliderAfterChange = useCallback((value) => { // 獲取滑動進(jìn)度條后的進(jìn)度條時間 const currentTime = value / 100 * duration / 1000 // 設(shè)置當(dāng)前時間 audioRef.current.currentTime = currentTime setCurrentTime(currentTime * 1000) }, [duration])
七、github鏈接
到此這篇關(guān)于React模仿網(wǎng)易云實現(xiàn)一個音樂項目詳解流程的文章就介紹到這了,更多相關(guān)React網(wǎng)易云音樂內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React 項目中動態(tài)設(shè)置環(huán)境變量
本文主要介紹了React 項目中動態(tài)設(shè)置環(huán)境變量,本文將介紹兩種常用的方法,使用 dotenv 庫和通過命令行參數(shù)傳遞環(huán)境變量,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04React實現(xiàn)錨點跳轉(zhuǎn)組件附帶吸頂效果的示例代碼
這篇文章主要為大家詳細(xì)介紹了React如何實現(xiàn)移動端錨點跳轉(zhuǎn)組件附帶吸頂效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-01-01React組件對子組件children進(jìn)行加強(qiáng)的方法
這篇文章主要給大家介紹了關(guān)于React組件中對子組件children進(jìn)行加強(qiáng)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用React具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Yarn安裝項目依賴報error?An?unexpected?error?occurred:?“XXXXX:E
這篇文章主要為大家介紹了Yarn安裝項目依賴報error?An?unexpected?error?occurred:?“XXXXX:ESOCKETTIMEOUT”問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03