React + React-Redux + React-Router + Antd 搭建单页管理应用模板

目录

  • 前言
  • 一、实现效果
    • 环境
  • 二、搭建步骤
    • 1.创建项目
    • 2.运行项目
  • 三、项目改造
    • 1.添加redux-router、redux-router、axios、antd依赖
    • 2.修改后目录,及主要修改文件
  • 总结


前言

本文主要使用React全家桶一步步搭建单页管理应用,阅读本文需要React、React-Redux、React-Router 基础知识,使用react 脚手架 create-react-app 创建项目


提示:以下是本篇文章正文内容,下面案例可供参考,如有问题,欢迎指正。

一、实现效果

React + React-Redux + React-Router + Antd 搭建单页管理应用模板_第1张图片

环境

windows10 + node

node版本

二、搭建步骤

1.创建项目

在cmd窗口中执行:

# 安装 create-react-app ( react脚手架,已安装,请忽略)
npm i create-react-app -g

# 创建项目
npx create-react-app day7-react-cli 

项目目录如下:
React + React-Redux + React-Router + Antd 搭建单页管理应用模板_第2张图片
目录说明:

│  .gitignore                     # git 忽略文件
│  package.json					  # 项目说明文件,定义项目依赖、描述等信息
│  README.md					  # 项目说明文档
│  yarn.lock					  # yarn 版本锁定文件
├─ public						  # 静态资源文件夹,该文件夹下的文件不会被打包处理,直接复制
│      favicon.ico				  # 浏览器标签栏图标
│      index.html				  # 项目唯一html 入口模板文件 react root元素
│      logo192.png
│      logo512.png
│      manifest.json              # pwa 文件
│      robots.txt                 # 自定义爬虫文件
└─ src
        App.css					  # App 组件的样式
        App.js					  # App组件
        App.test.js				  # App组件测试示例文件
        index.css				  # 项目入口index.js 文件的样式
        index.js                  # 项目入口文件
        logo.svg
        reportWebVitals.js          
        setupTests.js

2.运行项目

执行命令:

# 进入项目目录
cd day7-react-cli 
# 安装依赖(我这边使用的是yarn,如果用npm: npm i 即可)
yarn
# 运行项目(npm run start)
yarn start

控制台看见如下就代表运行成功:

React + React-Redux + React-Router + Antd 搭建单页管理应用模板_第3张图片
会自动打开浏览器窗口,展示如下:
React + React-Redux + React-Router + Antd 搭建单页管理应用模板_第4张图片


三、项目改造

1.添加redux-router、redux-router、axios、antd依赖

# 状态管理库
yarn add react-redux -S
# 适用于浏览器的路由
yarn add react-router-dom -S
# ajax请求库
yarn add axios -S
# ui 组件库
yarn add antd -S

2.修改后目录,及主要修改文件

React + React-Redux + React-Router + Antd 搭建单页管理应用模板_第5张图片

│  .gitignore       
│  jsconfig.json    
│  LICENSE
│  package.json     
│  README.md        
│  yarn.lock        
├─ public
│      favicon.ico  
│      index.html   
│      logo192.png  
│      logo512.png  
│      manifest.json
│      robots.txt   
└─ src
    │  App.css
    │  App.js
    │  App.test.js
    │  index.css
    │  index.js
    │  reportWebVitals.js
    │  setupProxy.js
    │  setupTests.js
    ├─ assets							# 静态资源文件夹
    │  └─images
    │          logo.svg
    ├─ layout                           # 页面主体布局,展示左侧菜单及头部
    │  │  app-main.css
    │  │  app-main.jsx
    │  └─ components
    │      │  content.jsx				# 页面二级路由
    │      │  header.jsx				# 头部,logo 头像等
    │      │  sider.jsx					# 左侧菜单
    │      └─css
    │              content.css
    │              header.css
    │              sider.css
    ├─ redux						# 状态管理store 和各action
    │  │  redux.js
    │  ├─ action
    │  │      user-action.js
    │  └─ reducer
    │          user-reducer.js
    ├─ route
    │      main-router.jsx			# 页面一级路由
    └─ views						# 页面各组件
        ├─ home
        │      home.jsx
        ├─ login					# 登录页,我这边讲 UI 和 reducer 分开了,实际可以合并在一起
        │  │  login.jsx
        │  └─ui
        │          login.css
        │          login.jsx
        ├─ page1
        │      page1.jsx
        └─ page2
                page2.jsx

主要文件代码:
index.js:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import store from "redux/redux";

// Provider 为自组件提供store对象
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

App.js:

import "./App.css";
import MainRouter from "route/main-router";

const App = () => <MainRouter />;

export default App;

MainRouter.js:

import { Component } from "react";
// 这里引如 BrowserRouter 适合浏览器的路由实现
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Redirect,
} from "react-router-dom";

import "layout/app-main.css";
import Login from "views/login/login";
import AppMain from "layout/app-main";

export default class MainRouter extends Component {
  // 此处渲染 第一层路由
  render() {
    return (
      <Router>
        <Switch>
          <Route path="/login" component={Login}></Route>
          <Route path="/app" component={AppMain}></Route>
          <Redirect path="/" to="/app"></Redirect>
        </Switch>
      </Router>
    );
  }
}

/src/layout/app-main.jsx:

这里使用antd 布局,完成管理页面经典布局,实际MainContent 可以直接把代码拿到这边写,我这边拆开不影响

由于app-main 组件为 react-router 直接渲染的组件,所以他的props中有history对象,可以用作菜单跳转,我们这里可以传一个跳转菜单的方法到 SiderMenu 组件中用作点击左侧菜单跳转页面,这种方法有点麻烦,我这边直接用了高阶组件 withRuter,被他包裹的组件的props可以拥有history,用它完成路由跳转

import React, { Component } from "react";
import { Layout } from "antd";
import SiderMenu from "layout/components/sider";
import MainContent from "layout/components/content";
import HeaderWrapper from "layout/components/header";

const { Header, Sider, Content } = Layout;

/**
 * 页面的主要架构,考虑是否需要添加登陆判断
 */
export default class AppMain extends Component {
  render() {
    return (
      <Layout className="app_main">
        <Header>
          <HeaderWrapper></HeaderWrapper>
        </Header>
        <Layout className="app_main_content">
          <Sider>
            <SiderMenu></SiderMenu>
          </Sider>
          <Content>
            <MainContent></MainContent>
          </Content>
        </Layout>
      </Layout>
    );
  }
}

/src/components/sider.jsx:

import { Component } from "react";
import { Menu } from "antd";
import { AppstoreOutlined, MailOutlined } from "@ant-design/icons";
import { withRouter } from "react-router-dom";
import "./css/sider.css";
const { SubMenu } = Menu;

const menus = {
  home: [],
  menu1: ["page1", "page2"],
  menu2: ["page3", "page4", "page5", "page6", "page8"],
  menu3: ["page9", "page10", "page11", "page12"],
};

class SiderMenu extends Component {
  state = {
    openKeys: [],
    activeKeys: [],
  };

  componentDidMount() {
    let { pathname } = this.props.location;
    const key = pathname.split("/")[2] || "home";
    this.setOpenKeys(key);
    this.setState({
      activeKeys: [key],
    });
  }

  setOpenKeys = (key) => {
    for (let tmp in menus) {
      let menu = menus[tmp];
      if (menu.includes(key)) {
        this.setState({
          openKeys: [tmp],
        });
        break;
      } else {
        this.setState({
          openKeys: [],
        });
      }
    }
  };

  handleOpenChange = (openKeys) => {
    if (openKeys.length === 0) {
      this.setState({
        openKeys: [],
      });
    } else {
      this.setState({
        openKeys: [openKeys[openKeys.length - 1]],
      });
    }
  };

  handleMenuClick = ({ key }) => {
    this.setOpenKeys(key);
    this.setState({
      activeKeys: [key],
    });
    // 被包裹组件,因为他不是Router 直接渲染的组件,如果不包裹它没有history
    this.props.history.push(`/app/${key}`);
  };

  render() {
    return (
      <Menu
        theme="dark"
        mode="inline"
        openKeys={this.state.openKeys}
        onOpenChange={this.handleOpenChange}
        onClick={this.handleMenuClick}
        selectedKeys={this.state.activeKeys}
      >
        <Menu.Item key="home">首页</Menu.Item>
        <SubMenu key="menu1" icon={<MailOutlined />} title="测试1">
          <Menu.Item key="page1">菜单 1</Menu.Item>
          <Menu.Item key="page2">菜单 2</Menu.Item>
        </SubMenu>
        <SubMenu key="menu2" icon={<AppstoreOutlined />} title="测试2">
          <Menu.Item key="page5">菜单 5</Menu.Item>
          <Menu.Item key="page6">菜单 6</Menu.Item>
          <Menu.Item key="page7">菜单 7</Menu.Item>
          <Menu.Item key="page8">菜单 8</Menu.Item>
        </SubMenu>
        <SubMenu key="menu3" icon={<AppstoreOutlined />} title="测试3">
          <Menu.Item key="page9">菜单 page9</Menu.Item>
          <Menu.Item key="page10">菜单 page10</Menu.Item>
          <Menu.Item key="page11">菜单 page11</Menu.Item>
          <Menu.Item key="page12">菜单 page12</Menu.Item>
        </SubMenu>
      </Menu>
    );
  }
}
// 这边返回包裹后的组件
export default withRouter(SiderMenu);

注意下,在项目根目录里面添加了一个setupProxy.js 用作配置代理服务器地址规则,前后端分离的项目,如果后端不允许跨域,需要我们自己用代理来完成ajax请求
setupProxy.js:

// 如下配置会将请求本地地址3000/student/* 代理到5000
const createProxyMiddleware = require("http-proxy-middleware");

module.exports = function (app) {
  app.use(
    "/student",
    createProxyMiddleware({
      target: "http://localhost:5000",
      changeOrigin: true,
      pathRewrite: {
        "^/student": "",
      },
    })
  );
// 如下配置会将请求本地地址3000/person/* 代理到6000
  app.use(
    "/person",
    createProxyMiddleware({
      target: "http://localhost:6000",
      changeOrigin: true,
      pathRewrite: {
        "^/person": "",
      },
    })
  );
};

请求的地方:

// 这边可以直接填本地的地址,
axios.get("/student/login").then((res) => {
  this.props.saveUserInfo({
     ...res.data,
   });
   this.props.history.push("/app/home");
 });

总结

项目地址

你可能感兴趣的:(react全家桶,antd,react)