Source address (real-time update) github.com/yayxs/Netea…

The wiki address github.com/yayxs/Netea…

preface

Project background

Based on two points :(1) the author developed react in the enterprise, but some solutions to deal with problems are still outdated. For example, redux is still written using the original switch, such as Thunk, which handles several states of network requests, including exceptions recorded before loading, and so on. There is a lot of similar code that looks awkward, but it is not easy to change the solution of enterprise projects. Therefore, I plan to develop a complete enterprise-level project to integrate the current better solution to the problem. (2) Only pay attention to the realization of the front end, plus a very perfect interface (here refers to the Api of the open source netease cloud, which must be known to everyone). So choose network suppression cloud music.

About the project

This project is a PERSONAL training project for THE JS version (currently starting from the JS version). It integrates the most practical practices in the React project and updates the progress weekly, aiming to better follow the React enterprise project development process.

React development tips. Please communicate with me. The code will be synchronized to the warehouse address at the beginning of the article (temporarily not in the code cloud).

Try to write a progress share every week. Interested guys click a star is my motivation to update.

Week 1 Results presentation

The main finished part

  • The pop-upwebpackConfigure, AddsrcThe alias
  • configuration.vscodeAdd debugjsonconfiguration
  • finishingReduxRelevant practice process, run throughReduxprocess
  • ApiOf the interfaceproxyThe proxy debug
  • axiosThe encapsulation, as wellapiThe configuration of the
  • react-router-domInitial configuration of routes

Preview

Basic introduction

The first thing to say is that the project is created using the Create React app. Then pop up the webpack configuration via NPM Run eject, removing the individual build dependencies

The directory structure

| - LICENSE | -- README. Md / / description file | - build | - config | | -- webpack. Config. Js / / webpack configuration file | - docs/post/supporting document | ` - Images | - examples | ` - proxy middleware / / nestjs project for testing interface agent | -- jsconfig. Json | -- package. Json | - public | | - The favicon. Ico | ` -- index. | - HTML scripts | - SRC | | - App. Js/App/main application | | - API/interface/API | | - assets / / resources | | - Common public configuration / / | | - components / / component | | - index. The js | | | - layouts / / layout | - pages / / page views | | - the router / / React - the router - dom routing configuration | | - services / / axios network request package | | - store/configuration/redux | | - styles / / style file | | - utils/method/tools |-- yarn.lockCopy the code

Depend on the environment

NPM script

Scripts as of the latest time

 "scripts": {
    "analyze": "source-map-explorer 'build/static/js/*.js'"."start": "node scripts/start.js"."build": "node scripts/build.js"."test": "node scripts/test.js"."clear": "rimraf node_modules && yarn add"."check": "npm install -g && npm-check-updates"."ncu": "ncu -u && npm i"
  },
Copy the code

Package bag

Click the link below to jump directly to the official website for easy viewing

  • “React” : “^ 16.13.1”

  • “React-router-dom “: “^5.2.0”

  • “Story”, “^ 4.0.5”

  • “Antd” : “^ 4.6.1.” “

  • “Axios” : “^ 0.20.0”

  • “Webpack” : “4.44.1”

  • Others to be added

"Antd" : "^ 4.6.1", "axios" : "^ 0.20.0", "classnames" : "^ 2.2.6", "HTTP proxy - middleware" : "^ 1.0.5", "husky" : "^ 4.2.5," "immutable" : "^ 4.0.0 - rc. 12", "normalize. CSS" : "^ 8.0.1", "the react - redux" : "^ 7.2.1", "the react - the router - config" : "^ 5.1.1", "story", "^ 4.0.5", "the story - the immutable" : "^ 4.0.0", "styled - components" : "^ 5.1.1", "webpack" : "4.44.1."Copy the code

Just to be brief

  • antdUse V4 big version up to now the latest, because and V3 version of the way to write there are different places
  • immutable redux-immutableA solution for immutable redux data flow (not the best)
  • redux react-reduxAll are the latest bags
  • The React version is officially up to date

NodeSuch as the environment

  • The node v14.8.0

Run the project

Clone project

https://github.com/yayxs/NeteaseCloudMusic.git
Copy the code

Install dependencies

NPM run check && nCU // Check the dependency package version to be updated and installedCopy the code

Project running

npm run start
Copy the code

The above commands can be replaced with yarn, etc., if you prefer

About the style of the project

Styled using the ANTD component library + Styled – Components in conjunction with Sass and normalize.css

import styled from "styled-components";

export const WrapperContainer = styled.div`
  height: 285px;
  width: 100vw;
  background: url(${(props) => props.bgImage}) center center/6000px;

  .banner {
    height: 285px;
    display: flex;
    position: relative;
  }
`;
Copy the code

Write styles in the same way as above and complete the style section of the page together with SASS

Axios Network request encapsulation

/* * @Author: yayxs * @Date: 2020-08-26 21:37:00 * @LastEditTime: 2020-08-26 23:54:10 * @LastEditors: yayxs * @Description: * @FilePath: \NeteaseCloudMusic\src\services\request.js * @ */
/ / introduce axios
import axios from "axios";
// import * as commonConfig from ".. /common/config";

const instance = axios.create({
  baseURL: process.env.REACT_APP_BASE_URL,
  timeout: Number(process.env.REACT_APP_TIME_OUT),
});

// Add a request interceptor
// Global request interception, executed before sending the request
instance.interceptors.request.use(
  function (config) {
    // Do something before request is sent
    // Set the request token, etc
    // config.headers["authorization"] = "Bearer " + getToken();
    return config;
  },
  function (error) {
    // Do something with request error
    return Promise.reject(error); });// Add a response interceptor
// Execute after the request returns
instance.interceptors.response.use(
  function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response.data;
  },
  function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error); });/** * get request *@param {*} Url Request address *@param {*} params* /
export function get(url, params) {
  return instance.get(url, {
    params,
  });
}

/** * Post request *@param {*} Url Request address *@param {*} data* /
export function post(url, data) {
  return instance.post(url, data);
}

/** * put request *@param {*} Url Request address *@param {*} data* /
export function put(url, data) {
  return instance.put(url, data);
}

/** * delete request *@param {*} url* /
export function del(url) {
  return instance.delete(url);
}


Copy the code

When using, you can directly create the corresponding JS file under the @/ API folder and configure the corresponding interface

/* * @Author: yayxs * @Date: 2020-08-26 22:35:37 * @LastEditTime: 2020-08-26 23:48:17 * @LastEditors: Yayxs * @description: Recommended API * @filepath: \NeteaseCloudMusic\ SRC \ API \recommend.js * @ */
// import request from "@/services/request";

import { get } from "@/services/request";

const fetchBannerListApi = () = > get("/banner");

export { fetchBannerListApi };

Copy the code

About the interface

Please refer to the netease Cloud Music NodeJS API

Component writing

Take the header component of the header as an example

@/components/header

  • index.jsx

    import React, { memo } from "react";
    import classnames from "classnames";
    import { WarpperContainer, StyledLeft, StyledRight } from "./styled";
    import { NavLink } from "react-router-dom";
    import { headerNavConfig } from ".. /.. /common/config";
    const HeaderComp = memo(() = > {
      return (
        <WarpperContainer>
          <div className="wrap_1100_center container">
            <StyledLeft>
              <a hidefocus="true" href=# "/" className="logo sprite_topbar">Netease Cloud Music</a>
              <ul>
                {headerNavConfig.map((item) => (
                  <li key={item.title} className={classnames("setected_nav")} >
                    <NavLink to={item.path}>
                      {item.title}
                      <i className="sprite_topbar icon"></i>
                    </NavLink>
                  </li>
                ))}
              </ul>
            </StyledLeft>
            <StyledRight></StyledRight>
          </div>
        </WarpperContainer>
      );
    });
    
    export default HeaderComp;
    
    Copy the code
  • styled.js

    /* * @Author: yayxs * @Date: 2020-08-24 23:28:16 * @LastEditTime: 2020-08-25 23:28:36 * @LastEditors: yayxs * @Description: * @FilePath: \NeteaseCloudMusic\src\components\header\styled.js * @ */
    import styled from "styled-components";
    
    export const WarpperContainer = styled.div` width: 100vw; height: 70px; background-color: #242424; .container { display: flex; } `;
    
    export const StyledLeft = styled.div` display: flex; .logo { display: block; width: 176px; height: 69px; background-position: 0 0; text-indent: -9999px; } ul { width: 508px; height: 70px; display: flex; align-items: center; justify-content: space-between; li { font-size: 14px; color: #ccc; }} `;
    export const StyledRight = styled.div` `;
    
    Copy the code

Redux process

There are two types of data for this project: one is the internal state of the component. Use useState hook in React hooks

const [currIndex, setCurrIndex] = useState(0);
Copy the code

Axios network requests data that we all access in Redux

Overall process

import { Provider } from "react-redux";
import store from "./store";

ReactDOM.render(
  <>
    <Provider store={store}>
      <App />
    </Provider>
  </>.document.getElementById("root"));Copy the code
  • store/index.js
/* * @Author: yayxs * @Date: 2020-08-22 11:48:40 * @LastEditTime: 2020-08-26 23:01:17 * @LastEditors: yayxs * @Description: * @FilePath: \NeteaseCloudMusic\src\store\index.js * @ */
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
const middlewares = [thunk];
if (process.env.NODE_ENV === `development`) {
  const { logger } = require(`redux-logger`);

  middlewares.push(logger);
}

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  /* preloadedState, */composeEnhancers(applyMiddleware(... middlewares)) );export default store;

Copy the code

Local component state

The local component flow is dealt with in several parts

actionTypes.js

/* * @Author: yayxs * @Date: 2020-08-26 22:27:29 * @LastEditTime: 2020-08-26 22:32:33 * @LastEditors: yayxs * @Description: * @FilePath: \NeteaseCloudMusic\src\pages\foundMusic\childrenPages\recommend\store\actionTypes.js * @ */
// Get the data from the rotation chart
export const FETCH_BANNER_LIST_SUCCESS = "FETCH_BANNER_LIST_SUCCESS";
export const FETCH_BANNERLIST_BEGIN = "FETCH_BANNERLIST_BEGIN";
export const FETCH_BANNERLIST_ERROR = "FETCH_BANNERLIST_ERROR";

Copy the code

actionCreators

/* * @Author: yayxs * @Date: 2020-08-26 22:27:33 * @LastEditTime: 2020-08-27 21:07:49 * @LastEditors: yayxs * @Description: * @FilePath: \NeteaseCloudMusic\src\pages\foundMusic\childrenPages\recommend\store\actionCreators.js * @ */
import { fetchBannerListApi } from "@/api/recommend.js";
import { FETCH_BANNER_LIST_SUCCESS } from "./actionTypes";

export const changeBannerLsitAction = (data) = > ({
  type: FETCH_BANNER_LIST_SUCCESS,
  payload: { data },
});

export const getBannerListAsyncAction = () = > (dispatch) = > {
  fetchBannerListApi()
    .then((res) = > {
      if (res.code === 200) {
        dispatch(changeBannerLsitAction(res.banners));
      }
    })
    .catch((err) = > {});
};

Copy the code

reducer

/* * @Author: yayxs * @Date: 2020-08-26 22:27:34 * @LastEditTime: 2020-08-27 21:09:44 * @LastEditors: yayxs * @Description: * @FilePath: \NeteaseCloudMusic\src\pages\foundMusic\childrenPages\recommend\store\reducer.js * @ */
import { Map } from "immutable";
import * as actionTypes from "./actionTypes";

const initState = Map({
  bannersList: [],});export default (state = initState, action) => {
  switch (action.type) {
    case actionTypes.FETCH_BANNER_LIST_SUCCESS:
      return state.set("bannersList", action.payload.data);

    default:
      returnstate; }};Copy the code

For the reDUx process above, on the one hand you can look at this project to see the complete code, and on the other hand ** I have compiled a vertical analysis of the reDUx process practice (updated in August 2020) **

Of course you can also check directly in Juejin (if you encounter network problems) juejin.cn/post/684490…

Route lazy loading

const YYHeaderComp = lazy(() = > import("./components/header/index"));
const YYFooterComp = lazy(() = > import("./components/footer/index"));
Copy the code

Webpack alias configuration

This project adds aliases by simply modifying the webpack configuration

const resolveDir =(dir) = >{
  let res  = path.resolve(__dirname, dir)
  console.log(res)
  return res
}
Copy the code
   alias: {
        // Webpack configures the alias
        The '@': resolveDir('.. /src'),// @ points to the SRC directory
        'components': resolveDir(".. /src/components"),
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
        'react-native': 'react-native-web'.// Allows for better profiling with ReactDevTools. (isEnvProductionProfile && {'react-dom$': 'react-dom/profiling'.'scheduler/tracing': 'scheduler/tracing-profiling',}),... (modules.webpackAliases || {}), },Copy the code

The configuration of CRA

Proxy Proxy configuration

To request a proxy API in development, use http-proxy-middleware and create a new setupproxy.js file in the SRC directory. Note that the SRC directory is not the root directory of the project

const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:5000'.changeOrigin: true,})); };Copy the code

The test agent

If you want to test agents, start examples/proxy-middleware (github.com/yayxs/Netea…). This is a Nest project, install dependencies, then NPM run start

other

Basic configuration of the editor

React projects can use the vscode plugin to generate code snippets quickly

At the top of the project directory there is a.vscode that is not added to the ignore file, as you can see if you use the vscode editor

  • launch.json

    {
      "version": "0.2.0"."configurations": [{"name": "Chrome"."type": "chrome"."request": "launch"."url": "http://localhost:3001"."webRoot": "${workspaceFolder}/src"."sourceMapPathOverrides": {
            "webpack:///src/*": "${webRoot}/*"}}}]Copy the code
  • settings.json

    {
      "emmet.includeLanguages": {
        "javascript": "javascriptreact"}}Copy the code

Please search for specific meanings and functions by yourself

resources

Netease Cloud official wizard picture

  • S2.music.126.net/style/web2/… The head logo etc.
  • S2.music.126.net/style/web2/… Downloading a Client

Related reading

  • The react | react – redux | react – thunk | state | state management | state – management – compare

Q&A

Have what problem return please much exchange github.com/yayxs/Netea…

You can also add communication groups

  • Front end mutual fish 1 group 713593204

This is the first netease Cloud Web version of the project to share your small praise is my motivation to update, can see here people ah, are “idle people”, old fans know our play, the comment area of the most like friends, the next post directly top [read the comments]. Give it a star, baby