情绪日记 MVP 闭环:从本地 Demo 到内测版打包 情绪日记 MVP 闭环从本地 Demo 到内测版打包开发一个独立小工具最关键的心理里程碑就是跑通 MVP最小可行产品闭环。在本地开发阶段代码跑在localhost:3000上一切都显得顺风顺水。但当真正要把代码打包成可以在真实移动端设备上运行、并且能发给第一批内测用户试用的版本时各种在本地被忽略的隐形问题就会集中爆发iOS Safari 软键盘弹出时顶烂底部布局、PWA 离线 Service Worker 缓存死锁、暗黑模式下字体对比度失效……今天下午我把 AI 情绪日记的第一个内部测试版本v0.1.0-alpha打包完成并分发给了 10 位早期用户。记录一下从本地 Demo 到可交付内测版的过程中我打过的四个核心补丁。-------------------------------------------------------------------- | AI 情绪日记 MVP 闭环核心演进路线 | -------------------------------------------------------------------- | 本地 Demo 阶段 | | └── 仅实现核心 Prompt 提取与 IndexedDB 存储 | | | | 打包内测阶段 (修复 4 大体验断层) | | ├── 补丁 1: iOS 软键盘动态视口高度适配 (100dvh) | | ├── 补丁 2: PWA Web App Manifest 配置 (可直接添加到主屏幕) | | ├── 补丁 3: Canvas 高清屏 2x/3x 像素比物理抗锯齿重绘 | | └── 补丁 4: 离线模式降级拦截 (网络断开时纯本地渲染) | --------------------------------------------------------------------1. 补丁一iOS Safari 动态视口高度dvh适配在移动端浏览器上很多开发者习惯写height: 100vh。但在 iPhone 上Safari 底部的地址栏和工具栏会在用户滑动时动态收起和展开导致原本应该固定在底部的操作栏被工具栏遮挡了一半。重构为现代 CSS 的动态视口单位dvh/* 适配移动端动态地址栏的视口容器 */ .app-container { min-height: 100vh; /* 旧版浏览器保底 */ min-height: 100dvh; /* 动态感知 Safari 工具栏高度 */ display: flex; flex-direction: column; padding-bottom: env(safe-area-inset-bottom, 16px); }配合env(safe-area-inset-bottom)避开 iPhone 底部的小黑条Home Indicator界面在真机上瞬间变得极其整洁。2. 补丁二PWA 支持免安装直接添加到手机主屏幕对于小而美的手账工具让用户去 App Store 搜索下载一个动辄上百兆的 App 门槛太高。通过配置 PWA渐进式 Web 应用用户只需要在 Safari 中点击“添加到主屏幕”就能获得一个没有任何浏览器地址栏、带有独立 App 图标的原生级体验。配置极简的manifest.json{ name: 听汐的情绪手账, short_name: 情绪手账, start_url: /, display: standalone, background_color: #FAF7F0, theme_color: #FAF7F0, icons: [ { src: /icons/icon-192.png, sizes: 192x192, type: image/png }, { src: /icons/icon-512.png, sizes: 512x512, type: image/png } ] }3. 补丁三Canvas 导出卡片的 Retina 视网膜高清适配在将手账卡片渲染并导出为 PNG 图片时如果在普通的 Canvas 容器上直接绘制在高清手机屏幕上查看导出的图片会发现文字和线条非常模糊。必须在绘制前获取设备的window.devicePixelRatio进行物理像素倍乘export function setupRetinaCanvas(canvas: HTMLCanvasElement, width: number, height: number) { const dpr window.devicePixelRatio || 2; canvas.width width * dpr; canvas.height height * dpr; canvas.style.width ${width}px; canvas.style.height ${height}px; const ctx canvas.getContext(2d); if (ctx) { ctx.scale(dpr, dpr); // 缩放绘图上下文保证后续绘图逻辑坐标不变 ctx.imageSmoothingEnabled true; ctx.imageSmoothingQuality high; } return ctx; }内测分发与心流反馈把内测链接通过私信发给几位平时爱写手账的朋友后收到的第一条反馈是“原来不用打一长篇大论随手写两个字就能生成一张这么好看的卡片太治愈了。”看到用户真正从工具中获得轻松感的那一刻前面几天调 CSS 和踩坑的疲惫感一扫而空。明天开始收集第一波集中反馈继续精简与打磨。