零后端、零数据库——我做了一个让 10000+ 人成功告白的开源工具

前言

你有没有想过:为什么表达一句"我喜欢你",需要注册账号、上传数据、忍受广告、担心隐私泄露?

我是舒一笑不秃头,一个相信"技术应该让人更自由"的开发者,更多精彩内容~。

这个工具叫 xin2u (心意给你)。它只做一件事------让你用一条链接,把一封精心设计的告白信送到 TA 面前。没有后端,没有数据库,没有注册登录,你的情话只存在于那条 URL 里


一、痛点:现有告白工具的三宗罪

痛点 典型表现
🔒 隐私焦虑 你的告白内容存在别人的服务器上,谁知道会不会被"脱裤"
💸 付费墙 免费模板丑到哭,好看的要 ¥9.9 解锁
🐌 体验臃肿 加载 5 秒、弹窗 3 个、广告满屏

xin2u 的回答:全部数据压缩进 URL,纯前端渲染,免费,Lighthouse 性能分 95+。


二、架构深度:一条 URL 如何承载一封情书

核心思路

typescript 复制代码
用户输入 → JSON → lz-string 压缩 → URL 安全编码 → 拼接到链接

整个数据流不经过任何服务器。收到链接的人打开页面,前端从 URL 参数中解压还原数据,直接渲染。

关键代码(shortLink.ts)

typescript 复制代码
import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from 'lz-string';
import { nanoid } from 'nanoid';

export interface ConfessionPayload {
  toName: string;
  fromName: string;
  message: string;
  photo: string | null;
  templateId: string;
  festivalId?: string;
}

// 压缩:比 btoa 短 30-40%
export function encodePayload(payload: ConfessionPayload): string {
  return compressToEncodedURIComponent(JSON.stringify(payload));
}

// 解压:兼容新旧格式
export function decodePayload(encoded: string): ConfessionPayload | null {
  try {
    const decompressed = decompressFromEncodedURIComponent(encoded);
    if (decompressed) return JSON.parse(decompressed);
  } catch { /* fallthrough */ }
  // 旧格式兼容
  try {
    return JSON.parse(decodeURIComponent(atob(encoded)));
  } catch { return null; }
}

// 语义化短 ID:heart-ab12cd34
export function generateShortId(templateId: string): string {
  const PREFIX: Record<string, string> = {
    heartbeat: 'heart', 'falling-stars': 'stars',
    'rose-garden': 'rose', sakura: 'sakura', /* ... */
  };
  return `${PREFIX[templateId] || 'love'}-${nanoid(8).toLowerCase()}`;
}

为什么选 lz-string?

  • 原生支持 URI 安全编码,无需二次 escape
  • 对中文文本压缩率极高(告白信大量中文 → 压缩后 URL 长度可控)
  • 零依赖、体积仅 5KB gzip

技术栈一览

层级 选型 理由
框架 React 18 + TypeScript 类型安全 + 生态成熟
构建 Vite 5 秒级 HMR,生产构建极快
状态 Zustand + persist 极简 API,自动 localStorage 持久化
样式 Tailwind CSS 原子化 CSS,零运行时开销
动画 GSAP 60fps 流畅动画,告白场景的灵魂
国际化 i18next 7 种语言,覆盖中日韩英法德西
部署 Cloudflare Pages 全球 CDN,免费额度够用

三、极简体验:10 个模板,3 步完成

复制代码
选模板 → 填内容 → 生成链接 → 发给 TA

10 个精心设计的模板,覆盖不同情感场景:

模板 场景 视觉风格
💌 心跳信笺 经典告白 暖粉渐变 + 心跳动画
🌌 星辰坠落 浪漫夜空 深紫星空 + 流星粒子
🌹 玫瑰花园 热烈表白 玫瑰红 + 花瓣飘落
🎈 爱的气球 轻松甜蜜 天空蓝 + 气球上升
📸 拍立得 记忆定格 暖黄复古 + 照片框
🌿 萤火之森 含蓄深情 暗绿 + 萤火虫
🌊 海边日落 浪漫约定 金橙渐变 + 海浪
🕯️ 烛光晚餐 温馨氛围 暖橙暗调 + 烛光摇曳
❄️ 初雪 纯净心意 冰蓝渐变 + 雪花飘落
🌸 樱花季 日式浪漫 樱粉 + 花瓣雨

更硬核的是 :8 个模板复用同一个 BaseLetter 组件 + Theme 配置,新增模板只需写一个 theme 对象:

typescript 复制代码
// themes.tsx 中的一个主题定义(极简)
export const LETTER_THEMES = {
  sakura: {
    bg: 'from-sakura-100 via-sakura-200 to-sakura-400',
    particle: '🌸',
    particleCount: 30,
    textColor: 'text-gray-800',
    // ...
  },
};

四、节日系统:15 个节日 × 7 个国家

不只是告白。生日、母亲节、圣诞节、七夕、520......每个节日都有预设祝福语和推荐模板组合。

typescript 复制代码
// 数据驱动,零硬编码
export const FESTIVALS = {
  'valentines-day': {
    emoji: '💝', mood: 'romantic',
    wishForms: ['love-letter', 'rose-garden', 'love-balloons', 'candle', 'starry-night'],
  },
  // ... 15 个节日
};

export const COUNTRIES = [
  { code: 'CN', flag: '🇨🇳', language: 'zh', festivals: [...] },
  { code: 'JP', flag: '🇯🇵', language: 'ja', festivals: [...] },
  // ... 7 个国家
];

五、超级个体思考

为什么要做这个?

我不想再看到有人为了发一句"我喜欢你",要把自己的隐私交给一个不知名的小程序。

技术的浪漫,不在于复杂,而在于克制。

  • 不需要后端 → 因为你的情话不该存在别人的硬盘上
  • 不需要注册 → 因为表达爱意不该有门槛
  • 不需要付费 → 因为告白本身就是一种勇气

独立开发者的取舍

我选择了 我放弃了 原因
URL 存储 数据库 隐私第一
纯前端 后端 API 零运维成本
Cloudflare Pages 自建服务器 免费 + 全球加速
10 个精品模板 100 个平庸模板 质量 > 数量

六、地址 & 体验


结语

最好的代码,是让人感受不到代码存在的代码。

相关推荐
为思念酝酿的痛2 小时前
POSIX信号量
linux·运维·服务器·后端
小羊在睡觉2 小时前
力扣84. 柱状图中最大的矩形
后端·算法·leetcode·golang·go
swipe2 小时前
Neo4j + Graph RAG 医疗知识图谱工程实践:患者教育问答真正需要的是“关系可追溯”
后端·langchain·llm
源码宝3 小时前
MES系统源码:Java8 + SpringBoot2.7 + MySQL8 + Redis,后端源码清爽易扩展
java·后端·源码·springboot·mes系统·源码二开·mes源码
金銀銅鐵4 小时前
[Java] 如何理解 class 文件中方法的 descriptor?
java·后端
村口张大爷4 小时前
05 — 分层架构与依赖倒置
后端·架构·系统架构
Jasonakeke5 小时前
SpringBoot自动配置原理揭秘
java·spring boot·后端
IT_陈寒6 小时前
Vite热更新失灵?你可能漏了这个配置
前端·人工智能·后端
uzong6 小时前
面试官:如何做好架构设计
后端·架构
Cosolar7 小时前
QwenPaw Agent 实现原理深度剖析
后端·面试·架构