文章目錄
- 一、項目功能說明
- 二、最終效果
- 三、檔案目錄結構說明
- 四、項目技術棧
- 五、核心技術
-
- 1. 配置項目别名: @craco/craco
- 2. 使用reset.css進行 css 重置
- 3. 使用CSS Sprites 精靈圖
- 4. 使用 memo 包裹函數式元件,減少渲染次數
- 5. AppHeader以及AppFooter的布局實作:
- 6. 推薦頁輪播圖采用 antd 的 Carousel走馬燈元件完成
- 7. 自定義公共元件,實作元件的複用
- 8. 對于圖檔及播放次數也做了格式化處理
- 9. 播放音樂闆塊
- 10. 歌詞的處理
- 六、遇到的問題
- 七、github連結
一、項目功能說明
- 暫停、播放歌曲
- 切換上一首、下一首歌曲
- 拖動進度條改變播放進度
- 随機播放、循環播放、單曲循環
- 實時展示歌詞
- 切換不同分類的歌單、歌手、電台
- 實作推薦、排行榜、歌單、主播電台、歌手、新碟上架闆塊的展示
二、最終效果
首頁:
排行榜:
歌單:
主播電台:
歌手:
新碟上架:
三、檔案目錄結構說明
- assets:存放共用的css、font圖示、image
- common:存放共用的資源,如資料、常量
- components:存放多個頁面共享的元件
- pages:劃分各個頁面
- router:路由配置
- services:網絡請求
- store:合并所有reducer
- utils:一些js的工具
四、項目技術棧
- React 作為前端架構
- Ant Design 作為前端UI架構
- Redux 進行狀态管理
- Axios 進行網絡請求
- 通過調用網易雲的API來擷取資料
- 使用 react-router-dom 的 Route, Switch 管理路由
- 使用普通css 及 styled-component 編寫 CSS
五、核心技術
1. 配置項目别名: @craco/craco
craco還可以進行wepback 進行自定義配置、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"
},
- 根目錄下建立 craco.config.js
- 重新開機項目
2. 使用reset.css進行 css 重置
通過
import './assets/css/reset.css'
引入。但 reset.css 存在一個很大的問題是它将所有的浏覽器的預設樣式清除,進而達到所有浏覽器樣式的統一的目的,但這麼操作會導緻浏覽器原本的預設樣式失去意義。比起 reset.css 我更傾向于使用 normalize.css 來統一樣式, normalize.css 是在盡量保留浏覽器的預設樣式的基礎上,不進行太多的重置,這樣保留了有價值的預設值,它還可以子產品化引入、修複了浏覽器的一些 bug、沒有複雜的繼承鍊。
3. 使用CSS Sprites 精靈圖
本次項目很多地方使用了CSS Sprites 精靈圖技術,可以有效減少圖檔的請求次數,優化性能。
4. 使用 memo 包裹函數式元件,減少渲染次數
React.memo是一個高階函數,與類元件裡面的 PureComponent類似,它傳遞一個元件進去,傳回一個可以記憶的元件,在 props 不變的情況下,這個被包裹的元件是不會重新渲染的,這樣就減少了 render 的渲染次數,進而提高了性能。
export default memo(function App() {
return (
<div className="App">
</div>
)
})
5. AppHeader以及AppFooter的布局實作:
- 使用 styled-components 編寫css樣式,通過
引入css 檔案import styled from 'styled-components'
- 導航欄使用flex布局
- 導航欄前三項采用 路由跳轉、後三項是連結跳轉
- 搜尋欄使用了 antd 的樣式,通過
引入import { Input } from 'antd'
- 搜尋圖示使用了antd 的圖示庫,通過
引入import { SearchOutlined } from '@ant-design/icons'
6. 推薦頁輪播圖采用 antd 的 Carousel走馬燈元件完成
背景的模糊效果使用高斯模糊,在url後加上
?imageView&blur=40x20
實作
7. 自定義公共元件,實作元件的複用
以下内容都被抽取到
components目錄
中。
8. 對于圖檔及播放次數也做了格式化處理
圖檔的處理:在 utils 目錄的 format-utils 下編寫
getSizeImage
函數,根據輸入的參數來決定圖檔的大小
對數字的處理:在 utils 目錄的 format-utils 下編寫
getCount
函數,格式化歌曲播放數量,讓使用者可以更直覺的知道播放次數
9. 播放音樂闆塊
- 播放/暫停音樂:采用了 html5 的 audio 标簽實作,通過點選播放/暫停按鈕實作歌曲的播放或暫停,通過isPlaying來擷取目前播放狀态,進而實作狀态切換。
// 播放/暫停音樂
const playMusic = useCallback(() => {
isPlaying ? audioRef.current.pause() : audioRef.current.play()
setIsPlaying(!isPlaying)
}, [isPlaying])
- 通過 antd 的 Slider滑動條來改變播放進度:通過Slider自帶
屬性擷取目前播放進度,進而設定audio的播放進度。value={progress}
//進度改變觸發onchange時間,調用該函數
const sliderChange = useCallback((value) => {
setIsChanging(true)
const currentTime = value / 100 * duration / 1000
setCurrentTime(currentTime * 1000)
setProgress(value)
}, [duration])
- 擷取目前狀态,切換播歌類型:
// 擷取狀态中的播放類型
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)
- 利用 antd 的 Slider元件 自帶的onChange和onAfterChange的屬性可以得到進度條滑動的位置和滑動後結束的位置
-
使用useCallback減少渲染次數:
原理:把函數以及依賴項作為參數傳入 useCallback,它将傳回該回調函數的 memoized 版本,這個 memoizedCallback 隻有在依賴項有變化的時候才會更新。
例如:
// 播放歌曲進度條部分
// 滑動中的位置
const sliderChange = useCallback((value) => {
setIsChanging(true)
// 滑動滾動條時,實時更新時間的改變
const currentTime = value / 100 * duration / 1000
setCurrentTime(currentTime * 1000)
setProgress(value)
}, [duration])
// 滑動後的位置
const sliderAfterChange = useCallback((value) => {
//滑動進度條後的進度條時間
const currentTime = value / 100 * duration / 1000
audioRef.current.currentTime = currentTime
// 重新更新進度條時間
setCurrentTime(currentTime * 1000)
setIsChanging(false)
//如果沒有播放音樂,當滑動滾動條後開始播放音樂
if (!isPlaying) {
playMusic()
}
}, [duration, isPlaying, playMusic])
10. 歌詞的處理
- 實作:展示歌詞部分使用的是 antd的 Message全局提示
- 原理:先擷取到這首歌的全部歌曲,在
下格式化歌詞,原理就是将字元串轉為數組,數組的每一項為 一個對象utils/parse-lyric
包含了時間及該時間的歌詞。{ time, content }
六、遇到的問題
- 背景圖檔不能正常顯示:
源代碼:
解決方法:為圖檔添加.default
- 編寫代碼過程中無法将資料存儲到redux的state中,但是看了react devtools 發現代碼能夠正常執行到action部分,在我編寫了兩遍 actionCreators.js 和 reducer.js 後發現是因為我沒有在總的 store 中合并 reducer,留下悲傷的淚水。
-
滑動播放音樂的進度條時,進度條會短暫的回彈到滑動前位置。
錯誤原因:在擷取目前音樂播放時間時,利用
,但是setCurrentTime(e.target.currentTime * 1000)
e.target.currentTime
無法更加實時的獲得目前滑動的資料,是以出現回彈。
解決方法:在滑動結束後的回調函數沖重新更新進度條時間。
const sliderAfterChange = useCallback((value) => {
// 擷取滑動進度條後的進度條時間
const currentTime = value / 100 * duration / 1000
// 設定目前時間
audioRef.current.currentTime = currentTime
setCurrentTime(currentTime * 1000)
}, [duration])
七、github連結
react仿寫網易雲音樂項目