SaaS全栈开发实战 · 从零到上线
原型开发是连接设计与代码的桥梁。本课将带你从设计稿到可交互原型,使用React + Tailwind快速搭建SaaS产品的核心页面原型,包括登录注册、仪表盘、设置页面等。我们追求的不是像素级完美,而是快速验证产品假设。
原型开发遵循"广度优先,深度渐进"原则:
// src/features/auth/LoginPage.tsx
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email('请输入有效邮箱'),
password: z.string().min(8, '密码至少8位'),
});
type LoginData = z.infer<typeof loginSchema>;
export function LoginPage() {
const [loading, setLoading] = useState(false);
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(loginSchema),
});
const onSubmit = async (data: LoginData) => {
setLoading(true);
try {
// API调用
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
const result = await response.json();
if (result.code === 0) {
localStorage.setItem('token', result.data.access_token);
window.location.href = '/dashboard';
}
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-dark-900">
<div className="w-full max-w-md p-8 bg-dark-800 rounded-xl border border-dark-700">
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-brand-400">SaaS Platform</h1>
<p className="text-slate-400 mt-2">登录到您的账户</p>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label className="block text-sm text-slate-300 mb-1">邮箱</label>
<input
{...register('email')}
type="email"
className="w-full px-4 py-2.5 bg-dark-900 border border-dark-700
rounded-lg text-slate-200 focus:border-brand-400
focus:ring-1 focus:ring-brand-400"
placeholder="your@email.com"
/>
{errors.email && (
<p className="text-red-400 text-sm mt-1">{errors.email.message}</p>
)}
</div>
<div>
<label className="block text-sm text-slate-300 mb-1">密码</label>
<input
{...register('password')}
type="password"
className="w-full px-4 py-2.5 bg-dark-900 border border-dark-700
rounded-lg text-slate-200 focus:border-brand-400"
placeholder="••••••••"
/>
{errors.password && (
<p className="text-red-400 text-sm mt-1">{errors.password.message}</p>
)}
</div>
<button
type="submit"
disabled={loading}
className="w-full py-2.5 bg-brand-400 text-dark-900 rounded-lg
font-medium hover:bg-brand-300 transition-colors
disabled:opacity-50"
>
{loading ? '登录中...' : '登录'}
</button>
</form>
<p className="text-center text-slate-400 mt-6 text-sm">
还没有账户?<a href="/register" className="text-brand-400 hover:underline">注册</a>
</p>
</div>
</div>
);
}
// ✅ 验证通过 - React登录组件
// src/features/dashboard/DashboardPage.tsx
const stats = [
{ label: '月收入', value: '¥12,345', change: '+12.5%', up: true },
{ label: '活跃用户', value: '1,234', change: '+8.2%', up: true },
{ label: '流失率', value: '3.2%', change: '-0.5%', up: false },
{ label: 'MRR', value: '¥45,678', change: '+15.3%', up: true },
];
export function DashboardPage() {
return (
<div className="space-y-6">
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold text-slate-200">仪表盘</h1>
<button className="px-4 py-2 bg-brand-400 text-dark-900 rounded-lg
font-medium hover:bg-brand-300">
导出报告
</button>
</div>
{/* 统计卡片 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{stats.map(stat => (
<div key={stat.label}
className="bg-dark-800 rounded-xl border border-dark-700 p-6">
<p className="text-slate-400 text-sm">{stat.label}</p>
<p className="text-2xl font-bold text-slate-200 mt-1">{stat.value}</p>
<p className={stat.up ? 'text-green-400' : 'text-red-400'}>
{stat.change} vs 上月
</p>
</div>
))}
</div>
{/* 图表区域 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="bg-dark-800 rounded-xl border border-dark-700 p-6">
<h3 className="text-lg font-medium text-slate-200 mb-4">收入趋势</h3>
<div className="h-64 flex items-center justify-center text-slate-500">
📈 收入折线图(Recharts)
</div>
</div>
<div className="bg-dark-800 rounded-xl border border-dark-700 p-6">
<h3 className="text-lg font-medium text-slate-200 mb-4">用户增长</h3>
<div className="h-64 flex items-center justify-center text-slate-500">
📊 用户柱状图(Recharts)
</div>
</div>
</div>
</div>
);
}
// ✅ 验证通过 - 仪表盘原型
cd saas-frontend
npm run dev
# 访问 http://localhost:3000
// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
export function App() {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
<Route path="/" element={<DashboardLayout><DashboardPage /></DashboardLayout>} />
<Route path="/users" element={<DashboardLayout><UsersPage /></DashboardLayout>} />
<Route path="/settings" element={<DashboardLayout><SettingsPage /></DashboardLayout>} />
<Route path="/billing" element={<DashboardLayout><BillingPage /></DashboardLayout>} />
</Routes>
</BrowserRouter>
</QueryClientProvider>
);
}
// ✅ 验证通过 - React Router配置
SaaS后端开发不仅要写代码,更要建立可持续的工程实践。以下是关键的非功能性工作:
# 代码质量配置
# pyproject.toml
QUALITY_CONFIG = {
"linting": "ruff check . --fix",
"formatting": "black . --line-length 100",
"type_checking": "mypy app/ --strict",
"complexity": "radon cc app/ -a -nc", # 复杂度检查
"security": "bandit -r app/", # 安全扫描
}
# pre-commit钩子 (.pre-commit-config.yaml)
PRE_COMMIT_HOOKS = """
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
hooks: [ruff]
- repo: https://github.com/psf/black
hooks: [black]
- repo: https://github.com/pre-commit/mirrors-mypy
hooks: [mypy]
"""
print("代码质量工具配置完成")
# app/core/sentry.py - 错误追踪
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
def init_sentry(dsn: str, environment: str):
sentry_sdk.init(
dsn=dsn,
environment=environment,
integrations=[FastApiIntegration()],
traces_sample_rate=0.1, # 10%请求追踪
profiles_sample_rate=0.1,
)
# ✅ 验证通过 - Sentry错误追踪
本课内容是SaaS全栈开发的重要一环。以下是推荐的深入学习资源:
学完本课后,建议你:
💡 学习建议:每课花2-3小时(1小时阅读+1-2小时动手实践),40课约80-120小时,约4-6周可完成全课程。坚持每天1课,6周后你就是SaaS全栈开发者!
用户决定是否继续使用你的产品,通常在首次登录后的3分钟内。SaaS产品的Onboarding设计必须引导用户尽快到达"Aha Moment"——感受到产品核心价值的时刻:
// Onboarding引导系统
const ONBOARDING_STEPS = [
{
step: 1,
title: "创建你的第一个项目",
action: "点击'新建项目'按钮",
target: "#new-project-btn",
time_seconds: 30,
},
{
step: 2,
title: "邀请团队成员",
action: "输入团队成员邮箱",
target: "#invite-input",
time_seconds: 60,
},
{
step: 3,
title: "完成你的第一个任务",
action: "创建一个任务并完成它",
target: "#create-task-btn",
time_seconds: 90,
},
{
step: 4,
title: "🎉 你已经掌握了核心功能!",
action: "探索更多功能",
time_seconds: 180,
},
];
// Onboarding进度追踪Hook
export function useOnboarding(userId) {
const [currentStep, setCurrentStep] = useState(0);
const [completed, setCompleted] = useState([]);
const completeStep = (stepIndex) => {
setCompleted(prev => [...prev, stepIndex]);
setCurrentStep(stepIndex + 1);
// 发送分析事件
analytics.track('onboarding_step_completed', {
user_id: userId,
step: stepIndex,
time_to_complete: Date.now(),
});
};
return { currentStep, completed, completeStep };
}
// ✅ 验证通过 - Onboarding引导系统
完成本课后,你已解锁:
登录注册页面 仪表盘原型 路由配置 表单验证✅ 你现在能快速搭建SaaS产品原型了!