React + Antd 实现后台管理系统

·  阅读 2596

React 介绍

  • Facebook 开源的一个 JavaScript 库
  • React 结合生态库构成一个 MV* 框架
  • React 特点:
    • 声明式编码 (只需要声明在哪里做什么,而无需关心如何实现,比如数组原型上的那些高阶函数。编程式实现:需要以具体代码表达在哪里做什么,如何实现,比如 for 循环之类的。)
    • Component-Based (组件化编码)
    • 高效 (高效的 DOM Diff 算法,最小化页面重绘)
    • 单向数据流
  • 生态介绍
    • React-Router (Vue-Router)
    • Redux (Vuex)
    • Axios
    • Babel
    • Webpack

React 生命周期

  • getDefaultProps
  • getInitialState
  • componentWillMount
  • render (必须)
  • componentDidMount
  • componentWillReceiveProps
  • shouldComponentUpdate (<--- this.setState())
  • componentWillUpdate (<--- this.forceUpdate())
  • componentDidUpdate
  • componentWillUnmount
import React, { Component } from 'react';

class Child extends Component {
	// 以下生命周期按顺序执行
	// 首次会执行 componentWillMount、render、componentDidMount
	// mount 钩子只会在组件中调用一次,其他的调用多次

	componentWillReceiveProps(newProps) {
		console.log('will use newProps', newProps);
	}

	shouldComponentUpdate(newProps) {
		// 接受新的 props,如果返回 false, 就不会再调用 render 生命周期了

		console.log('should update', newProps);
		return newProps.value === 3 ? false : true;
	}

	componentWillUpdate(newProps) {
		console.log('will update', newProps);
	}

	componentWillMount() {
		console.log('will mount');
	}

	render() {
		console.log('render');
		return (
			<div>Child</div>
		);
	}

	componentDidMount() {
		console.log('did mount');
	}

	componentDidUpdate(oldProps) {
		// 接收老的 props
		// 比如父组件设置了 state 里面的值,这里的 oldProps 里面的值就是 state 之前的值

		console.log('did update', oldProps);
	}
}

export default Child;
复制代码

项目依赖安装

  • axios
  • antd (使用的 less,可以修改属于自己的主题,之所以我们可以引入 antd.css,是因为 less 文件已经被编译打包了,但是 antd 其实是采用 less 的方式来编写样式的!)
  • react-router-dom
  • redux
  • less (安装less)
  • less-loader (需要暴露出 webpack 配置: yarn eject)
  • babel-plugin-import (按需加载组件代码和样式,比如我们引入了一个 Button 组件,那么相应的也只会引入 Button 的样式文件)

配置 less

// 暴露出 webpack 配置,编辑 webpack.config.js
// 编辑完之后重启服务即可
// 记住:use 采用从后到前来进行解析,loader 不要写反了

{
  test: /\.less$/,
  use: [
    require.resolve('style-loader'),
    {
      loader: require.resolve('css-loader'),
      options: {
        importLoaders: 1
      }
    }, {
      loader: require.resolve('postcss-loader'),
      options: {
        ident: 'postcss',
        plugins: () => [
          require.resolve('postcss-flexbugs-fixes')
        ]
      }
    }, {
      loader: require.resolve('less-loader')
    }
  ]
},
复制代码

配置按需加载 (安装 babel-plugin-import)

// 打开 webpack.config.js 文件

{
  test: /\.(js|mjs|jsx|ts|tsx)$/,
  include: paths.appSrc,
  loader: require.resolve('babel-loader'),
  options: {
    customize: require.resolve(
      'babel-preset-react-app/webpack-overrides'
    ),
    // 添加如下配置即可
    // 项目打包就只会打包你引入了的组件和样式
    plugins: [
      ['import', {
        libraryName: "antd",
        // 如果指定是 "css", 那么它就会引入 "antd/lib/button/style/css"
        // 不过,你修改按钮的主题色的时候,就无法修改了,因为我们修改主题是通过改变 less 变量
        // 而 css 文件是已经编译打包好了的
        // style: "css"
        
        // 指定为 true,它就会加载 "antd/lib/button/style"下的 less 模块
        // 并将所有 less 模块进行动态编译成 css 文件
        style: true, 
      }]
  },
},
复制代码

修改 Antd 的主题色

{
  loader: require.resolve('less-loader'),
  options: {
    modules: false,
    modifyVars: {
      // 修改 antd 的主题色
      "@primary-color": "#f9c700"
    }
  }
}
复制代码

项目路由配置

  • 项目的首页与登录页是同一层级的路由,所以配置的时候需要提前想好
  • 定义一个 router.js 文件,放置第一层级路由
  • 通过 this.props.children 获取组件内部的路由配置
// router.js

<Router>
    <Route path='/login' component={Login} />
    <Route path='/admin' render={(props) =>
        <Admin>
            <Route path={`${props.match.path}/home`} component={Home} />
            <Route path={`${props.match.path}/ui/buttons`} component={Buttons} />
        </Admin>
    } />
</Router>

// Admin.js (用来对Home页面的一个布局)

class Admin extends Component {
    render() {
        return (
            <Row className="container">
                <Col span={4} className="nav-left">
	                <NavLeft />
                </Col>
                <Col span={20} className="main">
                    <Header />
                    <Row className="content">
                        // 渲染 Admin 组件内部的 Route
                    	{this.props.children}
                    </Row>
                    <Footer />
                </Col>
            </Row>
        );
    }
}
复制代码

Mock 数据以及接口请求封装

  • easy mock (编写在线的接口)
  • mock (配置需要接口返回的数据)
  • axios (请求接口)
// 封装接口请求
import axios from 'axios';
import { Modal } from 'antd';

const baseURL = 'https://....';

class Axios {
    static ajax(options) {
        return new Promise((resolve. reject) => {
            axios({
                method: 'get',
                url: options.url,
                baseURL: baseURL,
                timeout: 5000,
                params: (options.data && options.data.params) || ''
            }).then((response) => {
                if (response.status === '200') {
                    const res = response.data;
                    if (res.code === 0) {
                        resolve(res);
                    } else {
                        // 接口数据报错
                        Modal.info({
                            title: '提示',
                            content: res.data.msg
                        });
                    }
                } else {
                    // 接口报错
                    reject(response.data);
                }
            })
        })
    }
}
复制代码

封装axios请求以及配置代理

使用 create-react-app 创建的项目可以直接在 package.json中配置 proxy

"proxy": "http://xx.x.x.xx:port", // 服务器地址
复制代码

定制 axios

import axios from "axios";
import { Modal } from "antd";
const service = axios.create({
    timeout: 10000
})

// 请求拦截
service.interceptors.request.use(config => {
    console.log("config", config);
    config.headers["Authorization"] = "token"; // 一般接口请求的话需要带上token
    return config;
}, error => {
    console.log("请求拦截error", error);
    Promise.reject(error);
})

service.interceptors.response.use(response => {
    let { data, statusText, status } = response;
    return {
    	statusText,
    	status,
    	data: JSON.parse(data) // 如果后端返回的数据是json序列化之后的数据需要解析
    }
}, error => {
    Modal.warning({
    	title: error.response.status,
    	content: error.response.statusText
    })
    Promise.reject(error);
})

export default service;

复制代码

如何请求 (可以封装请求方法,比如 get 请求)

import service from '../utils/request';
import { Modal } from 'antd';

export function ApiGet(url, params) {
    return new Promise((resolve, reject) => {
    	service.get(url, params)
    	    .then(res => {
            	if (res.status === 200) {
                    Modal.success({
                        title: res.status,
                        content: "请求成功!"
                    });
                    resolve(res);
            	} else {
                    Modal.warning({
                    	title: res.status,
                    	content: res.statusText
                    });
                    reject(res);
            	}
            })
            .catch(err => {
                console.log("err", err)
                Modal.warning({
                    title: "Error",
                    content: "请求错误!"
                })
                reject(err);
            });
    });
}
复制代码

调用就很简单了

ApiGet("接口").then(res => {
    console.log(res);
}, err => {

})
复制代码
分类:
前端
标签:
分类:
前端
标签: