零后端、零数据库——我做了一个让 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 个平庸模板 质量 > 数量

六、地址 & 体验


结语

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

相关推荐
Java技术小馆2 小时前
如何零成本将各种 AI 编程工具接入免费大模型?
后端
Tutankaaa2 小时前
从10队到50队:知识竞赛软件的高并发场景如何设计?
java·经验分享·后端·spring
阿丰资源2 小时前
基于Spring Boot的网上摄影工作室系统(源码一键运行)
java·spring boot·后端
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题】【Java基础篇】第40题:Java中的深拷贝和浅拷贝有什么区别
java·开发语言·后端·面试
小强19884 小时前
为什么你建了索引,查询还是很慢?常见失效原因汇总
后端
长大19884 小时前
MySQL 索引到底是什么?普通人也能看懂的通俗讲解
后端
阿苟4 小时前
spring重点详解
java·后端·面试
l软件定制开发工作室5 小时前
Spring开发系列教程(35)——使用Actuator
java·后端·spring
我叫黑大帅5 小时前
PyScript-GitHubRepo: 构建高性能GitHub仓库批量下载工具的技术实践
后端·python·面试