从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(四) 实现注册功能

news/2025/2/27 5:33:19

1.前端页面文件夹  路由功能 

  前端web目录下 创建pages 文件夹 创建Home.jsx   首页  SignUpPage 注册 LoginPage 登录 

  ProfilePage 个人资料  SettingPage

 各个页面简单实现一下 内容如下

const HomePage = () => {
    return (
      <div>
          HomePage
      </div>
    )
  }
  
  export default HomePage

 在main.js 引入  BrowserRouter

import { BrowserRouter } from 'react-router-dom' 

用BrowserRouter 把App 组件包裹 提供给全局路由功能

javascript">import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

import { BrowserRouter } from 'react-router-dom'
createRoot(document.getElementById('root')).render(
  <StrictMode>
    <BrowserRouter>
        <App />
    </BrowserRouter>
  </StrictMode>,
)

 App.jsx 内容  如下  

javascript">
import HomePage from '@/pages/HomePage'
import SignUpPage from './pages/SignUpPage'
import ProfilePage from './pages/ProfilePage'
import LoginPage from './pages/LoginPage'
import SettingsPage from './pages/SettingPage'

import { Routes, Route} from 'react-router-dom'
const App = () => {
  return (
    <div>
      <Routes>
        <Route path="/" element={<HomePage />} /> 
        <Route path="/signup" element={<SignUpPage />} /> 
        <Route path="/login" element={<LoginPage /> } /> 
        <Route path="/settings" element={<SettingsPage /> } /> 
        <Route path="/profile" element={<ProfilePage />} /> 
      </Routes>
    </div>
  )
}

export default App

  1.测试 路由功能  首页跳转到注册 再从注册跳转到首页

 HomePage.jsx 内容如下

javascript">
import {Link} from "react-router-dom"
const HomePage = () => {
    return (
      <div>
          <p className="text-red-500">首页</p>
          <Link to="/signup">跳转注册页面</Link>
      </div>
    )
  }
  
  export default HomePage

注册页面类似 点击跳转可以实现路由切换  

 

2.注册页面

 注册页面分2部分 我们先实现左侧功能 

javascript">import { useState } from "react";
import { MessageSquare,User,Mail,Lock,Eye, EyeOff,Loader2} from "lucide-react";
import {Link} from "react-router-dom"

    const SignUpPage = () => {
    const [showPassword, setShowPassword] = useState(false);
    const [formData, setFormData] = useState({
        userName: "",
        email: "",
        password: "",
    })
    const {signup,isSigningUp} = useAuthStore();

    const validateForm = () => {
        
    }

    const handleSubmit = (e) => {
        console.log(e)
        e.preventDefault();
    }

  return (
    <div className="min-h-screen grid lg:grid-cols-2">
       {/*left side*/}
        <div className="flex flex-col justify-center items-center p-6 sm:p-12">
            <div className="w-full mt-10">
                {/* logo */}
                <div className="text-center mb-8">
                    <div className="flex flex-col items-center gap-2 group">
                        <div className="size-12 rounded-xl bg-primary/10 flex items-center justify-center
                        group-hover:bg-primary/20 transition-colors">
                            <MessageSquare className="size-6 text-primary"></MessageSquare>
                        </div>
                        <h1 className="text-2xl font-bold mt-2">账号注册</h1>
                        <p className="text-base-content/60">从免费创建账号开始</p>
                    </div>
                </div>
                {/* form */}
                <form onSubmit={handleSubmit} className="space-y-6">
                    <div className="form-control">
                        <label className="label">
                            <span className="label-text font-medium">用户名</span>
                        </label>
                        {/* 输入框 */}
                        <div className="relative">
                            <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                                <User className="size-5 text-base-content/40" />
                            </div>
                            <input 
                                type="text"
                                className={`input input-bordered w-full pl-10`}
                                placeholder="请输入用户名"
                                value={formData.userName}
                                onChange={(e)=> setFormData({...formData,userName:e.target.value})}
                            >

                            </input>
                        </div>
                    </div>
                    <div className="form-control">
                        <label className="label">
                            <span className="label-text font-medium">邮箱</span>
                        </label>
                        {/* 输入框 */}
                        <div className="relative">
                            <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                                <Mail className="size-5 text-base-content/40" />
                            </div>
                            <input 
                                type="text"
                                className={`input input-bordered w-full pl-10`}
                                placeholder="请输入邮箱地址"
                                value={formData.email}
                                onChange={(e)=> setFormData({...formData,email:e.target.value})}
                            >

                            </input>
                        </div>
                    </div>

                    <div className="form-control">
                        <label className="label">
                            <span className="label-text font-medium">密码</span>
                        </label>
                        {/* 输入框 */}
                        <div className="relative">
                            <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                                <Lock className="size-5 text-base-content/40" />
                            </div>
                            <input 
                                type={showPassword ? "text" : "password"}
                                className={`input input-bordered w-full pl-10`}
                                placeholder="请输入密码"
                                value={formData.password}
                                onChange={(e)=> setFormData({...formData,password:e.target.value})}
                            >
                            </input>
                            
                            <button
                                type="button"
                                className="absolute inset-y-0 right-0 pr-3 flex items-center"
                                onClick={()=> setShowPassword(!showPassword)}
                            >
                                {showPassword ? (<EyeOff className="size-5 text-base-content/40" />) : (<Eye className="size-5 text-base-content/40" />)}
                            </button>
                        </div>
                    </div>

                   <button 
                        type="submit"
                        className="btn btn-primary w-full"
                        disabled={isSigningUp}
                    >

                        {isSigningUp ? (
                            <>
                                <Loader2 className="size-5 animate-spin"/>
                                Loading...
                            </>
                        ):(
                            "创建账户"
                        )}
                    </button>
                </form>

                <div className="text-center">
                    <p className="text-base-content/60">
                        已有账号?{""}
                        <Link to="/login" className="link link-primary">去登录</Link>
                    </p>
                </div>
            </div>
        </div>

        {/* right side */}
    </div>
  )
}

export default SignUpPage

效果

  1.引入axios 

        前端http请求 我们通axios实现  新建lib文件夹 创建axios.js

javascript">import axios from 'axios';

const axiosInstance = axios.create({
    baseURL: 'http://localhost:3000/api',  //后端服务地址
    withCredentials: true,
})

export default axiosInstance;

  2.引入zustand 

     我们全局所有接口请求 状态 数据都用zustand管理 类似 pinia vuex redux等

  新建store文件夹 创建userAuthStore.js   主要实现用户登录 注册 认证功能

javascript">import {create} from 'zustand'
import axiosInstance from '../lib/axios'
import toast from 'react-hot-toast'

// 创建一个auth store
export const useAuthStore = create((set,get) => ({
    authUser:null, // 用户信息
    isSigningUp: false, // 是否登录
    isLoginningIn: false, // 是否登录中

    signup: async(data) => {
        set({isSigningUp: true})
        try {
            const res = await axiosInstance.post('/auth/signup', data)
            toast.success("注册成功")
            set({authUser: res.data})
        } catch (error) {
            console.log("useAuthStore signup error",error.message)
            set({isSigningUp: true})
        } finally {
            set({isSigningUp: false})
        }
    },
    login: async(data) => {
        set({isLoginningIn: true})
        try {
            const res = await axiosInstance.post('/auth/login', data)
            set({authUser: res.data})
            toast.success("登录成功")

        } catch (error) {
            toast.error(error.response.data.message)
        } finally{
            set({isLoginningIn: false})
        }
    },
    logout: async() => {
        try {
            await axiosInstance.post('/auth/logout')
            set({authUser: null})
            toast.success("退出成功")
        } catch (error) {
            console.log("useAuthStore logout error",error.message)
        }
    }
}))

在signupPage 引入useAuthStore 更新如下代码 

javascript">const {signup,isSigningUp} = useAuthStore();

    const validateForm = () => {
        if(!formData.userName.trim()) return toast.error("请输入用户名");
        // 邮箱正则验证
        if(!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(formData.email)) return toast.error("请输入正确的邮箱");
        if(formData.password.length < 6) return toast.error("请输入至少6位的密码");

        return true
    }

    const handleSubmit = (e) => {
        console.log(e)
        e.preventDefault();
        const success = validateForm();
        if(success) signup(formData);
    }

注册按钮加个loading的状态 

javascript"><button 
                        type="submit"
                        className="btn btn-primary w-full"
                        disabled={isSigningUp}
                    >

                        {isSigningUp ? (
                            <>
                                <Loader2 className="size-5 animate-spin"/>
                                Loading...
                            </>
                        ):(
                            "创建账户"
                        )}
                    </button>

3.测试注册功能

看提示根据浏览器同源策略  前后端端口不一样 跨域了

所以我们要去server端配置下跨域 配置很简单

在server index.js中引入cors  做如下配置

import cors from 'cors'

// 跨域配置

// app.options('*', cors()) // include before other routes

app.use(cors({

    origin:'http://localhost:5173', // 我们前端的地址

    credentials: true

  }))

然后再去前端发送注册请求 

提示注册成功!!

去数据库验证结果 也有我们刚注册的用户

这篇 就这样把 不写登录功能了 篇幅有点多 有问题欢迎评论留言!!喜欢的来个3连 谢谢!! 


http://www.niftyadmin.cn/n/5869522.html

相关文章

OpenEuler学习笔记(三十五):搭建代码托管服务器

以下是主流的代码托管软件分类及推荐&#xff0c;涵盖自托管和云端方案&#xff0c;您可根据团队规模、功能需求及资源情况选择&#xff1a; 一、自托管代码托管平台&#xff08;可私有部署&#xff09; 1. GitLab 简介: 功能全面的 DevOps 平台&#xff0c;支持代码托管、C…

Linux 文件操作与 Socket 编程核心知识详解

Linux 文件操作与 Socket 编程核心知识详解 一、Linux 文件与 Socket 的统一性 1.1 核心设计理念 在 Linux 系统中&#xff0c;秉持"一切皆文件"的设计理念&#xff1a; 所有 I/O 设备&#xff08;常规文件、网络 socket、外设等&#xff09;均被抽象为文件统一通…

Java HTTP 请求的四种实现方式:Apache HttpClient、OkHttp、WebClient 与 Java 11 HttpClient

目录 1.使用Java11内置的HttpClient 2.使用OkHttp 3.使用SpringWebClient 4.使用ApacheHttpClient5.x 在现代Java开发中&#xff0c;HttpClient和PostMethod是较旧的ApacheHttpClient3.x的API。推荐使用更现代的技术来替代这些代码&#xff0c;例如Java11内置的HttpClient或…

ubuntu配置jmeter

1.前提准备 系统 ubuntu server 22.04 前提条件&#xff1a;服务器更新apt与安装lrzsz&#xff1a;更新apt&#xff1a; sudo apt update安装lrzsz: 命令行下的上传下载文件工具 sudo apt install lrzszsudo apt install zip2.安装jemeter 2.1.下载jdk17 输入命令&#xf…

网络渗透作业

第一题&#xff1a;使用Xpath对Order by 语句进行布尔盲注 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://www.w3.org/1999/xhtml&quo…

游戏引擎学习第125天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾并为今天的内容做准备。 昨天&#xff0c;当我们离开时&#xff0c;工作队列已经完成了基本的功能。这个队列虽然简单&#xff0c;但它能够执行任务&#xff0c;并且我们已经为各种操作编写了测试。字符串也能够正常推送到队…

初会学习记录

【25初级会计《实务》】第一章&#xff1a;权责发生制举例_哔哩哔哩_bilibili 务实&#xff1a; 第一章 (1)会计概念&#xff0c;职能和目标&#xff1a; 2025年2月25日&#xff1a; (2)会计假设&#xff1a; 2025年2月26日&#xff1a; (3)会计核算基础&#xff1a; 202…

centos和ubuntu安装mysql教程

1&#xff0c;安装包准备 cd /usr/local/ mkdir mysql cd mysql 将压缩包复制进mysql文件夹下 tar -xvf mysql-8.0.21-linux-glibc2.12-x86_64.tar.xz mv mysql-8.0.21-linux-glibc2.12-x86_64 mysql8 cd /usr/local/mysql mkdir data 2&#xff0c;创建用户组以及用户密码&…