前后端分离实践:使用 React 和 Express 搭建完整登录注册流程

文章目录

  • 概要
  • 整体架构流程
  • 技术名词解释
    • React
    • Express
    • React Router
    • Ant Design
  • 技术细节
    • 前端设计
    • 后端逻辑
    • 数据交互
  • 小结

概要

本项目是一个基于React和Express的简单登录注册系统。通过前后端分离的方式,实现了用户的注册、登录和查看用户列表等功能。前端使用React框架构建了用户界面,后端使用Express框架处理用户请求。借助`Ant Design组件库,我们不仅实现了基本的用户认证功能,还为用户提供了友好的界面体验。
在这里插入图片描述

整体架构流程

  1. 用户在Ant Design组件库提供的UI组件组成的前端界面输入邮箱和密码进行登录或注册操作。
  2. 前端通过React Router实现页面导航,通过axios库将用户输入的数据发送HTTP请求至后端。
  3. 后端Express服务器接收到请求后,根据路由管理不同请求,通过Controller层处理业务逻辑,并与数据库进行数据交互。
  4. 对于登录请求,后端查询数据库验证用户信息,返回登录成功或失败的响应。
  5. 对于注册请求,后端将新用户信息插入数据库,并返回注册成功或失败的响应。
  6. 对于查看用户列表的请求,后端从数据库中获取所有用户信息,并将其返回给前端。
  7. 前端接收到后端返回的数据后,根据需要更新界面展示用户列表或处理其他业务。

技术名词解释

React

React是一个用于构建用户界面的JavaScript库,它提供了组件化开发的思想和一系列工具,使得构建复杂用户界面更加简单和可维护。

Express

Express是一个基于Node.js的Web应用开发框架,它提供了一系列的功能和工具,使得构建高性能、可扩展的Web应用变得更加容易。

React Router

React Router是React的官方路由库,用于管理应用的路由和页面导航,使得构建单页面应用更加简单和灵活。

Ant Design

一套基于React的企业级UI组件库,提供了丰富的UI组件和设计模式,帮助开发者快速构建美观的用户界面。

技术细节

前端设计

使用React框架构建了登录注册页面,并采用Ant Design组件库提供的Input、Button等组件,使得界面清晰易用。

首先构建一个基础的react APP名字为react-study

npx create-react-app react-study

src目录下新建一个components文件夹,里面存放各个组件,例如登录注册,用户列表等
在这里插入图片描述
导入整体布局layout.js,如下图所示:
在这里插入图片描述
在这里插入图片描述
登录Login.js

import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { Form, Input, Button,message } from 'antd';
import axios from 'axios';


const Login = () => {
  const navigate = useNavigate();

  const onFinish = (n) => {
    console.log(n.email);
    console.log(n.password);
    axios({
      method: 'post',
      url: 'http://localhost:2531/login',
      data: {
        email: n.email,
        password: n.password
      }
    }).then((res) => {
      if (res.data.success) {
        message.success('登陆成功');
        // console.log(`Object ${res.data}`);
        console.log(res.data);
        navigate('/users')
      } else {
        message.success('用户名或密码不正确');
        console.log(res.data);
      }
    }).catch(() => {
      console.log("Something went wrong. Plase try again later");
    });
  };

  return (
    <div className="login1">
    <Form
      name="normal_login"
      labelCol={{
        span: 8,
      }}
      wrapperCol={{
        span: 16,
      }}
      style={{
        maxWidth: 600,
      }}
      className="login-form"
      initialValues={{ remember: true }}
      onFinish={onFinish}
    >
      <Form.Item
        name="email"
        label="邮箱"
        rules={[{ required: true, message: '请输入邮箱!' }]}
      >
        <Input placeholder="邮箱" 
        />
      </Form.Item>

      <Form.Item
        name="password"
        label="密码"
        rules={[{ required: true, message: '请输入密码!' }]}
      >
        <Input.Password
          placeholder="密码"
        />
      </Form.Item>

      <Form.Item
       wrapperCol={{
        offset: 8,
        span: 16,
       }}
      >
        <Button type="primary" htmlType="submit" className="login-form-button">
          登录
        </Button>
        <span className="zhuce">
            <Link to="/register">没有账号?去注册吧!</Link>
        </span>
      </Form.Item>
    </Form>
    </div>
  );
};

export default Login;

注册Register.js

import React from 'react';
import "./Login.css"
import { useNavigate } from 'react-router-dom';
import { Form, Checkbox, Input, Button, message } from 'antd';
// import { LockOutlined } from '@ant-design/icons';
import axios from 'axios';

const tailFormItemLayout = {
  wrapperCol: {
    xs: {
      span: 24,
      offset: 0,
    },
    sm: {
      span: 16,
      offset: 8,
    },
  },
};

const Register = () => {
  // const [email, setEmail] = useState('');
  // const [password, setPassword] = useState('');
  const navigate = useNavigate();

  const onFinish = (n) => {
    axios({
      method: 'post',
      url: 'http://127.0.0.1:2531/register',
      data: {
        email: n.email,
        password: n.password
      }
    }).then((res) => {
      if (res.data.success) {
        message.success('注册成功');
        console.log(res.data);
        navigate('/login')
      } else {
        message.success('注册失败');
        console.log(res.data);
      }
    }).catch(() => {
      console.log("Something went wrong. Plase try again later");
    });
  };

  return (
    <div className="login1">
    <Form
      name="normal_register"
      labelCol={{
        span: 8,
      }}
      wrapperCol={{
        span: 16,
      }}
      style={{
        maxWidth: 600,
      }}
      className="register-form"
      initialValues={{ remember: true }}
      onFinish={onFinish}
    >

      {/* 邮箱 */}
      <Form.Item
        name="email"
        label="邮箱"
        rules={[{ required: true, message: '请输入邮箱!' }]}
      >
        <Input 
        placeholder="邮箱"
        // value={email}
        // onChange={(e) => setEmail(e.target.value)}
        />
      </Form.Item>


      {/* 密码 */}
      <Form.Item
        name="password"
        label="密码"
        rules={[{ required: true, message: '请输入密码!' }]}
        hasFeedback
      >
        <Input.Password
          placeholder="密码"
          // value={password} 
          // onChange={(e) => setPassword(e.target.value)}
        />
      </Form.Item>

      {/* 重新输入密码 */}
      <Form.Item
          name="confirm1"
          label="验证密码"
          dependencies={['password']}
          hasFeedback
          rules={[
            {
              required: true,
              message: '请再一次填入密码!',
            },
            ({ getFieldValue }) => ({
              validator(_, value) {
                if (!value || getFieldValue('password') === value) {
                  return Promise.resolve();
                }
                return Promise.reject(new Error('两次密码不符!'));
              },
            }),
          ]}
        >
          <Input.Password 
          placeholder="重新输入密码"
          />
        </Form.Item>


        {/* 接受同意 */}
        <Form.Item
          name="agreement"
          valuePropName="checked"
          rules={[
            {
              validator: (_, value) =>
                value ? Promise.resolve() : Promise.reject(new Error('Should accept agreement')),
            },
          ]}
          {...tailFormItemLayout}
        >
          <Checkbox>
            I have read the <span>agreement</span>
          </Checkbox>
        </Form.Item>

      {/* 注册按钮 */}
      <Form.Item {...tailFormItemLayout}>
        <Button type="primary" htmlType="submit" className="register-form-button">
          注册
        </Button>
      </Form.Item>
    </Form>
    </div>
  );
};

export default Register;

用户列表UserList.js

import React, { useState, useEffect } from "react";
import axios from 'axios';

const UserList = () => {
    const [users, setUsers] = useState([]);
    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await axios.get('http://localhost:2531/users');
                setUsers(response.data);
            } catch (error) {
                console.error(error);
            }
        };
        fetchData();
    }, []);

    return (
        <div>
            <h1>用户列表</h1>
            <ul>
                {users.map(user => (
                    <li key={user.id}>{user.email}</li>
                ))}
            </ul>
        </div>
    )
}

export default UserList;

然后里面的组件都放在routes.js里面,使用 react-router-dom 实现路由功能,然后在 App.js 中引入 routes.js
在这里插入图片描述
在这里插入图片描述

后端逻辑

后端通过Express框架处理了登录、注册和用户列表展示等请求,并利用Controller层来处理业务逻辑,从数据库中读取和存储用户信息。

在 react-study目录下创建server目录,在这个文件夹下创建db.js封装连接数据库的操作
在这里插入图片描述
在控制器controller.js里封装数据库的操作,查询user表的所有信息,插入数据,根据邮箱和密码查询数据
在这里插入图片描述
用路由POST挂载提交登录和注册的操作,GET得到所有列表
在这里插入图片描述
app.js里挂载路由的方法,用跨域访问不同的端口,npm run server启动服务

在这里插入图片描述

数据交互

前端通过axios库发送HTTP请求与后端通信,后端接收请求后进行相应的业务处理,并将处理结果返回给前端。

在react前端提交表单的时候,onFinish函数可以用axios库去提交login的请求,注册和登录的方法是相同的
在这里插入图片描述
获取用户列表也可以用axios库去得到数据库里的数据

在这里插入图片描述

小结

通过本项目的实践,我们深入了解了React、Express和Ant Design这些流行的前端和后端技术。通过将它们结合起来,我们成功构建了一个简单而完整的登录注册系统,为今后开发更复杂的应用奠定了基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/593351.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HCIP第二节

OSPF&#xff1a;开放式最短路径协议&#xff08;属于IGP-内部网关路由协议&#xff09; 优点&#xff1a;相比与静态可以实时收敛 更新方式&#xff1a;触发更新&#xff1a;224.0.0.5/6 周期更新&#xff1a;30min 在华为设备欸中&#xff0c;默认ospf优先级是10&#…

安居水站:《是谁毁掉了下一代?》

在时光的长河中&#xff0c;我们总能听到这样的声音。四十年前&#xff0c;人们惊恐地呼喊&#xff0c;武侠小说会毁掉下一代&#xff1b;三十年前&#xff0c;流行音乐被视为罪魁祸首&#xff1b;二十年前&#xff0c;电视节目背负起这沉重的指责&#xff1b;十年前&#xff0…

WWW‘24 | 课程学习CL+模仿学习IL用于ETF及商品期货交易

WWW24 | 课程学习CL模仿学习IL用于ETF及商品期货交易 原创 QuantML QuantML 2024-05-04 13:47 论文地址&#xff1a;[2311.13326] Curriculum Learning and Imitation Learning for Model-free Control on Financial Time-series (arxiv.org) 本文探讨了在金融时间序列数据上…

大聪明原理

原创 | 刘教链 不知不觉之间&#xff0c;BTC&#xff08;比特币&#xff09;快速完成了一个V形反转&#xff1a;从5月2日低开56.8k连涨3天&#xff0c;重回64k一线&#xff0c;已超过4月30日开盘价63k。反转的原因&#xff0c;在5.3教链内参《美就业数据爆冷门&#xff0c;BTC急…

Claude聊天机器人推出全新iOS客户端及团队专属计划

Anthropic 正在使其 Claude AI 更易于在移动设备上访问。该公司发布了适用于 iOS 的 Claude 移动应用程序,任何用户都可以免费下载。与聊天机器人的移动网络版本类似,该应用程序跨设备同步用户与 Claude 的对话,允许他们从计算机跳转到应用程序(反之亦然),而不会丢失聊天…

【信息收集-基于字典爆破敏感目录--御剑/dirsearch

两个工具都是内置字典来对于目录进行爆破的&#xff0c;这是信息收集的一部分&#xff0c;若能在列举出的目录中找到有价值的信息能为后续渗透做准备。 御剑比较简便 dirsearch需要集成python3.x环境&#xff0c;但是可选的命令更多。两者爆破的结果不一定相同&#xff0c;可以…

Linux课程机房虚拟机

Linux课程机房虚拟机 机房虚拟机&#xff08;默认不能联网的&#xff09;&#xff1a; 百度网盘&#xff1a;https://pan.baidu.com/s/1WqSvqB3Y7b_D4690CDBlJA?pwdaugc 123网盘&#xff1a;https://www.123pan.com/s/tQ0UVv-LiolA.html提取码:F4xm ‍ 联网使用说明&…

CC工具箱1.2.8更新_免费_90+工具

​CC工具箱1.2.8更新【2024.5.5】 使用环境要求&#xff1a;ArcGIS Pro 3.0 一、下载链接 工具安装文件及使用文档&#xff1a; https://pan.baidu.com/s/1OJmO6IPtMfX_vob3bMtvEg?pwduh5r 二、使用方法 1、在下载链接中下载安装文件【CC工具箱1.2.8.esriAddinX】&#xf…

回归测试的几种方法

回归测试&#xff0c;是对修复Bug后的软件进行验证&#xff0c;确保所有缺陷得到修复&#xff0c;并且没有引入新的Bug。 如果确保缺陷得到修复&#xff0c;那么只需要执行发现缺陷的测试用例&#xff0c;但这样不能排除引入新的Bug&#xff1b;而如果把所有测试用例都执行一遍…

fatal: fetch-pack: invalid index-pack output

解决方案&#xff1a;git clone --depth1 要克隆的git地址 下载最近一次提交的代码 其他分支的内容都不下载 这样整体下载体量就变小了 执行命令&#xff1a;git clone --depth 1 https://gitlab.scm321.com/ufx/xxxx.git

交叉导轨维护和保养的方法!

交叉导轨系统作为一种常见的机械传动装置&#xff0c;广泛应用于各种精密机械设备中。为了确保交叉导轨系统的正常运行和延长其使用寿命&#xff0c;定期维护和保养是至关重要的。 1、清洁&#xff1a;定期清理交叉导轨表面的灰尘、油污等杂质&#xff0c;保持其清洁。在清理过…

【LinuxC语言】setitimer与getitimer函数

文章目录 前言一、setitimer() 函数二、getitimer() 函数三、示例代码总结 前言 在Linux系统下&#xff0c;编写程序时经常需要使用定时器来实现一些定时任务、超时处理等功能。setitimer() 和 getitimer() 函数是两个用于操作定时器的重要函数。它们可以帮助我们设置定时器的…

5月1日江苏某厂冷却塔清洗工作汇报-智渍洁

日期&#xff1a;5月1日 施工人员&#xff1a;张能超&#xff0c;张伟&#xff0c;刘平&#xff0c;曾巧 地点&#xff1a;江苏**** 事项&#xff1a;空冷器清洗 今日工作&#xff1a;设备安装完成&#xff0c;泡了三台 5月1日江苏某厂冷却塔清洗工作汇报 - 重庆智渍洁环保科技…

二进制,八进制,十六进制转十进制 c++

紧接着十进制转二进制&#xff0c;八进制&#xff0c;十六进制-CSDN博客这篇文章 输入一个二进制&#xff0c;八进制的数&#xff0c;怎样能转化为十进制呢&#xff1f; 原理如下&#xff1a; K进制转十进制 按权相加法展开成一个多项式&#xff0c;每项是该位的数码与相应…

【kettle006】kettle访问华为openGauss高斯数据库并处理数据至execl文件(已更新)

1.一直以来想写下基于kettle的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下华为openGauss高斯数据库相关知识体系 3.欢迎批评指正&#xff0c;跪谢…

2024年汉字小达人活动还有5个月开赛:来做18道历年选择题备考吧

距离2024年第11届汉字小达人比赛还有四个多月的时间&#xff0c;如何利用这段时间有条不紊地备考呢&#xff1f;我的建议是两手准备&#xff1a;①把小学1-5年级的语文课本上的知识点熟悉&#xff0c;重点是字、词、成语、古诗。②把历年真题刷刷熟&#xff0c;这对了解汉字小达…

任何人都可做的兼职副业,一单29.9元,利润70%的怀旧游戏项目

偶然发现一个博主正在直播玩一款经典的俄罗斯方块游戏机。这款游戏机年代久远&#xff0c;勾起了我对学生时代的无尽回忆。 那时&#xff0c;看到同学们玩这款游戏&#xff0c;我总是心生羡慕。为了能拥有一台自己的游戏机&#xff0c;我曾节衣缩食&#xff0c;悄悄攒钱购买。…

C++类定义时成员变量初始化

在C11中允许在类定义时对成员变量初始化。 class A { public:A() { }void show(){cout << "m_a " << m_a << endl;cout << "m_b " << m_b << endl;} private:int m_a 10;//类定义时初始化int m_b; //没有初始化…

CP,FT,WAT有什么区别?

‍ 知 识星球&#xff08;星球名&#xff1a; 芯片制造与封测社区&#xff0c;星球号&#xff1a; 63559049&#xff09;里的学员问&#xff1a; CP,FT,WAT都是与 芯片的测试有关&#xff0c;他们有什么区别呢&#xff1f; 如何区‍分&#xff1f; ‍ ‍ CP,FT,WAT分别…

MySQL LRU算法(冷热数据分离)

背景 MySQL中使用的InnoDB存储引擎采用了一种特别的最近最少使用&#xff08;LRU, Least Recently Used&#xff09;算法来管理其Buffer Pool中的页&#xff08;包括数据页和索引页&#xff09;。Buffer Pool是InnoDB用来缓存数据&#xff0c;以减少磁盘I/O操作的内存区域。正…