Awesome Open Source
Awesome Open Source

react hooks+redux+immutable.js仿网易云音乐打造精美webApp

系列拆解文章已经出炉,整理成了掘金小册,请点这里查看。如遇到问题,或者需要联系加群,请加微信: FESanyuan

移动端和PC端的chrome浏览器食用更佳 : )

打开方式:

  1. 将项目 clone 下来
$ git clone https://github.com/sanyuan0704/cloud-music.git
$ cd cloud-music
$ npm install

// 下载子模块
$ git submodule update --init --recursive
$ cd NeteaseCloudMusicApi
$ npm install 
$ cd ../  (注意: 一定要返回到上一层)

接下来,要记得把src/api/config.js中把baseUrl改成接口的地址。(一定要记得,不然报404!)

  1. 运行
$ npm run start

现在就在本地的3000端口访问了。如果要打包到线上,执行npm run build即可。

项目介绍:

说明:本项目参考网易云音乐安卓端app界面开发,基础UI绝大多数自己来构建,算是对自己的一个挑战,在这个过程也学到了不少设计经验。

功能介绍

1、推荐部分

首页推荐:

推荐歌单详情:

空中切入切出效果,另外还有随着滑动会产生和标题跑马灯效果。 在歌单中歌曲数量过多的情况下,做了分页处理,随着滚动不断进行上拉加载,防止大量DOM加载导致的页面卡顿。

2、歌手部分

歌手列表:

这里做了异步加载的处理,上拉到底进行新数据的获取,下拉则进行数据的重新加载。

歌手详情:

3、排行榜

榜单页:

榜单详情:

4、播放器

播放器内核:

播放列表:

会有移动端app一样的反弹效果。

5、搜索部分

项目部分模块分享

1、利用better-scroll打造超级好用的scroll基础组件

import React, { forwardRef, useState,useEffect, useRef, useImperativeHandle } from "react"
import PropTypes from "prop-types"
import BScroll from "better-scroll"
import styled from 'styled-components';
import { debounce } from "../../api/utils";

const ScrollContainer = styled.div`
  width: 100%;
  height: 100%;
  overflow: hidden;
`

const Scroll = forwardRef((props, ref) => {
  const [bScroll, setBScroll] = useState();

  const scrollContaninerRef = useRef();

  const { direction, click, refresh, pullUpLoading, pullDownLoading, bounceTop, bounceBottom } = props;

  const { pullUp, pullDown, onScroll } = props;

  useEffect(() => {
    if(bScroll) return;
    const scroll = new BScroll(scrollContaninerRef.current, {
      scrollX: direction === "horizental",
      scrollY: direction === "vertical",
      probeType: 3,
      click: click,
      bounce:{
        top: bounceTop,
        bottom: bounceBottom
      }
    });
    setBScroll(scroll);
    if(pullUp) {
      scroll.on('scrollEnd', () => {
        //判断是否滑动到了底部
        if(scroll.y <= scroll.maxScrollY + 100){
          pullUp();
        }
      });
    }
    if(pullDown) {
      scroll.on('touchEnd', (pos) => {
        //判断用户的下拉动作
        if(pos.y > 50) {
          debounce(pullDown, 0)();
        }
      });
    }

    if(onScroll) {
      scroll.on('scroll', (scroll) => {
        onScroll(scroll);
      })
    }

    if(refresh) {
      scroll.refresh();
    }
    return () => {
      scroll.off('scroll');
      setBScroll(null);
    }
    // eslint-disable-next-line
  }, []);

  useEffect(() => {
    if(refresh && bScroll){
      bScroll.refresh();
    }
  })

  useImperativeHandle(ref, () => ({
    refresh() {
      if(bScroll) {
        bScroll.refresh();
        bScroll.scrollTo(0, 0);
      }
    }
  }));

  const PullUpdisplayStyle = pullUpLoading ? { display: "" } : { display: "none" };
  const PullDowndisplayStyle = pullDownLoading ? { display: "" } : { display: "none" };
  return (
    <ScrollContainer ref={scrollContaninerRef}>
      {props.children}
      {/* 滑到底部加载动画 */}
      <PullUpLoading style={ PullUpdisplayStyle }></PullUpLoading>
      {/* 顶部下拉刷新动画 */}
      <PullDownLoading style={ PullDowndisplayStyle }></PullDownLoading>
    </ScrollContainer>
  );
})

Scroll.defaultProps = {
  direction: "vertical",
  click: true,
  refresh: true,
  onScroll: null,
  pullUpLoading: false,
  pullDownLoading: false,
  pullUp: () => {},
  pullDown: () => {},
  bounceTop: true,
  bounceBottom: true
};

Scroll.propTypes = {
  direction: PropTypes.oneOf(['vertical', 'horizental']),
  refresh: PropTypes.bool,
  onScroll: PropTypes.func,
  pullUp: PropTypes.func,
  pullDown: PropTypes.func,
  pullUpLoading: PropTypes.bool,
  pullDownLoading: PropTypes.bool,
  bounceTop: PropTypes.bool,//是否支持向上吸顶
  bounceBottom: PropTypes.bool//是否支持向上吸顶
};

export default React.memo(Scroll);

2、富有动感的loading组件

import React from 'react';
import styled, {keyframes} from 'styled-components';
import style from '../../assets/global-style'

const dance = keyframes`
    0%, 40%, 100%{
      transform: scaleY(0.4);
      transform-origin: center 100%;
    }
    20%{
      transform: scaleY(1);
    }
`
const Loading = styled.div`
    height: 10px;
    width: 100%;
    margin: auto;
    text-align: center;
    font-size: 10px;
    >div{
      display: inline-block;
      background-color: ${style["theme-color"]};
      height: 100%;
      width: 1px;
      margin-right:2px;
      animation: ${dance} 1s infinite;
    }
    >div:nth-child(2) {
      animation-delay: -0.4s;
    }
    >div:nth-child(3) {
      animation-delay: -0.6s;
    }
    >div:nth-child(4) {
      animation-delay: -0.5s;
    }
    >div:nth-child(5) {
      animation-delay: -0.2s;
    } 
`

function LoadingV2() {
  return (
    <Loading>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <span>拼命加载中...</span>
    </Loading>
  );
}
 
export default LoadingV2;

3、模块懒加载及代码分割(CodeSpliting)

react官方已经提供了相应的方案, 用react自带的lazy和Suspense即可完成。 操作如下:

import React, {lazy, Suspense} from 'react';
const HomeComponent = lazy(() => import("../application/Home/"));
const Home = (props) => {
  return (
    <Suspense fallback={null}>
      <HomeComponent {...props}></HomeComponent>
    </Suspense>
  )
};
......
export default [
  {
    path: "/",
    component: Home,
    routes: [
      {
        path: "/",
        exact: true,
        render:  ()=> (
          <Redirect to={"/recommend"}/>
        )
      },
      {
        path: "/recommend/",
        extra: true,
        key: 'home',
        component: Recommend,
        routes:[{
          path: '/recommend/:id',
          component: Album,
        }]
      }
      ......
    ]
  },

];

未来规划和展望

目前这个项目的核心已经完成,但是还是有很多扩展的余地。关于未来的规划,我是这么安排的:

  • 完成收藏、播放历史功能
  • 完成登录功能和评论模块
  • 实现MV模块
  • 同时撰写《手摸手,一起用React实现网易云音乐webApp》系列拆解文章
  • 未来更多功能待补充...

这个项目长期维护,希望大家踊跃提issue和pr,把这个项目打造得更加完美,帮助到更多的react开发者!

Related Awesome Lists
Top Programming Languages
Top Projects

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
Javascript (1,072,185
Reactjs (164,248
Redux (28,077
Axios (6,028
Styled Components (4,277
React Router (4,200
React Redux (3,537
Immutable (2,808
Redux Thunk (1,411
Better Scroll (62
Redux Immutable (6