Reaction hooks+redux+immutable.js Create Netease Cloud Music Beautiful webApp

Introduction of Technology Stack

Front end part:

  • react v16.8 family bucket (react,react-router): MVVM framework for building user interface

  • redux: Famous JavaScript State Management Container

  • redux-thunk: redux middleware for handling asynchronous logic

  • immutable: Facebook's three-year-old library for persistent data structure processing (it works with memo and Redux as magic artifacts, and memo wrapping function components have the same effect as PureComponent. For shallow comparison of data before component updates, see this article when PureComponent meets Immutable JS)

  • react-lazyload: react lazy load Library

  • better-scroll: a repository to enhance mobile sliding experience

  • Styled-components: Processing style, reflecting the front-end engineering artifact of css in js (please follow my previous article styled-components: new ideas for front-end component splitting)

  • axios: Data used to request back-end APIs

The rear end part:

  • Netease CloudMusic api, the api interface of NodeJS version of Netease CloudMusic, which is well known to women and children on github, is used to provide music data.

Other:

  • create-react-app: React Scaffolding, Fast Building Projects

  • eslint: Famous Code Style Checking Tool

  • iconfont: Alibaba Icon Library

  • fastclick: Solving the problem of 300 ms delay of click on mobile end

II. Project Specification

Before introducing the functions of the project, it is necessary for me to emphasize the development specifications of a project and my personal coding style. I should inform you in advance that I have my own reasons for doing so, so that the readability and maintainability of the project can be as high as possible. I hope to see some strange operations in the future.

1. class component is no longer used. It embraces hooks in an all-round way and uses function components in a unified way.

2. The internal state of components is handled by hooks, and all business data are managed in redux.

3. The specific code of ajax requests and subsequent data processing are all placed in action creator, which is processed by redux-thunk to streamline component code as much as possible.

4. Each container component has its own reducer, and then merges under the global store through the combineReducer method of redux.

5. JS variable names (including function names) are in the form of small humps. Component names or styled-components derived style container names are in the form of large humps. Constant names are capitalized in all letters.

6. Common CSS class names are all lowercase in English. Words are connected with underscores. Words in CSS animation hook class names are connected with each other.

7. Where there is data in props, all of them deconstruct the assignment in advance at the front of the component. Moreover, the attribute name and method name obtained should be declared separately, and the props obtained from the parent component and from the props obtained by mapping in react-redux should also be declared separately.

8. useEffect is written uniformly at the front, followed by the props deconstruction assignment code.

9. All functions responsible for returning JSX are centralized at the end of the function without interpolating event handling functions and other logic.

10. In the function returned by mapDispatchToProps, the function name is formatted as xxDispatch to avoid conflict with the existing action name.

III. Overall Architecture and Demonstration of the Project

Description: This project refers to the development of Android app interface of Netease Cloud Music. The basic wheel component does not use any UI framework, which is a challenge to itself. In this process, we have learned a lot of design experience.


Because the video transmission is more troublesome, but the picture is relatively monotonous, unable to reflect the dynamic of the webApp, so the following uses gif.

1. Recommendation

Home page recommendation:


Recommended song list details:


The air cut-in and cut-out effect, as well as the headlamp effect with sliding will be produced. In the case of excessive number of songs in the song list, paging is done. As the scroll continues to pull-up loading, page jamming caused by a large number of DOM loading is prevented. 2. Singer section

Singer List:


Here, asynchronous loading is done. Pull-up is used to get new data, and drop-down is used to reload data. Singer details:


3. Rankings

List page:


List details:


4. Player

Player Kernel:


Playlist:


There will be the same rebound effect as the mobile app.

5. Search section


IV. Module Sharing of Project Parts

1. Using better-scroll to build super-usable scroll basic components

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(() => { 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', () => { //Judge if it slides to the bottom
 if(scroll.y <= scroll.maxScrollY + 100){
 pullUp();
 }
 });
 } if(pullDown) {
 scroll.on('touchEnd', (pos) => { //Judging the user's drop-down action
 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}
 {/* Slide to the bottom and load the animation.*/} <PullUpLoading style={ PullUpdisplayStyle }></PullUpLoading>
 {/* Top drop-down refresh animation*/} <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,//Supporting upward suction
 bounceBottom: PropTypes.bool//Supporting upward suction}; export default React.memo(Scroll);
//Copy code

2. Dynamic loading Components

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>Desperate Loading...</span>
 </Loading>
 );
} 
export default LoadingV2;
//Copy code


3. Module Lazy Loading and Code Spliting

The react official has provided the corresponding solution, which can be completed with the lazy and Suspense that react comes with. The operation is as follows:

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,
 }]
 }
 ......
 ]
 },
];
//Copy code

V. Future Planning and Prospects

At present, the core of the project has been completed, but there is still a lot of room for expansion. Now the module is equivalent to only 60%. With regard to future planning, I have arranged it as follows:

  • Complete the function of collecting and broadcasting history at the end of the month

  • Complete login function and comment module by October

  • Implementation of MV module by mid-October

  • At the same time, he wrote a series of disassembly articles of "Hand Touching, Realizing Netease Cloud Music webApp with React Together".

  • More functions need to be added in the future.

Because there are other projects to be busy, so doing this open source project takes up most of my spare time, but I think it is worth it, after all, it is a training and challenge for myself. And the significance of doing this project for me is not just to complete these functions, but to condense my own thinking about technology and a personal practice of previous ideas. To be honest, when the project is stuck in one place, the heart is basically collapsed, but after going through, I found myself learning a lot of things, full of sense of achievement, which is my deep feeling of doing open source projects independently.

Finally, I would like to thank those who have helped me and projects, so that I have the courage to start this project and overcome difficulties one by one.

Thanks to Huang Yi's predecessor vue music practical course, I learned a lot of native JS skills and component packaging skills.

Thank you Dell Leereact for introducing me to React and developing the engineering coding habit of React.

Thank you for React open source project mango-music, although my current project and its development philosophy and coding style are completely different, but still some of the animation effects still learn from this open source project, let me open my eyes, thank you very much, please also forget to give this project a star, although not hooks, but still It's worth learning.

Finally, this project is not a temporary demo, I will maintain it for a long time. I hope you can actively mention pr and issue, make this project more perfect, and help more people learn the practical application of react in addition to official demo, avoid more pits.


Keywords: Big Data React Mobile Javascript axios

Added by ctcp on Fri, 06 Sep 2019 18:24:36 +0300