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 组件包裹 提供给全局路由功能
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 内容 如下
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 内容如下
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部分 我们先实现左侧功能
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
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 主要实现用户登录 注册 认证功能
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 更新如下代码
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的状态
<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连 谢谢!!