react项目创建
个人纪录,有些杂乱(介意勿看)
主要包括配置别名、配置路由、使用 styled-components、axios 封装、redux管理数据、immutable的使用等
1、 目录结构
2、配置css
yarn add normalize.css
重置 css reset.css
3、配置别名
yarn add @craco/craco
修改package.json
` "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "react-scripts eject" },`
新建并配置 craco.config.js
const path = require("path");
const resolve = (dir) => path.resolve(__dirname, dir);
module.exports = {
webpack: {
alias: {
"@": resolve("src"),
components: resolve("src/components"),
},
},
};
4、安装路由
yarn add react-router-dom
统一管理路由 安装
yarn add react-router-config
配置路由index
import Discover from "@/pages/discover";
import Friend from "@/pages/friend";
import Mine from "@/pages/mine";
const routes = [
{
path: "/",
exact: true,
component: Discover,
},
{
path: "/mine",
component: Mine,
},
{
path: "/friend",
component: Friend,
},
];
export default routes;
导航添加链接
import React, { memo } from "react";
import { NavLink } from "react-router-dom";
export default memo(function AppHeader() {
return (
<div>
<NavLink to="/">发现音乐</NavLink>
<NavLink to="/mine">我的音乐</NavLink>
<NavLink to="/friend">我的朋友</NavLink>
</div>
);
});
APP.js使用renderRoutes
import React from "react";
import { renderRoutes } from "react-router-config";
import AppFooter from "@/components/app-footer";
import AppHeader from "@/components/app-header";
import routes from "@/router";
import { HashRouter } from "react-router-dom";
export default function App() {
return (
<HashRouter>
<AppHeader />
{/* 路由映射到的对应组件 */}
{renderRoutes(routes)}
<AppFooter />
</HashRouter>
);
}
使用 styled-components
yarn add styled-components
style.js
import styled from "styled-components";
export const HeaderWrapper = styled.div`
height: 75px;
font-size: 14px;
color: #fff;
background-color: #242424;
position: relative;
.content {
height: 70px;
display: flex;
justify-content: space-between;
}
.divider {
height: 5px;
background-color: #c20c0c;
width: 100%;
position: absolute;
left: 0;
bottom: 0;
}
`;
使用style创建的组件HeaderWrapper
使用ant-design及其图标
yarn add antd
yarn add @ant-design/icons
获取子路由对应的组件
子路由:routes里的routes
获取子路由对应的组件
import React, { memo } from "react";
import { renderRoutes } from "react-router-config";
import { dicoverMenu } from "@/common/local-data";
import { DiscoverWrapper, TopMenu } from "./style";
import { NavLink } from "react-router-dom";
export default memo(function Discover(props) {
// 从props里拿到route对象,里面包含有子路由数组
const { route } = props;
console.log(route);
return (
<DiscoverWrapper>
<div className="top">
<TopMenu className="wrap-v1">
{dicoverMenu.map((item, index) => {
return (
<div className="item" key={item.title}>
<NavLink to={item.link}>{item.title}</NavLink>
</div>
);
})}
</TopMenu>
</div>
{/* 路由映射到的对应组件 */}
{renderRoutes(route.routes)}
</DiscoverWrapper>
);
});
引入axios
yarn add axios
axios 封装
config.js
const devBaseUrl = "http://xxx";
const proBaseUrl = "http://xxx";
//判断当前环境是开发环境还是生产环境
export const BASE_URL =
process.env.NODE_ENV === "development" ? devBaseUrl : proBaseUrl;
export const TIMEOUT = 5000;
request.js
import axios from "axios";
import { BASE_URL, TIMEOUT } from "./config";
//axios.create 能创造一个新的 axios 实例
const instance = axios.create({
baseURL: BASE_URL,
timeout: TIMEOUT,
});
// 请求和响应拦截
// 请求拦截
instance.interceptors.request.use(
(config) => {
// 1.发送网络请求时, 在界面的中间位置显示Loading的组件
// 2.某一些请求要求用户必须携带token, 如果没有携带, 那么直接跳转到登录页面
// 3.params/data序列化的操作
// console.log("请求被拦截");
return config;
},
(err) => {}
);
instance.interceptors.response.use(
(res) => {
return res.data;
},
(err) => {
if (err && err.response) {
switch (err.response.status) {
case 400:
console.log("请求错误");
break;
case 401:
console.log("未授权访问");
break;
default:
console.log("其他错误信息");
}
}
return err;
}
);
export default instance;
页面使用:
import request from "@/services/request";
useEffect(() => {
request({
url: "/banner",
}).then((res) => {
console.log(res);
});
return () => {};
}, []);
使用redux管理数据
yarn add redux react-redux redux-thunk
创建store(使用redux-thunk中间件)
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
const store = createStore(reducer, applyMiddleware(thunk));
export default store;
reducer(使用combineReducers )
import { combineReducers } from "redux";
import { reducer as recommendReducer } from "../pages/discover/c-pages/recommend/store";
const cReducer = combineReducers({
recommend: recommendReducer,
});
export default cReducer;
App.js获得到共享的store
import { Provider } from 'react-redux'
import store from './store'
// 共享store
<Provider store={store}>
<HashRouter>
<AppHeader />
{/* 路由映射到的对应组件 */}
{renderRoutes(routes)}
<AppFooter />
</HashRouter>
</Provider>
使用创建的新的axios实例请求数据
推荐(recommend组件)单独创建一个sore文件夹,新建对应文件
1、actionCreators.js
2、constants.js
export const CHANGE_TOP_BANNERS = "recommend/CHANGE_TOP_BANNERS";
3.index.js
导出reducer
import reducer from "./reducer";
export { reducer };
reducer.js
import * as actionTypes from "./constants";
const defaultState = {
topBanners: [],
};
function reducer(state = defaultState, action) {
switch (action.type) {
case actionTypes.CHANGE_TOP_BANNERS:
return { ...state, topBanners: action.topBanners };
default:
return state;
}
}
export default reducer;
在recommend/index.js
import React, { memo, useEffect } from "react";
import { connect } from "react-redux";
import { getTopBannerAction } from "./store/actionCreators";
function Recommend(props) {
const { getBanners, topBanners } = props;
useEffect(() => {
getBanners();
return () => {};
}, [getBanners]);
return (
<div>
<h2>Recommend{topBanners.length}</h2>
</div>
);
}
const mapStateToProps = (state) => ({
topBanners: state.recommend.topBanners,
});
const mapDispathToProps = (dispatch) => ({
getBanners: () => {
dispatch(getTopBannerAction());
},
});
export default connect(mapStateToProps, mapDispathToProps)(memo(Recommend));
使用immutable.js和 redux hook
yarn add immutable
reducer.js
import { Map } from "immutable";
import * as actionTypes from "./constants";
//immutable Map
const defaultState = Map({
topBanners: [],
});
function reducer(state = defaultState, action) {
switch (action.type) {
case actionTypes.CHANGE_TOP_BANNERS:
return state.set("topBanners", action.topBanners);
default:
return state;
}
}
export default reducer;
index.js
import React, { memo, useEffect } from "react";
//shallowEqual useSelector的第二个参数,进行浅层比较
import { useSelector, useDispatch, shallowEqual } from "react-redux";
// import { connect } from "react-redux";
import { getTopBannerAction } from "./store/actionCreators";
function Recommend(props) {
//使用hook redux
//react hooks中使用useSelector获取存在redux中的状态
//useSelector存在性能问题,需传入第二个参数shallowEqual进行浅层比较
const { topBanners } = useSelector(
(state) => ({
// topBanners: state.recommend.topBanners,
//使用immutable的Map中的get
topBanners: state.recommend.get("topBanners"),
}),
shallowEqual
);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTopBannerAction());
}, [dispatch]);
return (
<div>
<h2>Recommend{topBanners.length}</h2>
</div>
);
}
export default memo(Recommend);
combineReducers中使用immutable
yarn add redux-immutable
import { combineReducers } from "redux-immutable";
import { reducer as recommendReducer } from "../pages/discover/c-pages/recommend/store";
import { reducer as playerReducer } from "../pages/player/store";
const cReducer = combineReducers({
recommend: recommendReducer,
player: playerReducer,
});
export default cReducer;