如何搭建一个react项目?

使用react很长时间,写个搭建项目的文档,用于新项目的快速启动。

本项目使用的技术栈:

  1. create-react-app
  2. react-router
  3. ant-design
  4. react-redux

一、使用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(
  <React.StrictMode>
    <HashRouter>
      {renderRoutes(routes)}
    </HashRouter>
  </React.StrictMode>,
  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 (
        <div>
        <h1>home</h1>
        <Redirect from='/' to='/home/home1' />
        {renderRoutes(props.route.routes)}
        </div>
    )
}
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(
  // <React.StrictMode>
  <Provider store={store}>
    <HashRouter>
      {renderRoutes(routes)}
    </HashRouter>
  </Provider>,
   
  // </React.StrictMode>,
  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(
      
        <Layout style={{ minHeight: '100vh' }}>
        
          <SideBar/>
          <Layout className="site-layout">
            <Header className="site-layout-background" style={{ padding: 0 }} />
            <Content style={{ margin: '0 16px' }}>
              <Breadcrumb style={{ margin: '16px 0' }}>
                <Breadcrumb.Item>User</Breadcrumb.Item>
                <Breadcrumb.Item>Bill</Breadcrumb.Item>
              </Breadcrumb>
              <div className="site-layout-background" style={{ padding: 24, minHeight: 360 }}>
                Bill is a cat.<button onClick={()=>clickMe()}>{props.counter}</button>
              </div>
            </Content>
            <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
          </Layout>
        </Layout>
  )
}
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 ? (
  <Switch {...switchProps}>
    {routes.map((route, i) => (
      <Route
        key={route.key || i}
        path={route.path}
        exact={route.exact}
        strict={route.strict}
        render={(props) => {
          if (!route.requiresAuth || authed || route.path === authPath) {
            return <route.component {...props} {...extraProps} route={route} />
          }
          return <Redirect to={{ pathname: authPath, state: { from: props.location } }} />
        }}
      />
    ))}
  </Switch>
) : null
 
export default renderRoutes

修改index.js中部分内容


const authed = false // 如果登陆之后可以利用redux修改该值
const authPath = '/login' 

ReactDOM.render(
  // <React.StrictMode>
  <Provider store={store}>
    <HashRouter>
      {renderRoutes(routes,authed,authPath)}
    </HashRouter>
  </Provider>,
   
  // </React.StrictMode>,
  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 ? <C {...this.props} /> : null;
    }
  }

  return AsyncComponent;
}

然后在router.js文件中引入该文件,同时修改路由组件引入方式:

import asyncComponent from './components/AsyncComponent';
const asyncWelcome = asyncComponent(()=>import("./pages/Welcome") )

此方法实现了路由的分包加载。

以上是搭建一个react基础框架的过程,后续我会再增加一些开发中可能会遇到的问题解决方法。

另外,欢迎关注我个人的公众号,web前端梦工厂,可以留言交流问题。

 

 

 

 

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值