Адрес источника (обновляется в режиме реального времени)GitHub.com/AsianStudent/net EA…
вики-адресGitHub.com/AsianStudent/net EA…
предисловие
история проекта
Основываясь на двух моментах: (1) Автор используетreact
разработка, но некоторые решения для решения проблемы все еще относительно устарели. Например: оredux
или используйте оригиналswitch
Напишите за один проход, например, используяthunk
Несколько состояний для обработки сетевого запроса включаютперед загрузкой Загрузка запись исключенияПодождите, с этим надо разобраться, будет много похожего кода, который выглядит неловко, но изменить план энтерпрайз-проекта непросто. так что планРазработать полноценный проект корпоративного уровняИнтегрируйте текущее лучшее решение проблемы, (2) сосредоточьтесь только на前端
реализация, плюс очень совершенная接口
(имеется в виду открытый исходный код网易云
Апи, это должен знать каждый). так что выбирай网抑云
Музыка.
О проекте
Этот предмет является JS 版本
(в настоящее время начиная с версии JS) проект персонального обучения, интегрирующий текущуюReact
Самая экстремальная практика в проекте, прогресс обновляется каждую неделю, стремясь стать лучшеreact
Процесс разработки проекта на уровне предприятия.
Поделитьсяreact
Мелкие точки в развитии. Надеюсь на совместное общение. Код будет синхронизирован с адресом склада в начале статьи (пока не размещен в облаке кодов).
Старайтесь писать одну статью в неделю进度分享
. Заинтересованные ребята могут нажатьstar
Это моя мотивация для обновления.
Итоги первой недели
основная завершенная часть
- неожиданно возникнуть
webpack
настроить, добавитьsrc
псевдоним - настроить
.vscode
добавить отладкуjson
настроить - аккуратный
Redux
Соответствующий практический процесс, прохождениеRedux
Процесс -
Api
интерфейсproxy
Отладка прокси -
axios
пакет иapi
Конфигурация -
react-router-dom
Начальная настройка маршрутизации
Preview
основное введение
Первое, что нужно сказать, это то, чтоэлемент используетcreate react app создавать . затем пройтиnpm run eject
неожиданно возникнутьwebpack
config для удаления отдельных зависимостей сборки
Структура каталогов
|-- LICENSE
|-- README.md // 描述文件
|-- build
|-- config
| |-- webpack.config.js // webpack 配置文件
|-- docs // 配套文档博文
| `-- images
|-- examples
| `-- proxy-middleware // nestjs 项目 用于测试接口代理
|-- jsconfig.json
|-- package.json
|-- public
| |-- favicon.ico
| `-- index.html
|-- scripts
|-- src
| |-- App.js // App 主应用
| |-- api // api 接口
| |-- assets // 资源
| |-- common // 公用配置
| |-- components // 组件
| |-- index.js
| |-- layouts // 布局
| |-- pages // 页面 views
| |-- router // react-router-dom 路由配置
| |-- services // axios 网络请求封装
| |-- store // redux 配置
| |-- styles // 样式文件
| |-- utils // 工具方法
|-- yarn.lock
зависимая среда
нпм-скрипт
Последнее времяscripts
сценарий
"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"
},
упаковка
Нажмите на ссылку ниже, чтобы перейти непосредственно на официальный сайт для удобного просмотра
-
"react-router-dom": "^5.2.0"(реактивная маршрутизация V5, основная версия)
-
Другое, которое будет добавлено
"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",
"react-redux": "^7.2.1",
"react-router-config": "^5.1.1",
"redux": "^4.0.5",
"redux-immutable": "^4.0.0",
"styled-components": "^5.1.1",
"webpack": "4.44.1",
просто скажи
-
antd
Версия V4 является последней версией, поскольку она отличается от версии V3. -
immutable
redux-immutable
Неизменяемое решение для избыточного потока данных (не самое лучшее) -
redux
react-redux
Все последние пакеты -
react
Версия является последней официальной версией
Node
другая среда
- node v14.8.0
запустить проект
клонировать проект
https://github.com/yayxs/NeteaseCloudMusic.git
Установить зависимости
npm run check && ncu // 检查依赖包版本 更新至最新并安装
Запуск проекта
npm run start
Выше命令
также можно заменить наyarn
подожди, если хочешь
О стиле проекта
использоватьantd
Библиотека компонентов +styled-components
комбинироватьsass
а также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;
}
`;
Напишите стиль указанным выше способом в сочетании сsass
Совместная работа над оформлением части страницы
инкапсуляция сетевых запросов axios
/*
* @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
* @
*/
// 引入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
// 全局请求拦截,发送请求之前执行
instance.interceptors.request.use(
function (config) {
// Do something before request is sent
// 设置请求的 token 等等
// config.headers["authorization"] = "Bearer " + getToken();
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
// Add a response interceptor
// 请求返回之后执行
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请求
* @param {*} url 请求地址
* @param {*} params
*/
export function get(url, params) {
return instance.get(url, {
params,
});
}
/**
* post请求
* @param {*} url 请求地址
* @param {*} data
*/
export function post(url, data) {
return instance.post(url, data);
}
/**
* put请求
* @param {*} url 请求地址
* @param {*} data
*/
export function put(url, data) {
return instance.put(url, data);
}
/**
* delete请求
* @param {*} url
*/
export function del(url) {
return instance.delete(url);
}
При использовании вы можете напрямую@/api
Создайте соответствующую папкуjs
файл, а затем настроить соответствующий интерфейс
/*
* @Author: yayxs
* @Date: 2020-08-26 22:35:37
* @LastEditTime: 2020-08-26 23:48:17
* @LastEditors: yayxs
* @Description: 推荐 API
* @FilePath: \NeteaseCloudMusic\src\api\recommend.js
* @
*/
// import request from "@/services/request";
import { get } from "@/services/request";
const fetchBannerListApi = () => get("/banner");
export { fetchBannerListApi };
Об интерфейсе
Пожалуйста, обратитесь кAPI NetEase Cloud Music NodeJS
обозначение компонента
с головойheader组件为例子
@/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"> 网易云音乐 </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;
-
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``;
редукционный процесс
Данные для этого проекта разделены на двеОдин из них — это состояние внутри компонента. В это время мы используемreact hooks
серединаuseState hook
решать
const [currIndex, setCurrIndex] = useState(0);
axios
Мы всегда получаем доступ к данным, запрошенным сетью вredux
общий процесс
import { Provider } from "react-redux";
import store from "./store";
ReactDOM.render(
<>
<Provider store={store}>
<App />
</Provider>
</>,
document.getElementById("root")
);
- 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;
состояние локального компонента
Что касается процесса локальной составляющей, то мы разделим его на несколько частей
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
* @
*/
// 获取轮播图数据
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";
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) => {});
};
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:
return state;
}
};
о вышеизложенномredux
С одной стороны, вы можете обратить внимание на этот проект для просмотра полного кода, с другой стороны**Я составил практику лонгитюдного анализа редукс-процесса (обновлено в августе 2020 г.)**
Конечно, вы также можете напрямуюjuejin 查看
(если у вас возникли проблемы с сетью)nuggets.capable/post/684490…
Отложенная загрузка маршрута
const YYHeaderComp = lazy(() => import("./components/header/index"));
const YYFooterComp = lazy(() => import("./components/footer/index"));
конфигурация псевдонима webpack
Этот проект путем простой модификацииwebpack
конфиг для добавления алиасов (псевдонимов)
const resolveDir =(dir)=>{
let res = path.resolve(__dirname, dir)
console.log(res)
return res
}
alias: {
// webpack 配置别名
'@': resolveDir('../src') ,// 这样配置后 @ 可以指向 src 目录
'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 || {}),
},
Конфигурация CRA
прокси конфигурация прокси
агент в разработкеAPI
запрос, черезhttp-proxy-middleware
затем вsrcновый каталогsetupProxy.js
документ. Обратите внимание, чтоsrc
Каталог не является корневым каталогом проекта
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};
тестовый агент
Если вы хотите протестировать прокси, вы можете начатьexamples/proxy-middleware
(GitHub.com/AsianStudent/net EA…) это вложенный проект, устанавливаем зависимости, затемnpm run start
разное
Базовая конфигурация редактора
ПисатьReact
Предметы могут использоватьvscode
Плагин, быстро генерирующий фрагменты кода
В самом верху каталога проекта есть.vscode
и не добавляется для игнорирования файлов, если вы используетеvscode
Редактор, ты увидишь
-
launch.json
{ "version": "0.2.0", "configurations": [ { "name": "Chrome", "type": "chrome", "request": "launch", "url": "http://localhost:3001", "webRoot": "${workspaceFolder}/src", "sourceMapPathOverrides": { "webpack:///src/*": "${webRoot}/*" } } ] }
-
settings.json
{ "emmet.includeLanguages": { "javascript": "javascriptreact" } }
Пожалуйста, обратитесь к конкретному значению и функции搜索
ресурс
Официальная карта спрайтов облака Netease
- 2.music.126.net/style/Web2/…логотип на голове и т. д.
- 2.music.126.net/style/Web2/…скачать приложения
Связанное чтение
Q&A
Если у вас есть какие-либо вопросы, пожалуйста, свяжитесь с нами большеGitHub.com/AsianStudent/net EA…
Вы также можете добавить группы чата
- Фронтальная взаимная рыбка 1 группа 713593204
Это网易云web版本项目的第一篇分享
Ваши маленькие лайки - это мотивация для меня, чтобы обновить. Люди, которые могут видеть здесь, все "бездельники". Старые фанаты знают, как мы играем. Друг с наибольшим количеством лайков в области комментариев напрямую закрепит следующую статью наверху [прочитать комментарий]. датьstar
бар дорогой