react+antd+rdeux+react-router+ts搭建后台管理系统

3,254 阅读5分钟

前言

快速搭建我们的管理系统,这里记录一下自己的一些学习经验,希望能为小伙伴们带来方便,话不多说,开始我们的正文。

create-react-app脚手架生成项目

使用yarn安装

    yarn create react-app my-demo --typescript

使用npm安装(npx 来自 npm 5.2+ 或更高版本)

    npx create-react-app my-demo --typescript

安装完成之后

    cd my-demo
    yarn start
    or
    npm start

此时浏览器会访问 http://localhost:3000/ ,看到 Learn React 的界面就算成功了。

此时我们删除掉多余文件,保留最简单文件。替换index和app的内容。

index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

app.tsx

import React from 'react';

function App() {
  return (
    <div className="App">
      welcome to my-demo
    </div>
  );
}

export default App;

最后目录如下:

├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.tsx
│   ├── index.tsx
│   └── react-app-env.d.ts
├── package.json
├── README.md
├── tsconfig.json
└── yarn.lock

安装antd

因为官方脚手架把webpack封装在内部,如果我们想要自己配置webpack,可以使用yarn eject命令暴露相关配置,这里我选择暴露webpack。(注:此命令不可逆转)

    yarn eject
    or
    npm run eject

这时我们可能会在执行命令时候报错,原因是git没有提交相应操作代码。

第一步

    git add.

第二步

    git commit -m "init"

接下来再重新执行eject命令就好了。

安装antd(下面命令都用yarn做演示)

    yarn add antd

使用 babel-plugin-import 来进行按需加载

    yarn add babel-plugin-import

修改webpack.config.js文件大概374行,增加如下代码

    ["import", {
        "libraryName": "antd",
        "libraryDirectory": "es",
        "style": "css"
      }],

增加less配置

    yarn add less less-loader

修改webpack.config.js 52行新增

    const lessRegex = /\.less$/;
    const lessModuleRegex = /\.module\.less$/;

在sass-loader配置后新增

    {
      test: lessRegex,
      exclude: lessModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 3,
          sourceMap: isEnvProduction && shouldUseSourceMap,
        },
        'less-loader'
      ),
      sideEffects: true,
    },
    {
      test: lessModuleRegex,
      use: getStyleLoaders(
        {
          importLoaders: 3,
          sourceMap: isEnvProduction && shouldUseSourceMap,
          modules: {
            getLocalIdent: getCSSModuleLocalIdent,
          },
        },
        'less-loader'
      ),
    },

安装react-router-dom

    yarn add react-router-dom @types/react-router-dom

接下来我们修改一下文件目录并且添加相应的文件

├── config
│   ├── env.js
│   ├── getHttpsConfig.js
│   ├── jest
│   │   ├── cssTransform.js
│   │   └── fileTransform.js
│   ├── modules.js
│   ├── paths.js
│   ├── pnpTs.js
│   ├── webpack.config.js
│   └── webpackDevServer.config.js
├── public
│   ├── favicon.ico
│   └── index.html
├── scripts
│   ├── build.js
│   ├── start.js
│   └── test.js
├── src
│   ├── App.tsx
│   ├── components
│   ├── index.tsx
│   ├── pages
│   │   ├── home
│   │   │   ├── index.less
│   │   │   └── index.tsx
│   │   └── login
│   │       ├── index.less
│   │       └── index.tsx
│   ├── react-app-env.d.ts
│   └── router
│       └── main.tsx
├── package.json
├── README.md
├── tsconfig.json
└── yarn.lock

对应有修改的文件

app.tsx

import React from 'react';
import { HashRouter } from "react-router-dom";
import Main from './router/main'

function App() {
  return (
    <HashRouter>
      <Main />
    </HashRouter>
  );
}

export default App;

main.tsx

import React from "react";
import { Switch, Route, Redirect, Link, withRouter } from "react-router-dom";
import Home from '../pages/home';
import Login from '../pages/login';

export default () => (
    <Switch>
        <Route path='/home' component={Home} />
        <Route path='/login' component={Login} />
        <Redirect to='/login' />
    </Switch>
)

home/index.tsx

import React, { Component } from 'react';
import {Button} from 'antd';

interface Iprops{
    history:any
}

class Home extends Component<Iprops,any> {
    render() {
        return (
            <div>
                <h1>我是主页</h1>
                <Button type='primary' onClick={()=>this.props.history.push('/login')}>跳转到登录页</Button>
            </div>
        );
    }
}

export default Home;

login/index.tsx

import React, { Component } from 'react';
import {Button} from 'antd';

interface Iprops{
    history:any
}

class Login extends Component<Iprops,any> {
    render() {
        return (
            <div>
                <h1>我是登录页</h1>
                <Button type='primary' onClick={()=>this.props.history.push('/home')}>跳转到主页</Button>
            </div>
        );
    }
}

export default Login;

到此为止我们的路由就已经可以正常跳转了

最后加入redux

    yarn add redux react-redux @types/react-redux

具体redux使用方法请自行查找相关资料,本文只给出具体列子不做详细讲解

下面就拿home页面做一个todolist演示,最终文件如下:

文件目录:

├── config
│   ├── env.js
│   ├── getHttpsConfig.js
│   ├── jest
│   │   ├── cssTransform.js
│   │   └── fileTransform.js
│   ├── modules.js
│   ├── paths.js
│   ├── pnpTs.js
│   ├── webpack.config.js
│   └── webpackDevServer.config.js
├── public
│   ├── favicon.ico
│   └── index.html
├── scripts
│   ├── build.js
│   ├── start.js
│   └── test.js
├── src
│   ├── App.tsx
│   ├── components
│   ├── index.tsx
│   ├── pages
│   │   ├── home
│   │   │   ├── index.less
│   │   │   ├── index.tsx
│   │   │   └── store
│   │   │       ├── action.tsx
│   │   │       └── reducer.tsx
│   │   └── login
│   │       ├── index.less
│   │       └── index.tsx
│   ├── react-app-env.d.ts
│   ├── router
│   │   └── main.tsx
│   └── store
│       ├── index.tsx
│       └── reducer.tsx
├── package.json
├── README.md
├── tsconfig.json
└── yarn.lock

store/index.tsx

import {createStore} from 'redux'
import reducer from './reducer'

const store=createStore(reducer)

export default store

store/reducer.tsx

import {combineReducers} from 'redux'
import HomeReducer from '../pages/home/store/reducer';

const reducer=combineReducers({
    Home:HomeReducer
})
export default reducer

home/index.tsx

import React, { Component } from 'react';
import { Button, Input, Row, Col, Divider, List } from 'antd';
import { connect } from 'react-redux'
import { changeData } from './store/action'
import './index.less'

const { Search } = Input;

interface Iprops {
    history: any
    homeState: Array<any>
    changeData: Function
}

class Home extends Component<Iprops, any> {
    constructor(props: any) {
        super(props)
        this.state = {
            value: ''
        }
    }
    private handleSearch = (value: any) => {
        const { changeData } = this.props;
        changeData({ action: 'ADD', value })
        this.setState({ value: '' })
    }
    private handeChange = (e: any) => {
        this.setState({ value: e.target.value })
    }
    private deleteItem = (index: number) => {
        const { changeData } = this.props;
        changeData({ action: 'DEL', value: index })
    }
    render() {
        const { homeState } = this.props;
        return (
            <div>
                <h1>我是主页</h1>
                <Button type='primary' onClick={() => this.props.history.push('/login')}>跳转到登录页</Button>
                <Divider orientation="center">todolist演示</Divider>
                <Row justify='center' gutter={[16, 16]}>
                    <Col span={10} >
                        <Search
                            value={this.state.value}
                            placeholder="请输入内容"
                            enterButton="确定"
                            size="large"
                            onSearch={this.handleSearch}
                            onChange={this.handeChange}
                        />
                        <List
                            bordered
                            dataSource={homeState}
                            renderItem={(item: any, index: number) => (
                                <List.Item>
                                    <div className='content'>
                                        <span>{item}</span>
                                        <span className='del' onClick={this.deleteItem.bind(this, index)}>删除</span>
                                    </div>
                                </List.Item>
                            )}
                        />
                    </Col>
                </Row>
            </div>
        );
    }
}

// 把store中的数据映射到组件的props
const mapStateToProps = (state: any) => ({
    homeState: state.Home
})
// 把store的Dispatch映射到组件的props
const mapDispatchToProps = (dispath: any) => ({
    changeData: (data: any) => dispath(changeData(data))
})

export default connect(mapStateToProps, mapDispatchToProps)(Home);

home/index.less

.content{
    width: 100%;
    display: flex;
    justify-content: space-between;
    .del{
        cursor: pointer;
        color: red;
    }
}

home/store/action.tsx

export const ADD_DATA='ADD_DATA';  // 增加数据
export const DEL_DATA='DEL_DATA';  // 删除数据

// 改变state的action
export  const changeData=(data:any)=>{
    if(data.action==='ADD'){
        return{
            type:ADD_DATA,
            value:data.value
        }
    }else{
        return{
            type:DEL_DATA,
            value:data.value
        }
    }
}

home/store/reducer.tsx

let defaultState:Array<any>=[]

const todolist=(state:Array<any>=defaultState,action:any)=>{
    const newState=[...state]
    switch(action.type){
        case 'ADD_DATA':
                newState.push(action.value)
            return newState
        case 'DEL_DATA':
                newState.splice(action.value,1)
            return newState
        default:return state
    }
}
export default todolist

App.tsx

import React from 'react';
import { HashRouter } from "react-router-dom";
import Main from './router/main';
import { Provider } from 'react-redux';
import store from './store';

function App() {
  return (
    <Provider store={store}>
      <HashRouter>
        <Main />
      </HashRouter>
    </Provider>
  );
}

export default App;

到此完毕,以上就是整个项目的完整的代码了,希望能对各位有所帮助。

项目代码:github.com/heiantiansh…