基於 React + TypeScript 的網易雲音樂

fi3ework發表於2018-08-17

基於 React 實現的仿 iOS 客戶端網易雲音樂。

專案地址:戳我

線上地址:戳我(PC 瀏覽器需切換到移動端模式)

移動端體驗:

基於 React + TypeScript 的網易雲音樂

預覽

基於 React + TypeScript 的網易雲音樂

技術棧

  • React 16.3
  • TypeScript
  • Mobx + Redux
  • react-redux
  • react-router-v4
  • Scss

實現細節

目前只實現了上面四個頁面,但是總體的結構已經形成了,其他頁面的新增只是時間上的問題 (其實是懶),暫時沒有實現,下面是目前已實現的功能的細節:

區域性狀態管理

像首頁的 banner 或者推薦歌單等,都是不會被共享的區域性狀態,使用 Mobx 來進行請求的發起和狀態的管理。

全域性狀態

播放器的狀態是一個全域性狀態,包括當前的播放列表,切歌,播放 / 暫停等,所以很自然的使用 redux 來進行管理,可以清楚的掌握所有改變全域性狀態的行為。

TypeScript

儘管上手需要掌握一些語法,但是靜態型別與自動提示都能提供很大的幫助,在這個並不大的專案中我也體驗到了很大的幫助。但是要注意的是 TS 其實並不嚴格限制物件的型別,只要夠懶,遍地 any,就會把 TS 寫成 JS,所以為了充分發揮 TS 的威力,一定要有良好的 TS 程式碼風格。

手勢滑動

為了模仿 iOS 端可以通過滑屏切換頁面的功能,通過監聽 touchStarttouchMovetouchEnd 來進行手勢的判斷並通過 transform 觸發模擬滾動實現,在 touchMove 中檢測監聽滑動的方向及距離,在 touchEnd 中觸發路由的切換及頁面吸附到整屏的位置。

歌單的狀態保留

有這麼一個操作需要注意:使用者在某歌單往下滑了幾下,然後點了某歌播放然後進入了播放器,會發生路由的改變,如果此時從播放器返回,會丟包包括滾動位置在內的歌單頁的所有狀態丟失(因為 re-mount 了)。

我造了一個輪子來解決這個問題:react-live-route,是對 react-router-v4 中 Route 元件的增強,簡單的說就是將歌單頁隱藏掉而不是 unmount 掉,具體的解決思路可以參考輪子裡的文件。

跨元件傳遞狀態

在 iOS 版的網易雲中,可以滑動來切換頁面,同時會觸發頂部 tab 下的滑塊移動。在專案中,滑動頁面與滑塊分屬於兩個兄弟元件的子元件且巢狀層次較深,如果直接通過 prop 來傳遞略顯醜陋,有如下解決方案:

  1. 通過 redux,但是 redux 最好只負責領域資料,這種 UI 的狀態就不要往 store 中放了。

  2. 通過 event-emitter,其實和 redux 差不多,因為 redux 也是基於 event-emitter 實現的, 但是不經過 react-redux 雖然可以實現,但是破壞了 react 整個自頂向下介面更新的原則。

  3. 通過新的 context API 實現,如下圖:

基於 React + TypeScript 的網易雲音樂

API

專案中用到的網易雲音樂的 API 來自 NeteaseCloudMusicApi

TODO

目前還有一些部分沒完成,包括但不限於:

  • [ ] code splitting
  • [ ] 元件中有些功能還是有耦合,需要再抽象
  • [ ] SSR

開發

克隆程式碼到本地之後,需要在 4000 埠執行 NeteaseCloudMusicApi


專案地址:戳我

相關文章