基於 React 實現的仿 iOS 客戶端網易雲音樂。
專案地址:戳我
線上地址:戳我(PC 瀏覽器需切換到移動端模式)
移動端體驗:
預覽
技術棧
- React 16.3
- TypeScript
- Mobx + Redux
- react-redux
- react-router-v4
- Scss
實現細節
目前只實現了上面四個頁面,但是總體的結構已經形成了,其他頁面的新增只是時間上的問題 (其實是懶),暫時沒有實現,下面是目前已實現的功能的細節:
區域性狀態管理
像首頁的 banner 或者推薦歌單等,都是不會被共享的區域性狀態,使用 Mobx 來進行請求的發起和狀態的管理。
全域性狀態
播放器的狀態是一個全域性狀態,包括當前的播放列表,切歌,播放 / 暫停等,所以很自然的使用 redux 來進行管理,可以清楚的掌握所有改變全域性狀態的行為。
TypeScript
儘管上手需要掌握一些語法,但是靜態型別與自動提示都能提供很大的幫助,在這個並不大的專案中我也體驗到了很大的幫助。但是要注意的是 TS 其實並不嚴格限制物件的型別,只要夠懶,遍地 any,就會把 TS 寫成 JS,所以為了充分發揮 TS 的威力,一定要有良好的 TS 程式碼風格。
手勢滑動
為了模仿 iOS 端可以通過滑屏切換頁面的功能,通過監聽 touchStart
,touchMove
,touchEnd
來進行手勢的判斷並通過 transform
觸發模擬滾動實現,在 touchMove
中檢測監聽滑動的方向及距離,在 touchEnd
中觸發路由的切換及頁面吸附到整屏的位置。
歌單的狀態保留
有這麼一個操作需要注意:使用者在某歌單往下滑了幾下,然後點了某歌播放然後進入了播放器,會發生路由的改變,如果此時從播放器返回,會丟包包括滾動位置在內的歌單頁的所有狀態丟失(因為 re-mount 了)。
我造了一個輪子來解決這個問題:react-live-route,是對 react-router-v4 中 Route 元件的增強,簡單的說就是將歌單頁隱藏掉而不是 unmount 掉,具體的解決思路可以參考輪子裡的文件。
跨元件傳遞狀態
在 iOS 版的網易雲中,可以滑動來切換頁面,同時會觸發頂部 tab 下的滑塊移動。在專案中,滑動頁面與滑塊分屬於兩個兄弟元件的子元件且巢狀層次較深,如果直接通過 prop 來傳遞略顯醜陋,有如下解決方案:
-
通過 redux,但是 redux 最好只負責領域資料,這種 UI 的狀態就不要往 store 中放了。
-
通過 event-emitter,其實和 redux 差不多,因為 redux 也是基於 event-emitter 實現的, 但是不經過 react-redux 雖然可以實現,但是破壞了 react 整個自頂向下介面更新的原則。
-
通過新的 context API 實現,如下圖:
API
專案中用到的網易雲音樂的 API 來自 NeteaseCloudMusicApi。
TODO
目前還有一些部分沒完成,包括但不限於:
- [ ] code splitting
- [ ] 元件中有些功能還是有耦合,需要再抽象
- [ ] SSR
開發
克隆程式碼到本地之後,需要在 4000 埠執行 NeteaseCloudMusicApi。
專案地址:戳我