一步一步完成一个前后端项目Spring+React(一)

项目简介

这就是一个最简单的前后端分离的项目,由于项目比较小,所以说数据库设计的不是特别的规范,后端的代码也不是很完善可能会有一些bug什么的,这个项目主要讲述的就是React的各种用法吧
后端git地址:https://gitee.com/csmeng/nguc.git
前端git地址:https://gitee.com/csmeng/reactadmin.git

项目准备

我假设大家已经安装过Node和cnpm等前端常用的工具,并且对后端简单的SpringBoot项目开发也有一些了解,由于主要是讲述项目,这些基础的东西就不再一一赘述。

  1. 后端项目所需要的依赖 ,最基本的的SpringBoot的包还有mybatis,mybatis的分页插件,oss存储等,
	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!-- 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.0.4</version>
        </dependency>

        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <!-- 分页 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.10</version>
        </dependency>

        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.0.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.28</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.10.2</version>
        </dependency>
    </dependencies>
  1. 前端项目所需要的依赖:首先用脚手架创建一个前端的项目
npm install -g create-react-app : 全局下载工具 
create-react-app react-admin :下载模板项目

这样就是一个空的前端项目,
package.json里面的内容,更改之后需要npm install或者cnpm install一下,引入依赖

"dependencies": {
    "ali-oss": "^6.10.0",
    "antd": "^3.18.1",
    "axios": "^0.19.2",
    "babel-plugin-import": "^1.13.0",
    "customize-cra": "^0.2.12",
    "draft-js": "^0.10.5",
    "draftjs-to-html": "^0.9.1",
    "html-to-draftjs": "^1.5.0",
    "jsonp": "^0.2.1",
    "less": "^3.12.2",
    "less-loader": "^5.0.0",
    "moment": "^2.27.0",
    "react": "^16.2.0",
    "react-app-rewired": "^2.1.3",
    "react-dom": "^16.2.0",
    "react-draft-wysiwyg": "^1.14.5",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.0.1",
    "store": "^2.0.12"
  },
  1. antd的按需引入问题 :这是一个比较令人纠结的问题,你可以选择不使用按需加载,但是我们下载antd的时候是全部下载的,antd是蚂蚁金服的开源UI框架,确实是比较好用的,使用按需加载之后我们每次打包只打包自己用到的组件即可,其他的没有使用到的样式不会被引入,如果不使用的话则需要引入所有的组件样式,运行效率会降低很多,我们之前环境里面已经有了antd和react-app-rewiredcustomize-crababel-plugin-import这些依赖,接下来进行一些配置就可以了,
const {
   override, fixBabelImports,addLessLoader} = require('customize-cra');

module.exports = override(
    // 针对antd 实现按需打包,根据import来打包
    fixBabelImports('import', {
   
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: true,
    }),
    //使用less-loader对源码中的样式进行重新定义
    addLessLoader({
   
        javascriptEnabled: true,
        modifyVars: {
   '@primary-color': '#1890ff'},
    }),
);

上面我们不仅声明了antd组件的按需加载,并且也把antd默认的ui颜色变成了我们喜欢的,
到现在为止我们的项目的大致环境已经算是搭建完成了

前端项目整体框架

项目结构
页面大体框架
这些大体的框架的搭建需要路由的使用,前面已经引入过路由的依赖,react-router-dom 接下来使用路由构建出我们整体的页面。
在这里插入图片描述
大体就是这样,我们先构建比较上层的登录和主页,其实登录和主界面属于同一级的路由。
在App.js里面配置如下

import React, {
   Component} from 'react'
import {
   BrowserRouter, Switch, Route} from 'react-router-dom'
import Admin from "./pages/admin/admin";
import WrapLogin from "./pages/login/login";

export default class App extends Component {
   

    render() {
   
        return (
            <BrowserRouter>
                <Switch>
                    <Route path='/login' component={
   WrapLogin}/>
                    <Route path='/' component={
   Admin}/>
                </Switch>
            </BrowserRouter>
        )
    }
}

其实路由的用法很简单,在外面想要显示的路由的页面来一个BrowserRouter或者HashRouter,然后使用Switch包括住我们想要的界面,Route里面进行各种各样的跳转,path对应的是相应的路径,component对应的是相应的组件,当界面的path路径对应为和某一个Route匹配的时候,页面上就会显示相应的component里面的内容,
当然还有一些比较高级的用法

<Switch>
    <Route path='/home' component={
   Home}/>
    <Route path='/category' component={
   Category}/>
    <Route path='/product' component={
   Product}/>
    <Route path='/role' component={
   Role}/>
    <Route path='/user' component={
   User}/>
    <Route path='/charts/bar' component={
   Bar}/>
    <Route path='/charts/line' component={
   Line}/>
    <Route path='/charts/pie' component={
   Pie}/>
    <Redirect to='/home'/>
</Switch>

exact代表着精确匹配,有些场景下需要使用到,就好比上述,如果我的路由路径为 /charts/line ,但是其实它是可以匹配两个的 /charts也会被匹配到,这样就会报错,使用exact就可以避免此类问题,当路由路径为 /charts/line 时只能匹配精确的路径,那些模糊也可以的就代表不行,

Redirect则表示当所有的路由都不匹配的时候,重定向到某一个路由,一般都是重定向到首页或者登录页。

登录页面的生成

首先我们来写一下登录页面
首先在App.js文件中已经声明Login这个登录的页面也是属于路由界面的其中之一,所以它也可以使用路由界面的诸多属性,比如路由跳转重定向什么的,
登录页面
这就是很普通的登录界面,除了样式之外,这还有很多东西需要我们做的,

首先我们先来分析一下登录界面的作用:

1.页面的显示,(使用antd的组件,还有图标什么的)
2.发送请求之前进行验证输入合法性,(还是使用antd的表单进行验证,自定义一些校验的规则,错误的话进行一些提示)
3.发送Ajax请求到后端后端进行校验,
4.后端返回结果的处理,登录成功之后需要重定向到指定界面
5.登录成功之后需要将当前用户的信息保存到我们的缓存当中
6.当打开这个页面的时候,我们需要判断我们缓存中有没有用户对象,如果有的话,就要跳转到主页面
解决方法:
我们定义了两个工具类,一个用来存储我们的用户对象,一个用来操作我们的用户对象,
memoryUtils.js

/**
 * 用来在内存中保存一些数据的工具模块
 */

export default {
   
    user: {
   }, //保存当前登录的user
    role: {
   }, //当前登录的用户所拥有的角色
}

storageUtils.js

/**
 * 进行local数据存储管理的工具模块
 */
import store from 'store'

const USER_KEY = 'user_key';
const ROLE_KEY = 'role_key';
export default {
   
    /*
        保存user,
     */
    saveUser(user) {
   
        //localStorage.setItem(USER_KEY,JSON.stringify(user));
        store.set(USER_KEY, user);
    },
    /*
        读取user
     */
    getUser() {
   
        //return JSON.parse(localStorage.getItem(USER_KEY) || '{}');
        return store.get(USER_KEY) || {
   }
    },
    /*
        删除user
     */
    delUser() {
   
        //localStorage.removeItem(USER_KEY);
        store.remove(USER_KEY);
    },
    /*
        保存role,
     */
    saveRole(role) {
   
        //localStorage.setItem(USER_KEY,JSON.stringify(user));
        store.set(ROLE_KEY, role);
    },
    /*
        读取role
     */
    getRole() {
   
        //return JSON.parse(localStorage.getItem(USER_KEY) || '{}');
        return store.get(ROLE_KEY) || {
   }
    },
    /*
        删除role
     */
    delRole() {
   
        //localStorage.removeItem(USER_KEY);
        store.remove(ROLE_KEY);
    },
}

还有我们也建立了一个api的包,用来定义各种请求向后端发送数据,我们对axios进行一系列的封装,

ajax.js,这个文件的主要作用就是将axios进行封装,将get和post请求进行统一的管理

/**
 * 发送异步ajax请求的函数模块
 * 封装axios库
 * 函数的返回值是promise对象
 * 优化:统一处理请求异常
 *  在外层包一个自己创建的promise对象
 *  在请求出错时,不去reject(error),而是显示错误信息,并返回
 * 优化2:异步得到的不是response,而是response.data
 *  在请求成功resolve时:直接resolve(response.data)
 */
import axios from 'axios'
import {
   message} from 'antd'

export default function ajax(url, data = {
   }, type = 'GET') {
   
    return new Promise((resolve,reject) => {
   
        let promise;
        //1,执行异步ajax请求
        if (type === 'GET') {
   //发送get请求
            promise = axios.get(url,{
   
                params: data
            })
        } else {
   //发送POST请求
            promise = axios.post(url,data);
        }
        promise.then(response => {
   
            //2.如果成功,调用resolve(value)
            resolve(response.data);
        }).catch(error => {
   
            //3.如果失败了,不调用reject(reason),而是提示异常信息
            message.error('请求出错:',error.message);
            resolve(error.message);
        })
    });
}

接下来一个文件我们计划用来定义所有的ajax请求,然后将他们暴露出来,这样哪个地方需要用到都可以使用
由于这是一个比较小的项目,所以对于一些加密什么的也不是特别的在意,没有使用过常用的那些加密技巧,直接就是最简单的使用用户名和密码进行发送请求进行登录
index.js

export const reqLogin = (username, password) => ajax(BASE + '/portal/login', {
   username, password}, 'POST');

当然仅仅这些还不是特别的足够,我们现在的很多请求是无法访问到后端的端口的,因为跨域的问题,所以,需要在package.json里面加入这样的一句话,

"proxy": "http://localhost:8080"

后端方面处理请求:

这里我为了方便自己定义了一些小小的处理思想,
所有的controller都继承一个BaseController,在BaseController里面有一个统一的处理数据将数据转换为Json数据的方法,可能有些臃肿,不过我只是为了省事,

import com.nguc.ngucpractice.common.entity.Result;
import org.springframework.web.servlet.ModelAndView;

public class BaseController {
   
    protected ModelAndView feedback() {
   
        return feedback(null);
    }

    protected ModelAndView feedback(Result obj) {
   
        Object result = obj != null ? obj : "success";
        return new ModelAndView(new JsonView(result));
    }
}

还有我将所有的service处理的结果都定义为一个对象Result,这个对象具体有两部分status和data组成,具体的构造方法代码就省略了。

public class Result {
   

    private String status;
    private Object data;
}

后端具体的逻辑
controller

@Controller
@RequestMapping("/portal")
public class LoginController extends BaseController {
   

    @Resource
    private LoginService loginService;

    @RequestMapping("/login")
    public ModelAndView doLogin(@Request
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值