后端程序员快速入门React+Antd进行开发

本文最后更新于2023.05.16-07:16,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥

一,通过create-react-app脚手架创建项目

安装脚手架

npm install -g create-react-app

安装yarn包管理器

npm install -g yarn

创建项目

npx create-react-app my-app --template typescript

如图我们就已经搭建好了一个react项目雏形

快速启动项目,运行

cd my-app
npm start
或
yarn start

 二,引入Antd

yarn add antd

三,快速搭建后台模板

打开antd官网:Ant Design - 一套企业级 UI 设计语言和 React 组件库

配置App.tsx

import './App.css';
import React, {useState} from 'react';
import {
    DesktopOutlined,
    FileOutlined,
    MenuFoldOutlined,
    MenuUnfoldOutlined,
    PieChartOutlined,
    TeamOutlined,
    UserOutlined,
} from '@ant-design/icons';
import type {MenuProps} from 'antd';
import {Button, Breadcrumb, Layout, Menu, theme} from 'antd';
import {Link} from "react-router-dom";
import {renderRoutes} from "react-router-config";
import routes from "./router/index";

const {Header, Content, Footer, Sider} = Layout;

type MenuItem = Required<MenuProps>['items'][number];

function getItem(
    label: React.ReactNode,
    key: React.Key,
    icon?: React.ReactNode,
    children?: MenuItem[],
): MenuItem {
    return {
        key,
        icon,
        children,
        label,
    } as MenuItem;
}

const items: MenuItem[] = [
    getItem(<Link to="/">主页</Link>, '1', <PieChartOutlined/>),
    getItem(<Link to="/user">用户管理</Link>, '2', <DesktopOutlined/>),
    getItem('文章管理', 'sub1', <UserOutlined/>, [
        getItem(<Link to="/article">所有文章</Link>, '3'),
        getItem('Bill', '4'),
        getItem('Alex', '5'),
    ]),
    getItem('Team', 'sub2', <TeamOutlined/>, [getItem('Team 1', '6'), getItem('Team 2', '8')]),
    getItem('Files', '9', <FileOutlined/>),
];

const App: React.FC = () => {
    const [collapsed, setCollapsed] = useState(false);
    const {
        token: {colorBgContainer},
    } = theme.useToken();
    return (
        <Layout style={{minHeight: '100vh'}}>
            <Sider collapsible collapsed={collapsed}
                   onCollapse={(value) => setCollapsed(value)}>
                <div style={{height: 32, margin: 16, background: 'rgba(255, 255, 255, 0.2)'}}>
                    React-Antd-Admin
                </div>

                <Menu theme="dark" defaultSelectedKeys={['1']} mode="inline" items={items}/>
            </Sider>
            <Layout className="site-layout">
                <Header style={{padding: 0, background: colorBgContainer}}>
                    <Button
                        type="text"
                        icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
                        onClick={() => setCollapsed(!collapsed)}
                        style={{
                            fontSize: '16px',
                            width: 64,
                            height: 64,
                        }}
                    />
                </Header>
                <Content style={{margin: '0 16px'}}>
                    <Breadcrumb items={[
                        {
                            title: <a href="">Home</a>,
                        },
                        {
                            title: 'User',
                        }
                    ]} style={{margin: '16px 0'}}/>

                    <div style={{padding: 24, minHeight: 640, background: colorBgContainer}}>
                        {renderRoutes(routes)}
                    </div>
                </Content>
                <Footer style={{textAlign: 'center'}}>Ant Design ©2023 Created by Ant UED</Footer>
            </Layout>
        </Layout>
    );
};

export default App;

 四,配置路由

yarn add react-router-dom@5.2.0 
yarn add react-router-config 
yarn add @types/react-router-config 
yarn add @types/react-router-dom 
yarn add react-router@5.2.0 -S

router/index.ts

import { lazy } from 'react'

const routes = [
    {
        path:"/article",
        component: lazy(()=>import('../views/Article')),
        meta: {
            title:"文章管理",
        },
        exact: true,
        routes: []
    },
    {
        path:"/user",
        component: lazy(()=>import('../views/user/index')),
        meta: {
            title:"用户管理",
        },
        child: []
    },
    {
        path:"/",
        component: lazy(()=>import('../views/Home')),
        meta: {
            title:"App",
        },
        child: []
    },
]
export default routes;

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { HashRouter as Router } from "react-router-dom"

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
      <Router>
          <App />
      </Router>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

创建路由组件

views/user/index.tsx

import React, {useState} from 'react';
import {Button, Form, Input, Modal, Pagination, Space, Table, Tag} from 'antd';
import type {ColumnsType} from 'antd/es/table';
import {DeleteOutlined, EditOutlined,SearchOutlined} from '@ant-design/icons';
import UserForm from "./components/UserForm";

interface DataType {
    key: string;
    name: string;
    age: number;
    address: string;
    tags: string[];
}

const rowSelection = {
    onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
        console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
    },
    getCheckboxProps: (record: DataType) => ({
        disabled: record.name === 'Disabled Index',
        // Column configuration not to be checked
        name: record.name,
    }),
};

const data: DataType[] = [
    {
        key: '1',
        name: 'John Brown',
        age: 32,
        address: 'New York No. 1 Lake Park',
        tags: ['nice', 'developer'],
    },
    {
        key: '2',
        name: 'Jim Green',
        age: 42,
        address: 'London No. 1 Lake Park',
        tags: ['loser'],
    },
    {
        key: '3',
        name: 'Joe Black',
        age: 32,
        address: 'Sydney No. 1 Lake Park',
        tags: ['cool', 'teacher'],
    },
    {
        key: '4',
        name: 'John Brown',
        age: 32,
        address: 'New York No. 1 Lake Park',
        tags: ['nice', 'developer'],
    },
    {
        key: '5',
        name: 'Jim Green',
        age: 42,
        address: 'London No. 1 Lake Park',
        tags: ['loser'],
    },
    {
        key: '6',
        name: 'Joe Black',
        age: 32,
        address: 'Sydney No. 1 Lake Park',
        tags: ['cool', 'teacher'],
    },

];

const Index: React.FC = () => {
    const [open, setOpen] = useState(false);
    const [title, setTile] = useState<String>();
    const [rowData, setRow] = useState(null);
    const [confirmLoading, setConfirmLoading] = useState(false);

    const showModal = (title: String, record: any) => {
        setRow(record);
        setTile(title);
        setOpen(true);
    };

    const handleOk = () => {
        setConfirmLoading(true);
        setTimeout(() => {
            setOpen(false);
            setConfirmLoading(false);
        }, 2000);
    };

    const handleCancel = () => {
        console.log('Clicked cancel button');
        setOpen(false);
    };

    const onFinish = (values: any) => {
        console.log('Success:', values);
    };

    const onFinishFailed = (errorInfo: any) => {
        console.log('Failed:', errorInfo);
    };
    const columns: ColumnsType<DataType> = [
        {
            title: 'Name',
            dataIndex: 'name',
            key: 'name',
            render: (text: String) => <a>{text}</a>,
        },
        {
            title: 'Age',
            dataIndex: 'age',
            key: 'age',
        },
        {
            title: 'Address',
            dataIndex: 'address',
            key: 'address',
        },
        {
            title: 'Tags',
            key: 'tags',
            dataIndex: 'tags',
            render: (tags: string[]) => (
                <span>
        {tags.map((tag) => {
            let color = tag.length > 5 ? 'geekblue' : 'green';
            if (tag === 'loser') {
                color = 'volcano';
            }
            return (
                <Tag color={color} key={tag}>
                    {tag.toUpperCase()}
                </Tag>
            );
        })}
      </span>
            ),
        },
        {
            title: 'Action',
            key: 'action',
            render: (_, record) => (
                <Space size="middle">
                    <Button type={"primary"} ghost icon={<EditOutlined/>}
                            onClick={() => showModal('修改', record)}>Edit</Button>
                    <Button type={"primary"} danger ghost icon={<DeleteOutlined/>}>Delete</Button>
                </Space>
            ),
        },
    ];
    return (
        <div>
            <div style={{paddingBottom: '20px'}}>
                <Form layout={"inline"}>
                    <Form.Item
                        label="姓名"
                        name="name"
                    >
                        <Input name={'name'} style={{width: 180}} placeholder={'请输入需要搜索是关键词'}/>
                    </Form.Item>
                    <Form.Item>
                        <Input name={'name'} style={{width: 180,marginLeft: 10}} placeholder={'请输入需要搜索是关键词'}/>
                    </Form.Item>
                    <Form.Item>
                        <Button name={'search'}  icon={<SearchOutlined />} >搜索</Button>
                        <Button type={"primary"} danger name={'reset'} style={{marginLeft: 10}} icon={<SearchOutlined/>} >重置</Button>
                    </Form.Item>
            </Form>
        </div>
    <div style={{paddingBottom: '20px'}}>
        <Button type={"primary"} onClick={() => showModal('添加', null)}>add</Button>
        <Button type={"primary"} style={{
            marginLeft: '20px'
        }}>delete</Button>
    </div>
    <Table columns={columns} rowSelection={{
        type: 'checkbox',
        ...rowSelection,
    }} dataSource={data} pagination={false}/>
    <Pagination
        total={85}
        showSizeChanger
        showQuickJumper
        showTotal={(total) => `总共 ${total} 条`}
    />
    <UserForm title={title}
              open={open}
              onFinish={onFinish}
              onFinishFailed={onFinishFailed}
              handleCancel={handleCancel}
              handleOk={handleOk}
              confirmLoading={confirmLoading}
              rowData={rowData}
    />
</div>
);
};

export default Index;

弹出窗组件

views/user/components/UserForm.tsx

import React, {useState} from 'react';
import {Button, Modal, Form, Input, InputNumber} from 'antd';
import {DeleteOutlined, EditOutlined} from '@ant-design/icons';


const UserForm: React.FC<any> = (props) => {
    // console.log(props)
    const {title,open,rowData,handleOk,onFinish,onFinishFailed,handleCancel,confirmLoading,}= props;
    const [form] = Form.useForm();
    form.resetFields();
    if(title==='修改'){
        form.setFieldsValue(rowData);
    }
    console.log(form)
    return (
        <div>
            <Modal forceRender
                title={title}
                open={open}
                onOk={handleOk}
                confirmLoading={confirmLoading}
                onCancel={handleCancel}
            >
                <Form
                    form={form}
                    name="basic"
                    labelCol={{ span: 6 }}
                    wrapperCol={{ span: 12 }}
                    style={{ maxWidth: 600 }}
                    initialValues={{ remember: true }}
                    onFinish={onFinish}
                    onFinishFailed={onFinishFailed}
                    autoComplete="off"
                >
                    <Form.Item
                        label="姓名"
                        name="name"
                        rules={[{ required: true, message: 'Please input your username!' }]}
                    >
                        <Input allowClear />
                    </Form.Item>

                    <Form.Item
                        label="age"
                        name="age"
                        rules={[{ required: true, message: 'Please input your password!' }]}
                    >
                        <InputNumber  />
                    </Form.Item>

                    <Form.Item
                        label="address"
                        name="address"
                        rules={[{ required: true, message: 'Please input your password!' }]}
                    >
                        <Input allowClear/>
                    </Form.Item>
                </Form>
            </Modal>
        </div>
    );
};

export default UserForm;

五,配置less

暴露配置项

yarn eject

安装lessless-loader

 yarn add less less-loader -S

配置webpack.config.js,在 //style files regexes 最下面配置less

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

在大约530行左右配置

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

如何创建index.less 和index.scss并引用,重新启动项目

如果出现

如果 Failed to complie,就重新安装一下即可

yarn add sass -s

六,配置axios和反向代理

安装axios 和 http-proxy-middleware

yarn add axios http-proxy-middleware -s

在config下创建 setupProxy.js

const {createProxyMiddleware} = require('http-proxy-middleware');

module.exports = function(app) {
    app.use('/api', createProxyMiddleware({
        target: 'http://localhost:8080/',//后台服务器地址
        changeOrigin: true,
        pathRewrite: {
            '^/api': '',
        },}))
}

然后修改 config下 path.js的相关配置

配置 封装 /http/request.ts

import axios from "axios"
// 创建axios 赋值给常量service

const service = axios.create({
    baseURL: '/api',
    timeout: 5000,
    headers: {//请求头
        "Content-Type": "application/json;charset=UTF-8",
        // "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
        "token": null,
    }
});

// 添加请求拦截器(Interceptors)
service.interceptors.request.use(function (config) {
    // 发送请求之前做写什么
    let token =  localStorage.getItem("token");
    // 如果有token
    if(token){
        // 将token放在请求头
        config.headers.authorization = token;
    }

    return config;
}, function (error) {
    // 请求错误的回调
    return Promise.reject(error);
});

// 添加响应拦截器
service.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
}, function (error) {
    // 响应错误的回调
    return Promise.reject(error);
});


export default service

配置 /http/index.ts

import service from "./request";

// 这个模块封装各种请求方式(如:get,post等等)
export function post(url: any, params: {}) {
    return new Promise(function (resovle, reject) {
        service.post(url, params)
            .then((res: any) => {
                if (!res.data) {
                    resovle(res);
                }
                resovle(res.data);
            }, (err: any) => {
                reject(err);
            })
            .catch((err: any) => {
                reject(err);
            });
    });
}

export function get(url: String, params: []) {
    let querystr = "";
    for (let key in params) {
        if (params[key]) {
            querystr += `${key}=${params[key]}&`
        }
    }

    return new Promise(function (resovle, reject) {
        service.get(url + "?" + querystr)
            .then((res: any) => {
                if (!res.data) {
                    resovle(res);
                }
                resovle(res.data);
            }, (err: any) => {
                reject(err);
            })
            .catch((err: any) => {
                reject(err);
            });
    });
}

 

请求api 封装 /api/user.ts

//index.ts

import Service from "../http/request"
//获取所有用户
export function getUserList(config:any) {
    const params = new URLSearchParams()
    params.append("pageNum", config.pageNum)
    params.append("pageSize", config.pageSize)
    return Service({
        url: "/getUsers",
        data: params,
    })
}

调用服务

 getUserList(page).then((res: any) => {
        setUsers(res);
        setPage({pageNum: res.pageNum, pageSize: res.pageSize});
    })

结尾

到这里我们的React+antd+axios+router的学习就就结束了,欢迎分享给有需要的小伙伴!!!

 

阅读剩余
THE END