[{"data":1,"prerenderedAt":893},["ShallowReactive",2],{"\u002F2026-07-05":3,"\u002F2026-07-05-rel":306},{"id":4,"title":5,"body":6,"column":290,"date":291,"description":12,"extension":292,"hero_image":293,"meta":294,"navigation":295,"path":296,"seo":297,"series_id":293,"severity":293,"stem":298,"summary":299,"tags":300,"__hash__":305},"posts\u002F2026-07-05-克制的网页动效.md","这个动效，是在帮用户还是在展示我？",{"type":7,"value":8,"toc":273},"minimark",[9,13,17,22,25,28,31,39,43,46,49,52,55,61,65,68,74,77,84,87,91,98,101,109,116,120,127,134,149,152,155,158,164,170,176,179,189,198,213,216,219,222,225,228,231,238,241,244,257,263,270],[10,11,12],"p",{},"前端做了一轮动效设计，反复遇到同一个问题：这个动效是在帮助用户理解界面，还是只在展示\"我会做动效\"。区分的标准不是感觉，是三条具体的判据。",[14,15,16],"h2",{"id":16},"动效的三条判据",[18,19,21],"h3",{"id":20},"判据一是否传达了状态变化","判据一：是否传达了状态变化",[10,23,24],{},"用户操作后，系统进入新的状态。动效的作用是让这个状态变化可感知。",[10,26,27],{},"加载中。一个网络请求发出去了，但数据还没回来，用户看不到任何反馈会以为卡了。打字指示（三个点循环脉冲、错开时间）就是这类必做的动效。用户不需要理解\"这是一个脉冲\"，只需要看到\"系统在做什么\"。",[10,29,30],{},"成功和失败。表单提交后，服务器返回 200 还是 400，用户需要知道。Toast 从屏幕边滑入、带上颜色标记（绿色还是红色）和文案，用户秒懂结果。这里动效的信息密度很高：位置（从边界滑入说明是一个通知）、方向、颜色都在传达信息。",[10,32,33,34,38],{},"这一类动效 ",[35,36,37],"strong",{},"该做","。不做的代价是用户体验割裂——界面突然变了，用户需要花脑力去理解\"发生了什么\"。",[18,40,42],{"id":41},"判据二是否建立了空间关系","判据二：是否建立了空间关系",[10,44,45],{},"用户在多个界面间切换，每个界面有不同的内容。动效的作用是让用户在心理上形成\"我从 A 地来到了 B 地\"的感觉。",[10,47,48],{},"页面切换。新页面从右侧滑入或从中央缩放进来，老页面退出。用户的注意力自然跟随移动，不会觉得\"屏幕闪了一下\"；反而能通过位移方向推断页面的层级关系。",[10,50,51],{},"弹窗。一个模态框从中央向外弹出、带回弹感，用户知道\"这个内容是浮在上面的、暂时的\"。而如果弹窗突然出现，用户需要花力气解析\"这是什么、它和后面的页面什么关系\"。",[10,53,54],{},"展开与收起。列表条目逐个滑入，后出现的条目在前面条目之后，这叫 stagger 编排。用户可以跟踪每一项的出现，而不是\"列表突然全填满了，我从哪里开始看\"。导航栏的 active 指示器平移而不是闪烁切换，用户能看到\"我从这里跳到那里\"。",[10,56,57,58,60],{},"这一类动效也 ",[35,59,37],{},"。代价同样高：没有这些线索，界面就像一个无生命的状态表格，用户要主动扫描才能判断当前在哪、怎么走。",[18,62,64],{"id":63},"判据三装饰性动效","判据三：装饰性动效",[10,66,67],{},"一个元素 hover 时发光、卡片有光扫效果、logo 漂浮、按钮涟漪……这些都是装饰。它们不传达任何状态变化，不建立空间关系，就是\"看起来更高级\"。",[10,69,70,71],{},"品牌要求这些吗？可能。会提升用户体验吗？证据不足。",[35,72,73],{},"该砍。",[10,75,76],{},"这里的逻辑是：每一帧动画都要用户的 GPU 和浏览器去渲染。装饰性动效的每一帧都是在\"花钱不赚钱\"。当装饰堆积到一定程度，就会出现性能问题。",[10,78,79,80,83],{},"实践中的折衷是：装饰性动效只保留在",[35,81,82],{},"关键时刻","。用户花钱了、获得了成功的反馈，这时撒纸屑。余额减少了，扣点粒子迸发。这些装饰是在庆祝或强调，而不是无处不在。",[14,85,86],{"id":86},"性能与可访问性的硬要求",[18,88,90],{"id":89},"_60fps-底线","60fps 底线",[10,92,93,94,97],{},"动效要稳定在 60fps，否则就是卡顿伪装成流畅。这意味着",[35,95,96],{},"只动 transform、opacity 和 filter，不动 layout 属性","。",[10,99,100],{},"为什么？改变 width、height、margin 或 left\u002Ftop 会触发重排（reflow）。浏览器要重算文档流、重排页面，这会花很长时间，远超单帧预算。动画自动掉帧。",[10,102,103,104,108],{},"实例：一个列表容器用 height 从隐藏到展开的动画，看起来像\"列表展开\"，实际上每帧都在触发重排。改成用 ",[105,106,107],"code",{},"scaleY"," transform 动画，性能立刻改善。",[10,110,111,112,115],{},"另一个细节：长列表的逐项入场动画。当列表项数足够多时，用 stagger 从上往下依次滑入，屏幕可见范围内的项动画效果还不错，但超出视口的项同样在运行动画逻辑——整个列表的动画状态都要参与计算，对内存和渲染管线造成压力。硬要求是：",[35,113,114],{},"大列表只对首屏可见项做动画","，不可见项直接渲染到最终状态。",[18,117,119],{"id":118},"prefers-reduced-motion必须尊重用户的选择","prefers-reduced-motion：必须尊重用户的选择",[10,121,122,123,126],{},"系统级的「减少动态效果」开关（macOS 辅助功能、Windows 显示设置）对应 CSS 媒体查询 ",[105,124,125],{},"prefers-reduced-motion: reduce","。一部分用户打开这个设置（通常因为前庭功能障碍、晕动症或认知障碍），动效会让他们头晕或注意力分散。",[10,128,129,130,133],{},"硬要求：",[35,131,132],{},"当系统选择 reduced-motion 时，所有动效必须降级","。降级方案是什么？最简单且有效的方案是淡入淡出。用户看不到位移、没有高频闪烁，界面从无到有、从有到无。装饰性的粒子、纸屑、光扫一律不渲染。",[10,135,136,137,140,141,144,145,148],{},"实现方式是在应用根部用一个全局的 ",[105,138,139],{},"MotionConfig"," 接管 ",[105,142,143],{},"reducedMotion=\"user\"","。React 的 ",[105,146,147],{},"useReducedMotion()"," hook 会读系统偏好。检查返回值，该短路的短路，该降速的降速。",[10,150,151],{},"没有这一步，上线的产品会有一小部分用户会因为你的动效而无法使用你的产品。",[14,153,154],{"id":154},"参数选择的逻辑",[10,156,157],{},"时长有三个档位。",[10,159,160,163],{},[35,161,162],{},"Micro（0.17 秒 \u002F 170ms）","：按钮 hover 抬升、开关滑动、输入框 focus 发光。这些是每秒可能发生多次的交互，用户期望立刻看到反馈。太慢了会让交互感觉迟钝。",[10,165,166,169],{},[35,167,168],{},"Standard（0.34 秒 \u002F 340ms）","：消息气泡进场、列表项滑入、Toast 弹出。这是用户注意到的变化，需要一些时间让眼睛跟随，但不能太长。动效过长就开始让人觉得\"卡\"而不是\"流畅\"。",[10,171,172,175],{},[35,173,174],{},"Emphasis（0.48 秒 \u002F 480ms）","：页面切换的主体。新页面的进入需要最长的时间，因为整个屏幕都在变。但退出只需要 ~0.36 秒（75% 的进入时长），用户离开某个页面应该更快、不需要过度演示。",[10,177,178],{},"缓动曲线有两种常见类型。",[10,180,181,184,185,188],{},[35,182,183],{},"OutExpo","（",[105,186,187],{},"[0.16, 1, 0.3, 1]","）：开始快、结束慢。用在列表项进场、消息气泡滑入。因为是内容从无到有，用户的注意力已经被吸引，动画可以前置地快速到达目标位置，最后用缓冲平稳落地。感觉是\"内容飘进来了\"。",[10,190,191,184,194,197],{},[35,192,193],{},"SoftSpring",[105,195,196],{},"[0.34, 1.4, 0.5, 1]","）：轻微的超调回弹。用在弹窗进出、输入框 focus。因为是用户主动触发的强交互，回弹感可以传递\"系统在听我\"的反馈。不是生硬的到位，而是\"弹\"到位。",[10,199,200,201,204,205,208,209,212],{},"Spring 预设有三档：",[105,202,203],{},"snappy","（stiffness 520, damping 30）用于最快反应、",[105,206,207],{},"bouncy","（380, 22）用于入场、",[105,210,211],{},"smooth","（260, 32）用于页面转场。数字越大振荡越少，但响应越快；数字小了振荡次数多、柔软感强。",[10,214,215],{},"这些参数都是经过测试的。如果你随意加长动效时间想让用户\"看清每一帧\"，代价是整个产品的交互节奏都会崩。快动效之间的间隙会积累，让用户觉得系统在思考，而不是在响应。",[14,217,218],{"id":218},"时长过长的代价",[10,220,221],{},"某些设计师认为慢动效显得\"精致\"。实践中这是错的。",[10,223,224],{},"首先，从用户体验的角度：动效的目的是传达信息，不是让用户等待。当一个必要的动效（如页面转场）变得太长，用户在反馈到来之前就开始扫视下一个操作目标，或者认为系统在卡。更长的等待就变成了对用户时间的浪费。",[10,226,227],{},"其次，从性能的角度：更长的动效意味着更多的帧要渲染。一个快速的动效和一个冗长的动效相比，后者不仅占用的渲染时间更多，对 GPU 和内存的压力也更大。在移动设备上、在 GPU 受限的情况下，这直接转化成帧率下降、电池耗尽、设备发热。",[10,229,230],{},"第三，从注意力的角度：冗长的动效会让用户的焦点分散。正在播放的动画仍在吸引视觉注意，用户无法提前扫视接下来的内容。这不是在尊重用户，而是在强制停留。",[10,232,233,234,237],{},"在实施 yun-claude 的动效设计时，判断的标准是：",[35,235,236],{},"动效的时长应该足够用户感知状态变化，但不足够让用户等待","。所以页面进入用 0.48s（完整展示），退出只用 0.36s（快速离开），打字指示用 0.17s（微交互要快）。这个分层是为了加速工作流，而不是为了炫技。",[14,239,240],{"id":240},"小结",[10,242,243],{},"写三条判据是为了在每个 feature 决策点问自己：",[245,246,247,251,254],"ul",{},[248,249,250],"li",{},"这个动效在传达用户需要知道的状态变化吗？",[248,252,253],{},"这个动效在建立空间或层级关系吗？",[248,255,256],{},"如果都不是，它只是装饰。",[10,258,259,260,262],{},"如果是前两者，动效 ",[35,261,37],{},"。保证 60fps、尊重 reduced-motion、控制好时长参数。",[10,264,265,266,269],{},"如果是装饰，",[35,267,268],{},"该砍","。或者保留到特定时刻（成功、失败、关键反馈），而不是无处不在。",[10,271,272],{},"动效不是越多越好，也不是越慢越高级。克制地用、精准地用，动效才能真正帮助用户理解界面。",{"title":274,"searchDepth":275,"depth":275,"links":276},"",2,[277,283,287,288,289],{"id":16,"depth":275,"text":16,"children":278},[279,281,282],{"id":20,"depth":280,"text":21},3,{"id":41,"depth":280,"text":42},{"id":63,"depth":280,"text":64},{"id":86,"depth":275,"text":86,"children":284},[285,286],{"id":89,"depth":280,"text":90},{"id":118,"depth":280,"text":119},{"id":154,"depth":275,"text":154},{"id":218,"depth":275,"text":218},{"id":240,"depth":275,"text":240},"工程手记","2026-07-05","md",null,{},true,"\u002F2026-07-05",{"title":5,"description":12},"2026-07-05-克制的网页动效","三条判据分辨哪些动效值得做、哪些该砍，以及性能与可访问性的硬要求。",[301,302,303,304],"交互动效","Web 性能","无障碍","用户体验","nAZWp03XcfG3E4VVq7VsNOXAWYCi5XBAj6SGZflH3Hk",[307,613,757],{"id":308,"title":309,"body":310,"column":290,"date":600,"description":314,"extension":292,"hero_image":293,"meta":601,"navigation":295,"path":602,"seo":603,"series_id":293,"severity":293,"stem":604,"summary":605,"tags":606,"__hash__":612},"posts\u002F2026-07-29-一年八千次提交AI辅助开发工作流.md","一年八千次提交，我是怎么干的",{"type":7,"value":311,"toc":583},[312,315,318,321,325,328,331,334,338,341,344,358,361,365,368,371,374,378,381,384,404,407,411,414,425,428,432,435,449,452,455,459,462,465,469,472,475,478,482,485,488,513,520,523,529,532,535,538,545,548,551,577,580],[10,313,314],{},"2026 上半年，主要项目累计提交 7787 次（其中 newCodex 因为是 fork 项目，1922 次提交含上游历史；纯新增代码的项目是 yun-claude、yun-claw、new-openclaw 等）。提交数看起来多，但每个提交背后的价值不在数量，而在流程的可重复性。这篇文章把工作流写清楚。",[14,316,317],{"id":317},"流程的六个环节",[10,319,320],{},"整个开发周期分六步：需求澄清 → 设计文档审阅 → 拆实施计划 → 分阶段实现 → 代码审查 → 故障归档。每一步都有明确的输入输出和验证方式。",[18,322,324],{"id":323},"_1-需求澄清","1. 需求澄清",[10,326,327],{},"开始前问清楚，不要猜。典型问题：这个功能要处理哪些用户场景？边界条件是什么？现有系统的哪部分会受影响？",[10,329,330],{},"这一步的产出是一份结构化的需求文档，包括功能范围、约束条件、风险假设。不是长篇幅的铺垫，而是一份清单式的澄清记录。",[10,332,333],{},"在 new-openclaw 项目里，这一步通常是用 Claude 的 superpowers:brainstorming skill 来展开。提问方式很重要：我会列出已知条件，然后问\"这个设计下会遇到什么问题？\"而不是\"你觉得应该怎么做？\"让 AI 在我的约束框架内工作。",[18,335,337],{"id":336},"_2-设计文档与人工审阅","2. 设计文档与人工审阅",[10,339,340],{},"这是整个流程里最关键的检查点。花一小时审一份 200 行的设计文档，比花五小时审一份 2000 行的代码便宜得多。而且回头修改设计的成本远低于修改实现。",[10,342,343],{},"new-openclaw 项目现有 30 份设计文档（specs 目录），每份文档都包括：",[245,345,346,349,352,355],{},[248,347,348],{},"范围：这个设计覆盖什么、不覆盖什么",[248,350,351],{},"决策与权衡：为什么选这个方案，放弃了什么",[248,353,354],{},"接口契约：如果涉及多个模块，清晰定义每个边界",[248,356,357],{},"风险清单：已知的坑和防护措施",[10,359,360],{},"写完设计文档以后，我会读一遍、问几个\"为什么\"，然后提出修改意见。这一步排除了 80% 的方向错误。常见的修改方向有：缩小范围（第一个版本不用处理那么多边界情况）、明确约束（系统资源、网络延迟、并发数的假设）、补充防护（熔断、限流、幂等）。",[18,362,364],{"id":363},"_3-实施计划与-dod-定义","3. 实施计划与 DoD 定义",[10,366,367],{},"设计文档定下来以后，拆成实施计划。计划的粒度是\"一个可验证的功能单元\"——通常是一个小时到半天的工作量，完成后能单独验证成功。",[10,369,370],{},"计划文档里必须写清完成定义（DoD，Definition of Done）。不是\"实现登录功能\"，而是\"写出能拒绝无效格式的登录端点、覆盖单点故障下的重试、有端到端的冒烟测试\"。",[10,372,373],{},"new-openclaw 项目现有 36 份实施计划（plans 目录），跨度从一周的 S0 阶段（骨架 + Mock 后台）到数周的 S1 阶段（真实业务后台）。每份计划都带着清晰的 checklist，这样我在执行时能随时问 Claude：\"下一步应该是什么？\"而不是脑子里模糊地记着进度。",[18,375,377],{"id":376},"_4-分阶段实现与逐步验证","4. 分阶段实现与逐步验证",[10,379,380],{},"有了计划以后，按顺序实现。关键是每一步都要有验证：单元测试、集成测试、或者一个小的 end-to-end 冒烟测试。验证不通过就停在这一步，不往下推。",[10,382,383],{},"这一步会用到三个代理：",[245,385,386,392,398],{},[248,387,388,391],{},[35,389,390],{},"superpowers:test-driven-development"," —— 先写测试，再写实现",[248,393,394,397],{},[35,395,396],{},"superpowers:subagent-driven-development"," —— 复杂任务拆成独立的子任务，并行推进",[248,399,400,403],{},[35,401,402],{},"superpowers:systematic-debugging"," —— 遇到测试失败，用这个代理追根溯源，不要盲目修改代码",[10,405,406],{},"实施过程中如果发现设计假设错了（比如某个接口响应时间远超预期，或者并发场景下出现竞态条件），就停下来回到第 2 步重新审视设计，而不是继续往下推。",[18,408,410],{"id":409},"_5-代码审查","5. 代码审查",[10,412,413],{},"实现完成以后，不是立即合并，而是过一遍 code-reviewer 代理。审查的重点不在代码风格（那个自动工具做），而在：",[245,415,416,419,422],{},[248,417,418],{},"这段代码实现的是设计文档里的哪一部分？偏离了吗？",[248,420,421],{},"错误处理有没有遗漏？边界情况有没有考虑？",[248,423,424],{},"有没有意外改动无关的代码？",[10,426,427],{},"审查通常能抓住两类问题。一类是逻辑问题：某个条件判断漏了一个分支，或者并发场景下两个操作的顺序反了。另一类是\"设计和实现对不上\"：实现了一个设计里没提到的特性，或者某个约束（比如\"这个值不能为空\"）没在代码里强制。",[18,429,431],{"id":430},"_6-故障归档","6. 故障归档",[10,433,434],{},"系统上线以后，bug 是难免的。重要的是怎么处理它。new-openclaw 项目有一套 bug 知识库规范（CLAUDE.md 里定义），每个 bug 修好以后都要新建一份独立文档，包括：",[245,436,437,440,443,446],{},[248,438,439],{},"现象和复现路径",[248,441,442],{},"根因分析：为什么会发生，触发链路是什么",[248,444,445],{},"修复方法：改了什么，为什么选这个方案",[248,447,448],{},"预防措施：代码改进、测试添加、还是构建期检查",[10,450,451],{},"80 份 bug 文档（截至 5 月中旬）不是问题的多，而是追根溯源的记录的多。下次遇到类似现象，能直接查库而不是重新排查。",[14,453,454],{"id":454},"三条铁律",[18,456,458],{"id":457},"_1-假设必须显式声明","1. 假设必须显式声明",[10,460,461],{},"不要猜。不清楚的地方就问，把问题写成澄清清单。\"这个 API 能处理多大的请求体？\"、\"离线场景下要缓存多长时间？\"、\"错误重试间隔是指数退避还是固定时间？\"。",[10,463,464],{},"这些问题看起来小，但决定了实现的复杂度和测试用例的多少。猜错了会导致前期设计精美，但方向错误，后面要推倒重来。",[18,466,468],{"id":467},"_2-修改必须精确","2. 修改必须精确",[10,470,471],{},"只改需要改的部分。这听起来像常识，但在实际工作中容易出现\"顺手改一下边上的代码\"的情况 —— 格式不规范了，变量命名不一致了，某个函数太长了，\"顺便\"重构一下。",[10,473,474],{},"结果是一个改动影响了五个文件，代码审查花了双倍时间，引入了新 bug 的风险。",[10,476,477],{},"规则是：改动必须对应需求的某一行。格式、风格、无关的重构，单独立项，不要混在功能改动里。",[18,479,481],{"id":480},"_3-成功标准必须可验证","3. 成功标准必须可验证",[10,483,484],{},"\"添加验证\"这个说法是模糊的。改写成：\"写出一个测试用例，输入非法邮箱格式，验证 API 返回 400；输入合法邮箱，验证返回 200 和预期数据结构\"。",[10,486,487],{},"每个 plan 文档里的 DoD 都是这样写的。拿一个阶段做例子：",[489,490,491,494],"blockquote",{},[10,492,493],{},"S0 阶段的 DoD：",[495,496,497,500,507,510],"ol",{},[248,498,499],{},"openapi\u002Fapi-v1.yaml 包含 spec 全部 11 个端点的字段级 schema，可被 swagger-ui 加载 ✓",[248,501,502,503,506],{},"backend\u002F Mock 服务 ",[105,504,505],{},"npm start"," 后能响应全部端点，返回符合契约的 mock 数据 ✓",[248,508,509],{},"launcher\u002F Rust 项目能编译为 launcher.exe，运行后完成所有阶段扫描并输出 diagnostics.json ✓",[248,511,512],{},"至少一个 end-to-end 冒烟测试通过（launcher 上报 → mock 后台收到 → 审计日志记录） ✓",[10,514,515,516,519],{},"每一条都能通过一个具体的命令来验证。\"能工作\"太模糊，\"运行 ",[105,517,518],{},"npm test"," 且所有测试通过\"才是可验证的。",[14,521,522],{"id":522},"流程失效的情况",[10,524,525,526,97],{},"这套流程在一个关键点会失效：",[35,527,528],{},"需求本身没想清楚时",[10,530,531],{},"我遇到过的例子是这样的。客户说\"要支持 USB 存储检测\"，这个需求很清楚，所以设计文档写了 20 多页，规划了三个阶段，列出了 30 多个 test case。然后两周后，客户补充说\"哦对了，还要处理网络驱动器\"。",[10,533,534],{},"这时前面的设计和计划都要回头改。检测逻辑复杂了，测试场景翻倍，阶段划分要调整。这不是流程的问题，这是需求的问题。流程本身反而帮助我及时暴露了这个风险 —— 如果没有设计文档，可能要到代码审查阶段，甚至系统上线以后才发现这个遗漏。",[10,536,537],{},"应对办法是在第 1 步（需求澄清）多花时间。列出你想到的所有场景，问\"还有其他我忽略的情况吗？\"。不是要求完全预测未来，而是把已知的不确定性显式写出来，而不是假设需求是固定的。",[10,539,540,541,544],{},"另一个失效的情况是",[35,542,543],{},"设计和实现的沟通不畅","。如果设计文档是给另一个人读的（或者给 AI 代理读的），但执行者没有理解透彻，实现出来会偏离设计。预防办法是在开始实施前，再过一遍设计文档，确认\"我清楚要做什么\"。",[14,546,547],{"id":547},"提交数字背后的故事",[10,549,550],{},"为什么能积累 7787 次提交？不是因为每次都在写新功能。真实的分布大概是：",[245,552,553,559,565,571],{},[248,554,555,558],{},[35,556,557],{},"功能实现","：40%",[248,560,561,564],{},[35,562,563],{},"设计文档编写和迭代","：25%",[248,566,567,570],{},[35,568,569],{},"测试编写","：20%",[248,572,573,576],{},[35,574,575],{},"bug 修复与回归测试","：15%",[10,578,579],{},"关键是这些提交都有上下文。每个提交的 message 都指向一个设计文档或一个 plan 的某个环节，或者一个 bug 记录。下次有问题要追查根因时，能快速定位到那个提交，看当时的设计决策是什么。",[10,581,582],{},"另外，有 80 份 bug 记录和 66 份设计 + 计划文档（30 specs + 36 plans）这件事本身说明了一点：文档不是负担，文档是工作的实际产出。代码只是文档的一个落地形式。",{"title":274,"searchDepth":275,"depth":275,"links":584},[585,593,598,599],{"id":317,"depth":275,"text":317,"children":586},[587,588,589,590,591,592],{"id":323,"depth":280,"text":324},{"id":336,"depth":280,"text":337},{"id":363,"depth":280,"text":364},{"id":376,"depth":280,"text":377},{"id":409,"depth":280,"text":410},{"id":430,"depth":280,"text":431},{"id":454,"depth":275,"text":454,"children":594},[595,596,597],{"id":457,"depth":280,"text":458},{"id":467,"depth":280,"text":468},{"id":480,"depth":280,"text":481},{"id":522,"depth":275,"text":522},{"id":547,"depth":275,"text":547},"2026-07-29",{},"\u002F2026-07-29-ai",{"title":309,"description":314},"2026-07-29-一年八千次提交AI辅助开发工作流","从需求澄清到故障归档，一套系统化的 AI 辅助开发流程：先设计文档过审，再分阶段实施验证，最后代码审查与故障存档，三条铁律支撑整个流程。",[607,608,609,610,611],"Claude","工作流","AI 辅助开发","代码审查","文档驱动","DN7sqbGmg2UybSvRXTMLW-dj0UURXET3KsSNeW_1V9I",{"id":614,"title":615,"body":616,"column":290,"date":744,"description":620,"extension":292,"hero_image":293,"meta":745,"navigation":295,"path":746,"seo":747,"series_id":293,"severity":293,"stem":748,"summary":749,"tags":750,"__hash__":756},"posts\u002F2026-07-20-远程桌面管理器DPAPI与60万次迭代.md","密码，不该由我保管",{"type":7,"value":617,"toc":738},[618,621,625,628,631,634,648,651,655,658,661,664,667,670,681,684,687,690,696,702,708,718,732,735],[10,619,620],{},"远程服务器资料管理工具需要妥善保存账户凭据。这个工具采用两层加密设计：本机存储用 Windows DPAPI，备份使用基于密码的密钥派生。两层各有边界，理解这些边界对使用决策至关重要。",[14,622,624],{"id":623},"第一层dpapi-的便利与代价","第一层：DPAPI 的便利与代价",[10,626,627],{},"本机存储的凭据使用 Windows DPAPI（Data Protection API）加密，由当前 Windows 用户身份保护。这是 Windows 内置的用户级加密机制，密钥由操作系统管理，与登录用户的 SID 和机器的本地安全数据库绑定。不需要用户记一个额外的主密码，启动应用即可直接使用保存的凭据。",[10,629,630],{},"DPAPI 的好处显而易见：启动应用即用，无需输入密码，用户体验最优。代价是它的加密密钥锁定在当前用户、当前机器。凭据无法直接迁移到另一台电脑或另一个 Windows 用户——这不是工具的限制，而是 DPAPI 的设计约束。Windows 操作系统就是这样设计的，其他工具也无法绕过。",[10,632,633],{},"实际操作中的含义很明确：",[245,635,636,639,642,645],{},[248,637,638],{},"重装 Windows 前必须先导出备份。原有凭据会因为用户 SID 变化和机器密钥更新而无法解密，即便登录同一账户也不行。",[248,640,641],{},"更换电脑前需要先创建备份并妥善保存备份密码，目标电脑上导入时需要重新输入这个备份密码。",[248,643,644],{},"在同一电脑上切换 Windows 用户登录，旧用户的凭据对新用户完全不可见，因为加密密钥是用户级的。",[248,646,647],{},"多用户共享一台电脑的场景下，凭据不会跨用户暴露。",[10,649,650],{},"这些限制会在实际使用中暴露出来——比如忙于工作时重装系统忽略了导出，或者在公用工作电脑上多个人使用。正因为如此，工具强制要求提供备份机制。备份不是可选功能，而是必需的。",[14,652,654],{"id":653},"第二层备份加密的固定迭代设计","第二层：备份加密的固定迭代设计",[10,656,657],{},"备份采用 PBKDF2-SHA256（基于密码的密钥派生函数 2，使用 SHA-256 哈希）加密，固定执行 600,000 次迭代。用户创建备份时设置一个密码，导入时输入这个密码。PBKDF2 通过重复应用哈希函数来增加破解难度，迭代次数越多，从密码派生密钥所需的计算时间越长，攻击者进行暴力破解也需要投入成倍的计算资源。",[10,659,660],{},"600,000 次迭代的来源是什么？这是一个有意识的设计决定，而不是随意选择。迭代次数越多，暴力破解的成本越高，但加密和解密的耗时也越长。设计者需要在两者之间找到平衡点：够强以抵御现代硬件的破解能力，又不能强到让普通用户的导入操作变得难以忍受。600,000 次这个数字反映的是这个平衡的结果。",[10,662,663],{},"为什么固定而非可配置？这是一个纪律问题。可配置听起来更灵活、更给用户掌控权，但在实践中会诱使用户为了更快的备份导入速度而降低迭代次数，从而削弱安全强度。安全不应该由便利让步。人们往往倾向于选择快速方案，尤其当他们没有安全专业知识时。固定的迭代次数消除了这种选择权，保证了所有备份都有相同的防护等级。工具的职责是做出最合理的决定，而不是把这个决定推给用户。",[14,665,666],{"id":666},"性能实测与数据解读",[10,668,669],{},"在 Windows 11 构建机上，对 1 MiB 大小的备份数据进行加密与解密的完整往返，预热缓存后的连续五次耗时分别为：108.919 ms、104.079 ms、100.418 ms、94.933 ms、103.470 ms。中位数为 103.470 ms。这意味着从你按下\"导入备份\"到凭据被解密并加载到内存，大约需要 100 毫秒的等待时间。",[10,671,672,673,676,677,680],{},"这组数据收集的目的是",[35,674,675],{},"记录性能表现","。它提供了一个具体的参考：用户在 Windows 11 系统上可以预期导入备份的延迟大约是这个量级。这对评估工具的可用性很有用。但它明确",[35,678,679],{},"不","作为调整迭代次数的依据。这种表述听起来有些冗余，但它是设计纪律的一部分——必须写下来的目的是防止后续有人看到\"100 毫秒确实有点慢\"就建议降低迭代次数。防止的是这样的推理：因为性能数据显示延迟不够快，所以降低迭代次数。这个逻辑链条在安全工程中是禁止的。",[10,682,683],{},"相反，如果实践证明 100 毫秒对用户体验构成问题，正确的做法是要么接受这个成本作为安全性的代价，要么在未来硬件更新换代后自然加速。绝不是削弱密钥派生强度。性能和安全的权衡应该在上层的需求决策中做，而不是在密码学参数中做。",[14,685,686],{"id":686},"工具的明确边界",[10,688,689],{},"这个工具的安全设计有明确的保护范围和限制：",[10,691,692,695],{},[35,693,694],{},"DPAPI 层的限制","：本机凭据的安全性最终依赖于 Windows 用户密码。如果 Windows 账户被破解，攻击者用该账户登录电脑，DPAPI 解密会自动进行。如果用户以管理员身份运行工具（虽然不需要管理员权限），攻击者获得管理员权限后理论上也可能绕过某些保护。安全链的强度由最弱的一环决定——如果你的 Windows 用户密码很弱，或者电脑物理上被他人访问，DPAPI 的保护就名存实亡。",[10,697,698,701],{},[35,699,700],{},"备份密码的强度","：导入备份时用户设置的密码决定了备份的抗暴力破解能力。PBKDF2 提供的防护再强，也无法弥补一个简单密码的缺陷。\"123456\"这样的备份密码，在 600,000 次迭代和现代 GPU 的破解能力面前，可能在几秒到几分钟内被破解。",[10,703,704,707],{},[35,705,706],{},"系统策略的约束","：工具运行在 Windows 系统上，不会绕过任何系统级的安全机制。Windows SmartScreen 对未签名程序的警告、远程桌面连接的安全确认对话、Group Policy 的限制——这些工具都无法绕过。安装包为未签名的内部制品，Windows SmartScreen 会在首次运行时显示\"未知发布者\"警告。这不是工具的缺陷，而是系统安全策略的正常行为。",[10,709,710,713,714,717],{},[35,711,712],{},"加密设计的范围","：这套两层加密设计防的是",[35,715,716],{},"离线攻击","——攻击者获得了备份文件或本机的加密数据，在没有用户交互的情况下尝试破解。它防不了的情况：",[245,719,720,723,726,729],{},[248,721,722],{},"备份密码通过社工或偷看被直接获取",[248,724,725],{},"备份文件在网络传输过程中被中间人截获（如果使用不安全的传输方式）",[248,727,728],{},"凭据被恶意软件在内存中窃取（工具启动后、密码解密到内存这段时间内）",[248,730,731],{},"Windows 账户本身被已经登录电脑的恶意软件控制",[10,733,734],{},"对这些风险的防护需要用户在安全习惯和网络安全措施上自行补足——设置强密码、在信任的网络上操作、定期更新系统补丁、使用反恶意软件工具。",[10,736,737],{},"使用这套工具前要明确：本机凭据带来了便利，代价是将安全依赖在 Windows 用户身份上；备份凭据提供了迁移能力，代价是密码强度必须由用户自己把关。都不是\"一次设置永久安全\"的方案，都需要持续的安全意识和维护。",{"title":274,"searchDepth":275,"depth":275,"links":739},[740,741,742,743],{"id":623,"depth":275,"text":624},{"id":653,"depth":275,"text":654},{"id":666,"depth":275,"text":666},{"id":686,"depth":275,"text":686},"2026-07-20",{},"\u002F2026-07-20-dpapi60",{"title":615,"description":620},"2026-07-20-远程桌面管理器DPAPI与60万次迭代","两层加密保护远程桌面凭据，本机用 DPAPI 便利性换易用性，备份用 PBKDF2 固定 60 万迭代；为什么迭代次数不可配置，性能数据如何解读。",[751,752,753,754,755],"Windows","DPAPI","PBKDF2","凭据存储","加密设计","TzKUoZoFLtAsO_VcB1TOOdWOpwcoDj6AUwEdS6d7pv0",{"id":758,"title":759,"body":760,"column":290,"date":880,"description":764,"extension":292,"hero_image":293,"meta":881,"navigation":295,"path":882,"seo":883,"series_id":293,"severity":293,"stem":884,"summary":885,"tags":886,"__hash__":892},"posts\u002F2026-07-17-把设计文档变成可讲的演示.md","转不成演示的设计文档，本来就没讲清",{"type":7,"value":761,"toc":874},[762,765,771,774,777,780,783,801,808,812,815,821,832,841,844,847,850,853,856,859,862,865,868,871],[10,763,764],{},"一年积累了 200 多份设计文档，最初想法是直接拿这些文档去讲。结果发现这些东西不适合讲。设计文档是给人写的，演示文稿是给人听的，形式完全不同。",[10,766,767,768,97],{},"解决这个问题的思路不是\"写个通用 PPT 编辑器\"，而是\"从结构化文档一键生成演示\"。本质差异在这里——编辑器要处理用户的任意编辑行为，演示工具只要转换",[35,769,770],{},"已有的结构",[14,772,773],{"id":773},"为什么选单向转换而不是编辑器",[10,775,776],{},"设计文档有稳定的模板：背景、方案、架构、取舍、参考。这个顺序不是随意的，恰好就是讲一个设计时的叙述顺序。",[10,778,779],{},"用户不需要\"先生成再改\"，需要的是\"文档秒变幻灯片\"。一旦你改，就回到编辑器的坑里去了——要支持拖拽、删除、排版，工作量爆炸，而且多数用户不会调，生成好的东西就是定版。",[10,781,782],{},"这个判断来自实际数据。我的工具做两个决策：",[495,784,785,795],{},[248,786,787,790,791,794],{},[35,788,789],{},"产物是自包含 HTML","，不是 Office 文件（",[105,792,793],{},".pptx"," 需要可编辑格式，门槛高；HTML 在浏览器里就能放映，自包含意味着内联了所有 CSS、JS、图片）。",[248,796,797,800],{},[35,798,799],{},"内容由 LLM 生成，不开放编辑面板","（用一个\"预览挑选\"的两阶段流程，让用户在 3 种风格的封面里选一个，然后生成整份）。",[10,802,803,804,807],{},"这两个约束听起来很严格，实际上契合了需求的本质：",[35,805,806],{},"文档的目的是记录决策，演示的目的是讲述决策","。不需要演示过程中再改决策，改了就回到文档去改。",[14,809,811],{"id":810},"什么样的结构能转什么样的不能","什么样的结构能转、什么样的不能",[10,813,814],{},"设计文档要是能一键转成演示，必须满足几个条件。",[10,816,817,820],{},[35,818,819],{},"架构图可以直接映射","。我的演示工具支持 AI 生图，也支持用户指定图片 URL。当文档里写了架构设计时，LLM 看到这个描述会生成对应的图，然后内联到演示文稿里。舞台是固定的 1920×1080，所有图片容器有最小尺寸约束（GSAP 时间轴动画要能在任意时间点求值，不能靠动态布局）。",[10,822,823,826,827,831],{},[35,824,825],{},"关键数据用表格记录最不容易犯错","。表格这种结构容易转化——",[828,829,830],"span",{},"待补：具体表格如何转成演示页的实现规则","。如果文档里的数据以段落文字形式写，转成演示时就卡住了，LLM 要从自然语言反推结构。",[10,833,834,837,838],{},[35,835,836],{},"取舍（trade-off）部分","：",[828,839,840],{},"待补：设计文档中的取舍说明如何映射成演示内容的规则",[10,842,843],{},"一个更深的观察：文档转不出来好演示，通常不是演示工具的问题，是文档本身没讲清楚。",[10,845,846],{},"我见过一类项目文档，有 20 多份模板文件，但只要换个配色其他全一样。这说明什么？说明那些\"模板\"实际上没有结构差异，只有视觉差异。结构不清，自然转不出演示。或者反过来说，如果要证明自己的文档结构是清晰的，试试能不能一键转成演示——转不出来就是信号，说明需要先梳理文档本身。",[14,848,849],{"id":849},"工具的硬约束",[10,851,852],{},"演示不同于其他产物，有独特的硬约束。我的工具选择了 1920×1080 的固定舞台，所有动画用 GSAP 时间轴。这些看起来像限制，实际上是为了保证可靠性。",[10,854,855],{},"固定舞台尺寸意味着设计者写风格预设时要算好留白和排版，不能寄希望于\"容器自适应就行了\"。GSAP 时间轴的特点是能在任意时间点求值（渲染管线会 seek 到任意帧截图），不能用 CSS animation 这种依赖真实时间流逝的东西。这限制了动效，但换来的是确定性——动效一定会在预期时间点发生，不会因为网络卡而错位。",[10,857,858],{},"生成流程分两个阶段，有个细节很实用。第一阶段只生成 3 个风格的封面单页，第二阶段才生成整份演示文稿。这个设计看似多一步，实际上优雅地解决了两个问题。一是给用户选择风格的机会（不是非此即彼的\"生成或不生成\"）；二是掩盖异步生图的等待时间（生图 1-2 分钟，正好被用户在选择封面时吸收了）。",[14,860,861],{"id":861},"从文档能否转演示看结构清晰度",[10,863,864],{},"最后回到起点。为什么要做这个工具？",[10,866,867],{},"表面原因是 200 多份文档用演讲方式讲会更有力。深层原因是这个过程本身就是对文档质量的检验。",[10,869,870],{},"一份设计文档如果结构清晰——背景交代得清，方案对比得充分，架构图画得明确，取舍理由说得透彻——那转成演示就是平移内容，不费劲。转不出来、或者转出来很别扭，就是信号，说明某个环节讲得不够好。",[10,872,873],{},"这个反馈机制比任何 review 注释都直白。\"这个表述为什么转不成幻灯片？\"往往能逼出真实的问题——\"哦，因为我其实还没想清楚这个方案为什么比另一个好\"。",{"title":274,"searchDepth":275,"depth":275,"links":875},[876,877,878,879],{"id":773,"depth":275,"text":773},{"id":810,"depth":275,"text":811},{"id":849,"depth":275,"text":849},{"id":861,"depth":275,"text":861},"2026-07-17",{},"\u002F2026-07-17",{"title":759,"description":764},"2026-07-17-把设计文档变成可讲的演示","不做通用PPT编辑器，只做文档→演示的单向转换；关键在识别文档的稳定结构。",[887,888,889,890,891],"演示文稿","文档结构","设计工具","HTML","GSAP","Reh87_TKXZ8nl5BhDhHzVOONAzwRS-Im8LTYrIfeuNk",1785406912235]