使用react很长时间,写个搭建项目的文档,用于新项目的快速启动。
本项目使用的技术栈:
一、使用create-react-app创建项目
npx create-react-app my-app
cd my-app
npm start
二、引入react-router,创建基础路由及页面
cnpm install react-router-dom -S
使用react-router-config来设置路由
cnpm install --save react-router-config
用法:
在src目录下新建一个router.js文件,配置好路由信息,如下:
import Home from './pages/Home';
import Home1 from './pages/Home/children/Home1';
import Home2 from './pages/Home/children/Home2';
import NoPage from './pages/404';
import User from './pages/User';
const routes = [
{
path:'/',
// exact:true,
component: Home,
routes:[
{
path:'/home/home1',
exact:true,
component: Home1,
},
{
path:'/home/home2',
exact:true,
component: Home2,
}
]
},
{
path:'/404',
exact:true,
component: NoPage
},
{
path:'/user',
exact:true,
component: User
}
]
export default routes;
然后在src目录下的index.js文件中进行设置:
import routes from './router';
import {renderRoutes} from 'react-router-config';
ReactDOM.render(
{renderRoutes(routes)}
,
document.getElementById('root')
);
此时renderRoutes就与配置的路由信息routes进行了绑定。renderRoutes起了什么作用呢?它是根据route.js中配置的路由的层级,以及当前浏览器中的路由地址,在当前的页面中加载出对应层级的组件,比如上例中,我们在routes中配置的路由第一层级的path分别是/,/404,/user,那么此时在浏览器中敲入以上三个地址时,对应的组件内容就会在这个index.js中显示出来。那么路由嵌套该如何显示呢,下面我们来看一下Home组件的子组件如何显示。
Home组件有两个子组件,Home1与Home2,在Home组件中的代码如下:
import React from 'react';
import {HashRouter as Router,Redirect} from 'react-router-dom';
import {renderRoutes, matchRoutes} from 'react-router-config';
import routes from '../../router';
const Home = (props)=>{
console.log('props: ', props);
console.log('matchRoutes', matchRoutes(routes, "/home1"));
return (
home
{renderRoutes(props.route.routes)}
)
}
export default Home;
首先看Home组件的参数props,打印之后会发现props中包含了路由信息,如图:
其中,route下包含的routes就是Home组件下的子组件的路由信息,也就是之前在router.js文件中配置的路由信息。然后按照renderRoutes的语法,在Home组件中加载其子组件,renderRoutes(props.route.routes),同时加入想往子组件中通过路由传递一些其它的信息,可以这么写:
renderRoutes(props.route.routes, { someProp: "these extra props are optional" })
然后在子组件中通过props.someProp就能获取到这个信息。
以上通过react-router-config成功的进行了路由配置,但是并没有涉及到路由的鉴权,也就是哪些路由不需要登录就可以进入,哪些需要登录才能进入。鉴权的话,需要对renderRoutes方法进行重写,稍后会提到如何重写。
三、引入antd
cnpm install antd -S
然后在index.js中引入antd的css样式:
import 'antd/dist/antd.css';
接下来就可以在各个组件中引入antd的组件了。
四、设置开发环境和生产环境的请求地址
项目中ajax请求可以使用axios或者fetch,本例使用axios
首先安装axios
cnpm install axios -S
在src目录下新建一个utils目录,在这个目录下可以放一些公共的工具。在此目录下新建一个request.js,内容如下:
import axios from 'axios';
let baseURL = "http://www.baidu.com";
if(process.env.NODE_ENV == "development"){
baseURL = "/api";
}else if(process.env.NODE_ENV == "production"){
baseURL = "/user";
}
const service = axios.create({
baseURL: baseURL, // api的base_url
timeout: 200000, // 请求超时时间
withCredentials: true // 选项表明了是否是跨域请求
})
export default service;
上面例子是一个简单的axios配置例子,详细的配置可以见后面的内容。
然后,下一步进行proxy配置,也就是根据不同的baseURL指向不同的请求域名。
首先安装一下第三方的插件
cnpm install http-proxy-middleware -S
然后在src目录下新建一个setupProxy.js文件,文件内容如下:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api/',
createProxyMiddleware({
target: 'http://localhost:3000',
changeOrigin: true,
})
);
app.use(
'/user/',
createProxyMiddleware({
target: 'http://www.qiniu.com',
changeOrigin: true,
})
);
};
上述代码中的api和user,分别对应axios配置文件中不同环境下的baseURL。
配置成功后,重新运行项目,代理生效。
五、配置redux
一个项目中肯定离不开状态管理,所以redux必不可少。
首先安装redux和react-redux
cnpm install redux react-redux -S
为了使用方便,我们还需要安装react-actions
cnpm install redux-actions -S
然后在src目录下新建一个store文件夹
在此文件夹下新建四个文件
index.js,actionTypes.js,actionCreators.js,reducer.js,每个文件中代码如下
actionTypes.js
export const INCREMENT = 'increment1';
export const DECREMENT = 'decrement';
actionCreators.js
import { createActions } from 'redux-actions';
import {INCREMENT,DECREMENT} from './actionTypes';
export const actions = createActions({
[INCREMENT]: (amount = 1) => ({ amount }),
[DECREMENT]: (amount = 1) => ({ amount: -amount })
});
reducer.js
import { handleActions,combineActions} from 'redux-actions';
import {INCREMENT,DECREMENT} from './actionTypes';
const defaultState = { counter: 10 };
const reducer = handleActions(
{
[INCREMENT]:(state,action)=>{
return {...state,counter:state.counter+action.payload.amount}
},
[DECREMENT]:(state,action)=>{
return {...state,counter:state.counter-action.payload.amount}
}
},
defaultState
);
export default reducer;
index.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
然后在src下的index.js中,引入store
import { Provider } from 'react-redux'
import store from './store';
ReactDOM.render(
//
{renderRoutes(routes)}
,
// ,
document.getElementById('root')
);
最后,展示一下如何应用(在需要用到的组件中,按如下方式引入需要的文件)
import React,{ useState } from 'react';
import { Layout, Menu, Breadcrumb } from 'antd';
import {test} from '../api/test';
import { connect } from 'react-redux'
import {actions} from '../store/actionCreators';
// import './index.css';
import {
DesktopOutlined,
PieChartOutlined,
FileOutlined,
TeamOutlined,
UserOutlined,
} from '@ant-design/icons';
import SideBar from '../layouts/components/SideBar';
const { Header, Content, Footer, Sider } = Layout;
const { SubMenu } = Menu;
const Main = (props)=>{
const clickMe =()=>{
props.onClick(2);
}
return(
User
Bill
Bill is a cat.
)
}
const mapStateToProps = (state, ownProps) => {
return {
counter: state.counter
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: (a) => {
dispatch(actions.increment1(a))
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Main);
六、引入less文件的方式
因为引入less需要在webpack中进行配置,那么在不进行eject的情况下,antd官方推荐通过craco插件来引入,具体流程如下:
首先需要安装@craco/craco和craco-less,然后在项目根目录下(和package.json同级)新建一个文件:craco.config.js,文件内容如下:
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},
],
};
然后修改package.json的项目启动命令
"scripts": {
"start": "craco start",
},
重启项目即可。
当然,antd文档还说,使用 react-app-rewired 和 customize-cra也能实现引入less的功能。
七、重写renderRoutes方法,为路由加权限控制
import React from 'react'
import { Route, Redirect, Switch } from 'react-router-dom'
const renderRoutes = (routes, authed, authPath = '/login', extraProps = {}, switchProps = {}) => routes ? (
{routes.map((route, i) => (
{
if (!route.requiresAuth || authed || route.path === authPath) {
return
}
return
}}
/>
))}
) : null
export default renderRoutes
修改index.js中部分内容
const authed = false // 如果登陆之后可以利用redux修改该值
const authPath = '/login'
ReactDOM.render(
//
{renderRoutes(routes,authed,authPath)}
,
// ,
document.getElementById('root')
);
八、路由分包加载
在src/components下新建一个文件AsyncComponent.js,代码内容如下:
import React, { Component } from "react";
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null
};
}
async componentDidMount() {
const { default: component } = await importComponent();
this.setState({
component: component
});
}
render() {
const C = this.state.component;
return C ? : null;
}
}
return AsyncComponent;
}
然后在router.js文件中引入该文件,同时修改路由组件引入方式:
import asyncComponent from './components/AsyncComponent';
const asyncWelcome = asyncComponent(()=>import("./pages/Welcome") )
此方法实现了路由的分包加载。
以上是搭建一个react基础框架的过程,后续我会再增加一些开发中可能会遇到的问题解决方法。
另外,欢迎关注我个人的公众号,web前端梦工厂,可以留言交流问题。