怎么用 这是格式与深度的参考,不是让你照抄的模板。数字全部是示例,直接抄进简历一问就穿——必须换成你自己的真实数据,而且要能说清是怎么测出来的。下面每个模块都有「数字是怎么测的」和「面试追问」两段,写不出那两段的模块就不要往简历上写

面试题库(电商 / 短视频 / 社区 / 本地生活 / 旅游 / 企业服务 多业务场景)

项目背景设定 内容社区的笔记发布器,uni-app 打 App 与小程序。用户在这里写图文笔记:多张图片、正文、插入话题与提到的人,然后发布。图片压缩与上传队列在 本地生活到店 那一页讲过(采集类上传),这一页讲的是创作类编辑——重点在编辑器本身、端上图片处理、以及「发布」这个跨越多个步骤的长动作。私信在 私信与聊天,社区后端在 社区互动与消息
为什么选这三个模块 发布器是社区产品里最不能出错的页面——用户在这里投入了几十分钟的创作,丢一次内容他可能就不再发了,而它同时是端上能力约束最紧的地方。三个模块:图文混排编辑器(uni-app 下没有可用的富文本方案,怎么用分块模型做出「所见即所得」的编辑,以及光标与插入这些细节)、端上图片处理(裁剪滤镜要在端上完成,Canvas 处理在低端机上是硬约束)、发布的可靠性(发布是「多图上传加内容提交」的组合动作,中断、失败、重复提交都要有明确行为,而且草稿不能丢)。

模块一:图文混排编辑器

  1. 图文混排编辑器(分块模型替代富文本 + 块级增删与稳定块 ID + 光标与键盘协同 + 话题与提及的插入回填 + 字数与内容校验)★★★
    简历这样写 uni-app 下的图文混排编辑器(放弃富文本改用「文本块 + 图片块」分块模型 + 块以稳定 ID 标识支持增删与重排 + 光标位置与软键盘高度协同保证输入区始终可见 + 话题与提及以标记块插入并回填光标 + 内容校验定位到具体块):uni-app 各端缺少统一可用的富文本编辑能力(可编辑区在小程序端受限、样式与光标行为跨端不一致),因此把内容模型改为分块结构——正文由若干「文本块」与「图片块」有序组成,每块以稳定 ID 标识,增删与重排只改块序列而不影响其他块的已输入内容;针对移动端输入的核心体验问题,监听软键盘高度并结合当前光标所在块调整滚动,保证正在输入的位置始终可见;话题与提及以标记块插入并在插入后回填光标到正确位置,避免插入后光标跳到开头;内容校验定位到具体块而非只提示「内容不合规」。改造后跨端的编辑行为由分块模型统一,输入区被键盘遮挡的问题由高度协同消除,插入话题后光标错位不再出现
    展开完整拆解
    为什么要这么设计

    第一版的思路很自然:做一个富文本编辑器,图片和文字混在一起编辑,所见即所得。做了一半就发现走不通。

    问题在于 uni-app 各端缺少统一可用的富文本编辑能力:可编辑区域在小程序端受限、不同端的样式渲染和光标行为不一致、图片插入后的排版在各端表现不同。我们花了不少时间在「让三端表现一致」上,而且每修一个端就在另一个端引入新问题。

    所以做了一个方向性的调整:放弃富文本,改用分块模型。

    内容由一个有序的块序列组成:文本块、图片块。每个块是一个独立的组件——文本块就是一个多行输入框、图片块就是一张图加操作按钮。用户看到的是它们竖排在一起,视觉上和富文本很接近,但技术上是完全可控的

    这个调整的收益远超预期:跨端一致性问题几乎全部消失(因为只用了最基础的输入框和图片组件)、块的增删重排变得简单、而且数据结构天然是结构化的(不需要解析 HTML)。代价是不能做「一段文字里某几个字加粗」这种行内富文本——但这在社区笔记里需求很弱,用户主要是纯文本加图片。

    块必须有稳定 ID,这是从别处学来的教训:用数组下标做键,增删块之后会串位(用户删掉中间一张图,后面几个块的内容会错乱)。

    第二个必须处理的是软键盘,这是移动端编辑器最核心的体验问题。用户在一个靠下的文本块里打字,软键盘弹出来把输入位置挡住了,他看不到自己在打什么。第一版只做了简单的页面上推,但在多块的长内容里,需要知道「光标在哪个块」才能算出正确的滚动位置。所以要监听软键盘高度,结合当前聚焦的块,把输入位置滚到可见区域

    第三个是插入话题与提及。用户点「#话题」按钮选一个话题,话题要插到光标当前位置,而且插入之后光标要回到话题后面。第一版的问题是插入后光标跳到了文本开头,用户要重新点一下才能继续打字——这个细节很小,但在一次创作里可能发生十几次。

    话题与提及在数据上不能只是一段文本(否则用户改掉几个字它就失效了),要作为带标识的标记存在。

    最后是内容校验要定位到具体块。「内容包含违规词」这样的提示没用,要指出是哪一段、最好高亮出来——用户的笔记可能有十几个块,让他自己找是不现实的。

    整体链路
    方向性调整:放弃富文本,改用分块模型 │ ├─ 第一版做富文本,做了一半发现走不通 │ uni-app 各端缺少统一可用的富文本编辑能力 │ 可编辑区在小程序端受限 │ 不同端的样式渲染与光标行为不一致 │ 图片插入后的排版各端表现不同 │ → 花了不少时间在「让三端一致」上 │ → 每修一个端就在另一个端引入新问题 │ └─ 分块模型 内容 = 有序的块序列(文本块 / 图片块) 每个块是独立组件 文本块 = 一个多行输入框 图片块 = 一张图 + 操作按钮 视觉上接近富文本,技术上完全可控 分块模型的收益(远超预期) ├─ 跨端一致性问题几乎全部消失 │ 因为只用了最基础的输入框与图片组件 ├─ 块的增删重排变得简单 └─ 数据结构天然是结构化的,不需要解析 HTML 代价(要主动说清) └─ 不能做「一段文字里某几个字加粗」这种行内富文本 但社区笔记里这个需求很弱 用户主要是纯文本 + 图片 块的稳定 ID(从别处学来的教训) ├─ 用数组下标做键,增删块之后会串位 └─ 用户删掉中间一张图,后面几个块的内容会错乱 软键盘协同(移动端编辑器最核心的体验问题) │ ├─ 用户在靠下的文本块里打字 │ 软键盘弹出把输入位置挡住,他看不到自己在打什么 │ ├─ 第一版只做简单的页面上推 │ 但多块长内容里,需要知道「光标在哪个块」 │ 才能算出正确的滚动位置 │ └─ 监听软键盘高度 + 当前聚焦块 → 把输入位置滚到可见区 插入话题与提及 │ ├─ 要插到光标当前位置 ├─ 插入后光标要回到插入内容之后 │ 第一版插入后光标跳到文本开头 │ 用户要重新点一下才能继续打字 │ → 细节很小,但一次创作里可能发生十几次 │ └─ 数据上不能只是一段文本 否则用户改掉几个字它就失效了 要作为带标识的标记存在 内容校验 ├─ 定位到具体块,最好高亮出来 ├─ 不要只提示「内容包含违规词」 └─ 笔记可能有十几个块,让用户自己找不现实 其他编辑能力 ├─ 块的拖拽重排(长按拖动调整图片顺序) ├─ 块的合并与拆分(在文本块中间插入图片要拆成两块) └─ 撤销:按块级操作记录,不做字符级撤销
    分步拆解
    1. 先判断富文本方案在 uni-app 下走不通,果断换方向。各端可编辑区能力受限、样式与光标行为不一致,我们在「让三端一致」上花了不少时间,每修一个端就在另一个端引入新问题
    2. 改用分块模型:内容是有序的块序列(文本块、图片块)。每块是独立组件,视觉上接近富文本、技术上完全可控
    3. 只用最基础的输入框与图片组件。这是跨端一致性问题消失的根本原因——基础组件的跨端行为最一致
    4. 主动说清代价:不能做行内富文本(一段文字里某几个字加粗)。社区笔记里这个需求很弱,用户主要是纯文本加图片。
    5. 块必须有稳定 ID,不能用数组下标。否则用户删掉中间一张图,后面几个块的内容会错乱
    6. 监听软键盘高度并结合当前聚焦的块调整滚动。只做简单的页面上推在多块长内容里算不出正确位置
    7. 保证「正在输入的位置」始终可见,而不是「输入框」可见。长文本块里光标可能在中间,把输入框顶部滚到可见区是不够的
    8. 插入话题与提及要插到光标当前位置。不是插到末尾——用户可能想在句子中间插。
    9. 插入后光标要回填到插入内容之后。第一版光标跳到文本开头,用户要重新点一下才能继续打字,一次创作里发生十几次。
    10. 话题与提及在数据上要作为带标识的标记,不能只是文本。否则用户改掉几个字它就失效了
    11. 内容校验要定位到具体块并高亮。「内容包含违规词」这样的提示没用,笔记有十几个块,让用户自己找不现实
    12. 支持块的拖拽重排。长按拖动调整图片顺序是这类编辑器的基本能力。
    13. 支持块的合并与拆分。在文本块中间插入图片时要把它拆成两个文本块,删除中间图片时可考虑合并回一块。
    14. 撤销按块级操作记录,不做字符级撤销。字符级撤销在分块模型下要跨块处理,复杂度不值得;块级(撤销删图、撤销插入)覆盖了用户的主要需求。
    15. 失焦不要丢状态。用户切到相册选图再回来,各块的内容与光标位置都要还在
    关键决策与取舍

    放弃富文本改用分块模型,这是这个模块唯一真正重要的决策,而它是一个「减法」。坚持富文本的话,理论上能做出更自由的排版;但在 uni-app 的多端约束下,为了「让三端表现一致」要付出的成本是持续的、而且没有终点——每个端的可编辑区行为都在各自演进。分块模型用「只使用最基础的组件」换来了跨端一致性,代价是失去行内富文本能力判断依据是「用户真正需要的表达能力是什么」:社区笔记的主体是纯文本加图片,行内加粗这类需求很弱。我认为这个决策值得讲,因为它体现的是「先确认需求的真实边界,再选技术方案」,而不是先追求技术上的完备。

    撤销只做块级不做字符级。字符级撤销更完整,但在分块模型下它要跨块处理(跨块的删除、块的合并拆分),复杂度陡增;而且各端输入框的原生撤销行为不一致,我们的撤销会和原生撤销打架。块级撤销(撤销删图、撤销插入话题、撤销块重排)覆盖了用户真正会后悔的操作——字符级的输入错误用户直接删掉重打就行,而误删一张图是他没法自己恢复的取舍依据是「哪些操作用户无法自己恢复」,这些才需要撤销支持。

    软键盘协同要基于「光标位置」而不是「输入框位置」。简单做法是把聚焦的输入框滚到键盘上方,实现容易。但长文本块里光标可能在块的中间甚至底部,把输入框顶部滚到可见区,光标仍然被挡住。所以要拿到光标的实际位置。代价是各端获取光标位置的能力不同,有的端拿不到精确值,只能按行高估算。缓解手段是估算加一点余量——宁可多滚一点让输入位置在屏幕中部,也不要正好卡在键盘边缘。

    踩过的坑:坚持做富文本,在三端一致性上耗了很久最后推翻重做。可编辑区在小程序端受限、光标行为跨端不一致、图片插入后排版各端不同,每修一个端就在另一个端引入新问题。最后改成分块模型,之前的工作大部分作废教训是:在多端框架下选技术方案,要先确认「这个能力在所有目标端上是否都有稳定一致的实现」——如果没有,再优雅的方案也会变成持续的填坑,而换一个「能力更弱但各端都稳」的方案往往整体更快。

    踩过的坑二:块用数组下标做键,删除中间图片后内容错乱。用户删掉中间一张图,后面几个文本块的内容整体串位,他辛苦写的段落对应到了错的位置。修法是块用稳定 ID教训和 SKU 矩阵、楼层排序、配置矩阵完全一样:任何会被增删重排的列表,键必须来自数据本身而不是它在数组里的位置——这个错误我在四个不同场景里都遇到过,已经是我写任何列表渲染时的固定检查项。

    踩过的坑三:插入话题后光标跳到开头。用户在句子中间点「#话题」选完之后,光标回到了整个文本块的开头,他要重新点到刚才的位置才能继续打字。这个细节看起来很小,但一次创作里插入十几个话题就是十几次打断,用户的反馈是「这个编辑器很难用」但说不出具体哪里难用。修法是插入后回填光标到插入内容之后教训是:影响「操作连续性」的小问题,用户感受到的是整体难用而不是具体缺陷——所以这类问题很难从反馈里定位,要靠自己完整走一遍真实创作流程才能发现。

    踩过的坑四:切到相册选图再回来,编辑内容丢了。用户点插入图片、切到系统相册、选完回来,页面被重建,已输入的内容全没了。修法是切走前保存状态、返回时恢复(并且这也是模块三草稿机制的一部分)。教训是:任何会导致页面失去焦点或被系统回收的操作(选图、拍照、跳转授权、切后台),都必须视为「页面可能被重建」并保护状态——而这些操作在创作类页面里恰恰是最高频的。

    没做的部分:没做行内富文本(加粗、变色、链接)。这是分块模型的固有限制,产品评估后接受。也没做视频块(笔记里插视频),需要视频上传与封面选择,属于另一条链路,当时只支持图片。

    数字是怎么测的

    跨端一致性:这一项用「需要端特定处理的代码量」或「跨端不一致的缺陷数」来度量,改造前后对比。改造后应该显著下降,因为只用了基础组件不要报「100% 一致」——基础组件也有细微差异。

    输入区被遮挡:确定性验证——在长内容的最后一个块、以及长文本块的中间位置分别输入,检查光标位置始终可见两个场景都要测,因为后者是简单方案会漏掉的。

    块增删后的内容错位:确定性验证——删除中间的块、插入新块、拖拽重排,检查其余块的内容原样保留。这是踩坑后固化的用例。

    光标回填:确定性验证——在文本块中间插入话题与提及,检查光标停在插入内容之后

    失焦恢复:确定性验证——切到相册选图后返回、切后台后返回,检查各块内容与光标位置都还在

    创作流程的完整走查:是否做了完整创作流程的人工走查以及发现的问题数这一条值得单独说——像「插入话题后光标跳开头」这类问题不会出现在功能测试用例里,只有完整走一遍真实创作才会发现

    不要报「编辑器性能提升 N%」。分块模型的收益主要是跨端一致性与可维护性,不是性能,硬报性能会被追问。正确表述是「从富文本改为分块模型后端特定代码与跨端缺陷的变化、行内富文本能力的取舍与依据、遮挡与错位与光标回填由确定性用例保证」。

    面试追问
    Q:uni-app 里怎么做图文混排的编辑器? A:我的结论是不做富文本,改用「文本块 + 图片块」的分块模型——这是个减法决策。第一版做富文本,做了一半发现走不通:uni-app 各端可编辑区能力受限(小程序端尤其)、样式渲染与光标行为跨端不一致、图片插入后排版各端表现不同,我们在「让三端一致」上花了不少时间,每修一个端就在另一个端引入新问题,最后推翻重做、之前的工作大部分作废。分块模型是:内容 = 有序的块序列,每块是独立组件(文本块就是一个多行输入框、图片块就是一张图加操作按钮),视觉上接近富文本、技术上完全可控收益远超预期:跨端一致性问题几乎全部消失(因为只用了最基础的输入框与图片组件,而基础组件的跨端行为最一致)、块的增删重排变简单、数据结构天然结构化不需要解析 HTML代价要主动说清:不能做行内富文本(一段文字里某几个字加粗),但社区笔记里这个需求很弱教训是:在多端框架下选技术方案,要先确认「这个能力在所有目标端上是否都有稳定一致的实现」——没有的话,换一个「能力更弱但各端都稳」的方案往往整体更快。
    Q:软键盘弹出来把输入位置挡住了怎么办? A:关键是要基于「光标位置」调整滚动,而不是基于「输入框位置」——这是简单方案会漏掉的。简单做法是把聚焦的输入框滚到键盘上方,实现容易;但长文本块里光标可能在块的中间甚至底部,把输入框顶部滚到可见区,光标仍然被挡住。所以要监听软键盘高度、结合当前聚焦的块和光标的实际位置,把正在输入的位置滚到可见区。代价是各端获取光标位置的能力不同,有的端拿不到精确值只能按行高估算;缓解手段是估算加一点余量——宁可多滚一点让输入位置在屏幕中部,也不要正好卡在键盘边缘验证时两个场景都要测:在长内容的最后一个块输入,以及在长文本块的中间位置输入——后者是简单方案会漏的还有一个相关的高频问题:用户点插入图片、切到系统相册、选完回来,页面被重建、已输入内容全没了教训是:任何会导致页面失焦或被系统回收的操作(选图、拍照、跳授权、切后台)都必须视为「页面可能被重建」并保护状态——而这些操作在创作类页面里恰恰最高频。
    Q:用户删掉中间一张图,后面的内容会不会乱? A:如果块用数组下标做键就会乱,我们踩过:用户删掉中间一张图,后面几个文本块的内容整体串位,他辛苦写的段落对应到了错的位置。修法是每个块有稳定 ID,增删与重排只改块序列,不影响任何块的已输入内容教训和我在 SKU 矩阵编辑器、楼层搭建、城市配置矩阵上踩的完全一样:任何会被增删重排的列表,键必须来自数据本身而不是它在数组里的位置——这个错误我在四个不同场景里都遇到过,已经成了我写任何列表渲染时的固定检查项顺带说两个和块模型相关的能力块的合并与拆分——在文本块中间插入图片时要把它拆成两个文本块,删除中间图片时可考虑合并回一块;撤销只做块级不做字符级——字符级在分块模型下要跨块处理、复杂度陡增,而且会和各端输入框的原生撤销打架。依据是「哪些操作用户无法自己恢复」:字符级的输入错误用户删掉重打就行,而误删一张图是他没法自己恢复的,这些才需要撤销支持。
    Q:插入话题这类功能有什么坑? A:两个坑,一个是光标、一个是数据形态。光标这个坑看起来很小但影响很大:用户在句子中间点「#话题」选完之后,第一版光标回到了整个文本块的开头,他要重新点到刚才的位置才能继续打字。一次创作里插入十几个话题就是十几次打断,而用户的反馈是「这个编辑器很难用」但说不出具体哪里难用。修法是插入后回填光标到插入内容之后教训是:影响「操作连续性」的小问题,用户感受到的是整体难用而不是具体缺陷——所以这类问题很难从反馈里定位,要靠自己完整走一遍真实创作流程才能发现。我后来把「完整走查创作流程」当成了必做项。数据形态这个坑是:话题与提及不能在数据上只存成一段文本,否则用户改掉几个字它就失效了(发布后话题关联不上、被提到的人收不到通知),要作为带标识的标记存在还有一条容易漏:内容校验要定位到具体块并高亮——「内容包含违规词」这样的提示没用,笔记有十几个块,让用户自己找不现实

模块二:端上图片处理

  1. 端上图片处理(先降采样再处理 + 处理链一次成像 + 参数化预览与提交时统一出图 + 按机型限制并发与尺寸 + 处理失败降级为原图)★★★
    简历这样写 笔记图片的端上编辑与处理(处理前按目标尺寸降采样 + 裁剪与滤镜合成为一次成像而非逐步叠加导出 + 预览阶段只保存参数、提交时统一出图 + 按机型限制并发处理数与最大边长 + 处理失败降级为使用原图并提示 + 处理结果与原图分别保留以支持重新编辑):笔记需要在端上完成裁剪、旋转、滤镜等处理,早期每一步操作都基于上一步的输出重新导出一张图,在低端机上多图连续处理导致内存飙升与应用被回收,且反复导出造成画质持续劣化;因此改为参数化处理——预览阶段只记录裁剪框与滤镜参数并用轻量方式实时预览,提交时基于原图一次性合成出图;处理前先按目标尺寸降采样(避免在原始分辨率上做运算),并按机型限制并发处理数与最大边长;处理失败时降级为使用原图并明确提示而非中断发布;同时保留原图与处理参数以支持用户重新编辑。改造后多图处理的内存峰值由随图片数量累积改为受并发上限约束,反复编辑造成的画质劣化因一次成像而消除
    展开完整拆解
    为什么要这么设计

    社区笔记的图片要在端上做处理:裁剪成合适的比例、旋转、加滤镜。这些必须在端上做(用户要立刻看到效果),而端上做图像处理有很硬的资源约束

    第一版的实现是最直观的「逐步处理」:用户裁剪 → 导出一张裁剪后的图;用户加滤镜 → 基于裁剪后的图再导出一张。每一步操作都产生一张新图

    问题有三个,而且都很实际。

    第一是内存。在原始分辨率上做运算(现在手机随手一拍就是很大的图),一张图的处理过程就要占相当可观的内存;而笔记通常是多图,用户连续处理五六张,内存持续累积,低端机上应用直接被系统回收——用户辛苦编辑的内容全丢了。

    第二是画质持续劣化。每一步都重新导出一次,而导出通常伴随有损压缩。裁剪、旋转、滤镜三步下来就压了三次,用户看到的成品明显不如原图清晰,而他不知道为什么。

    第三是慢。每次调滤镜参数都要重新导出一张,用户拖动滤镜强度的滑块时,界面是一顿一顿的

    三个问题指向同一个改法:参数化处理加一次成像。

    预览阶段只记录参数(裁剪框位置、旋转角度、滤镜类型与强度),用轻量方式实时预览(比如用 CSS 滤镜或低分辨率的预览图),不产生任何中间产物用户拖滑块时是流畅的,因为压根没在导出图片。

    提交时基于原图一次性合成出图:把裁剪、旋转、滤镜合成为一次运算、一次导出。只压缩一次,画质最好;只占用一次内存峰值。

    另外三个必须做的约束。

    处理前先降采样。目标图片可能只需要一千多像素宽,而原图可能是四千像素——在原始分辨率上做运算是纯浪费先按目标尺寸降采样再处理,内存和耗时都是量级的改善。

    按机型限制并发处理数与最大边长。多图不能同时处理,低端机上只能一张一张来;最大边长也要按机型限制。

    处理失败要降级而不是中断。端上图像处理在低端机或异常情况下会失败,此时应该降级为使用原图并明确提示「滤镜未能应用」,而不是让整个发布流程卡住——用户的目的是发笔记,不是加滤镜。

    最后一个容易漏的:要保留原图与处理参数,这样用户重新编辑时能回到调整前的状态,而不是在已处理的图上二次处理(那会再劣化一次)。

    整体链路
    第一版「逐步处理」:每一步操作产生一张新图 ├─ 裁剪 → 导出一张裁剪后的图 └─ 加滤镜 → 基于裁剪后的图再导出一张 三个实际问题 │ ├─ 内存 │ 在原始分辨率上做运算(手机随手一拍就很大) │ 一张图的处理就要占相当可观的内存 │ 笔记通常是多图,连续处理五六张内存持续累积 │ → 低端机上应用被系统回收 │ → 用户辛苦编辑的内容全丢了 │ ├─ 画质持续劣化 │ 每一步重新导出,而导出通常伴随有损压缩 │ 裁剪 + 旋转 + 滤镜 = 压了三次 │ → 成品明显不如原图清晰,而用户不知道为什么 │ └─ 慢 每次调滤镜参数都要重新导出一张 → 拖动滤镜强度滑块时界面一顿一顿的 改法:参数化处理 + 一次成像 │ ├─ 预览阶段只记录参数 │ 裁剪框位置、旋转角度、滤镜类型与强度 │ 用轻量方式实时预览(CSS 滤镜 / 低分辨率预览图) │ 不产生任何中间产物 │ → 拖滑块流畅,因为压根没在导出图片 │ └─ 提交时基于原图一次性合成出图 裁剪 + 旋转 + 滤镜 合成为一次运算、一次导出 → 只压缩一次,画质最好 → 只占用一次内存峰值 三个必须做的约束 │ ├─ 处理前先降采样 │ 目标图可能只需一千多像素宽,原图可能四千像素 │ 在原始分辨率上做运算是纯浪费 │ → 先按目标尺寸降采样再处理 │ → 内存与耗时都是量级的改善 │ ├─ 按机型限制并发处理数与最大边长 │ 多图不能同时处理,低端机上一张一张来 │ └─ 处理失败要降级不要中断 端上图像处理在低端机或异常情况下会失败 降级为使用原图 + 明确提示「滤镜未能应用」 → 用户的目的是发笔记,不是加滤镜 保留原图与参数(容易漏) ├─ 用户重新编辑时回到调整前的状态 └─ 而不是在已处理的图上二次处理(会再劣化一次) 其他细节 ├─ 多图的顺序调整不涉及重新处理,只改顺序 ├─ 处理进度要可见,多图处理时给整体进度 └─ 处理期间不要阻塞用户继续编辑文字
    分步拆解
    1. 先认清端上图像处理有很硬的资源约束。手机随手一拍就是很大的图,在原始分辨率上做运算,一张就要占相当可观的内存
    2. 放弃「逐步处理」,改为参数化处理加一次成像。逐步处理的三个问题(内存累积、画质劣化、卡顿)指向同一个改法
    3. 预览阶段只记录参数,不产生中间产物。裁剪框位置、旋转角度、滤镜类型与强度。
    4. 预览用轻量方式实现(CSS 滤镜或低分辨率预览图)。这样用户拖滤镜滑块时是流畅的,因为压根没在导出图片
    5. 提交时基于原图一次性合成出图。裁剪、旋转、滤镜合成为一次运算一次导出——只压缩一次画质最好,只占用一次内存峰值
    6. 处理前先按目标尺寸降采样。目标可能只需一千多像素宽而原图四千像素,在原始分辨率上运算是纯浪费,降采样后内存与耗时都是量级改善。
    7. 按机型限制并发处理数。多图不能同时处理,低端机上一张一张来
    8. 按机型限制最大边长。低端机上即使降采样也要更保守。
    9. 处理失败要降级为使用原图并明确提示,不要中断发布。用户的目的是发笔记,不是加滤镜——因为滤镜失败就让他发不出去是本末倒置。
    10. 保留原图与处理参数。用户重新编辑时回到调整前的状态,而不是在已处理的图上二次处理(那会再劣化一次)。
    11. 多图的顺序调整不触发重新处理。只改顺序,不要因为拖动排序就把所有图重新处理一遍
    12. 多图处理时给整体进度。「正在处理第 3 张,共 6 张」比一个转圈有用得多。
    13. 处理期间不要阻塞用户继续编辑文字。处理是后台任务,用户可以一边等一边写正文
    14. 处理结果要有缓存,避免重复处理。参数没变时不要重新出图。
    15. 上报处理耗时与失败率并按机型分组。低端机才是约束条件,均值会把它平掉
    关键决策与取舍

    参数化处理加一次成像,这是这个模块的核心决策,而它同时解决了三个看起来不相关的问题。内存累积、画质劣化、拖动卡顿——三个问题的共同根源是「每一步操作都产生一张新图」。改成参数化之后三个问题一起消失。取舍是实现复杂度上升:要维护一套参数模型、要让预览效果和最终出图效果一致(这是最容易出问题的地方——预览用 CSS 滤镜、出图用 Canvas 运算,两者的算法不同会导致「预览好看但出图不一样」)。缓解手段是把滤镜参数定义成两边都能实现的形式,并做预览与成品的一致性对比测试。

    「预览与出图效果必须一致」是这个方案最大的隐藏风险,值得单独说。用户按预览效果调好了参数,如果出图不一样,他会认为是 bug 而且很难接受(他调了很久)。所以滤镜的定义要选择在预览通道和出图通道都能精确实现的形式,而不是各自用「差不多的效果」。验证上要做预览截图与成品出图的对比,这是必须有的测试项。

    处理失败降级为原图而不是中断,依据是「用户的真实目的」。技术上失败了就报错最直接。但用户来这里的目的是发笔记,滤镜是锦上添花因为滤镜失败让他发不出去,是把次要目标凌驾于主要目标之上。降级加明确提示既保住了主流程,又诚实告知了结果。这个判断的一般形式是:辅助功能的失败不应该阻断主流程,但必须明确告知而不是静默降级。

    踩过的坑:逐步处理导致低端机上应用被回收,用户编辑的内容全丢。用户连续处理五六张图,每一步都在原始分辨率上导出新图,内存持续累积,最后应用被系统杀掉——而当时草稿机制还不完善,他写了半小时的笔记全没了。修法是参数化加一次成像加降采样加并发限制教训是:端上的图像处理必须按「最差的目标机型」设计,而不是按开发机——而且内存问题的后果不是「慢一点」而是「进程被杀」,它会直接摧毁用户的工作成果

    踩过的坑二:反复导出导致画质明显劣化,用户投诉「你们把我的图弄糊了」。裁剪、旋转、滤镜三步各导出一次,每次有损压缩叠加起来非常明显,用户对比原图后来投诉。修法是一次成像只压缩一次教训是:有损操作不能串联——每一步单独看损失都可以接受,串起来就不可接受了,所以要把多步操作合并成一次运算。

    踩过的坑三:预览用 CSS 滤镜、出图用 Canvas 运算,两者效果不一致。用户按预览调好参数,出图之后颜色明显不同,他反复调、反复发现不对。这个坑比画质劣化更让人恼火,因为它让「调整」这件事失去了意义。修法是把滤镜定义成两边都能精确实现的形式,并加预览与成品的一致性对比测试教训是:所见即所得的前提是「所见」和「所得」用的是同一套算法或严格等价的算法——否则预览就是在骗用户。

    踩过的坑四:重新编辑时在已处理的图上二次处理。用户发布前想调一下滤镜,我们把已经处理过的图作为输入再处理一次,结果又劣化了一次而且裁剪没法撤回。修法是保留原图与处理参数,重新编辑时回到参数状态而不是图片状态教训是:参数化处理的完整形态包括「保留原始输入」——只保留参数不保留原图,参数就没法重新应用。

    没做的部分:没做贴纸与文字水印的端上合成。需求存在但涉及字体渲染与图层管理,在多端上一致性成本高,当时只做了裁剪旋转滤镜。也没做智能裁剪(自动识别主体裁出合适构图),需要模型能力。

    数字是怎么测的

    内存峰值:最该报的数字。报连续处理指定数量图片时的内存峰值与曲线,改造前是随图片数量累积、改造后是受并发上限约束的平稳峰值必须说明机型、图片原始分辨率、处理步骤数

    应用被回收:处理多图时应用被系统回收的发生次数,改造前后对比。这个数字比内存数值更能说明严重性——它对应的是「用户内容丢失」。

    画质:压缩次数从三次降为一次这个结构性变化,以及成品与原图的对比方式(人工目视对比加抽样)。不要编一个「画质提升 N%」,画质没有公认的单一数值指标。

    预览流畅度:拖动滤镜强度滑块时的帧率,改造前后对比。改造前每次调整都在导出图片,改造后压根没有导出动作

    预览与成品一致性:确定性验证——对每种滤镜做预览截图与成品出图的对比这是必须有的测试项,因为它决定了「所见即所得」这个承诺是否成立。

    处理耗时与失败率:按机型分组报。低端机才是约束条件,均值会把它平掉。失败率要配合降级路径的效果一起报。

    降级路径:确定性验证——模拟处理失败,检查降级为使用原图、发布流程不中断、且有明确提示。三项都要验。

    不要报「支持任意分辨率图片处理」。端上内存是硬约束,我们本来就限制了最大边长正确表述是「内存峰值从随数量累积改为受并发上限约束、压缩次数从三次降为一次、预览与成品一致性由对比测试保证、处理失败降级为原图不阻断发布」。

    面试追问
    Q:裁剪、旋转、滤镜这些处理在端上怎么做? A:关键是「参数化处理加一次成像」,而不是每一步操作都产生一张新图。第一版是逐步处理(裁剪导出一张、加滤镜再基于它导出一张),三个问题一起来了内存(在原始分辨率上运算,多图连续处理累积到低端机上应用被系统回收,用户写了半小时的笔记全没了)、画质持续劣化(每步导出都有损压缩,三步压三次,用户对比原图后投诉「你们把我的图弄糊了」)、(每次调滤镜参数都重新导出,拖滑块时界面一顿一顿)。三个问题的共同根源是「每一步操作都产生一张新图」,所以改法只有一个:预览阶段只记录参数(裁剪框、旋转角度、滤镜类型与强度)并用轻量方式实时预览、不产生任何中间产物提交时基于原图一次性合成出图另外必须先降采样——目标图可能只需一千多像素宽而原图四千像素,在原始分辨率上运算是纯浪费教训是:有损操作不能串联,每一步单独看损失可接受、串起来就不可接受。
    Q:预览看到的效果和最终出图不一样,怎么办? A:这是参数化方案最大的隐藏风险,我们踩过,而且比画质劣化更让人恼火。我们预览用 CSS 滤镜、出图用 Canvas 运算,两套算法不同,用户按预览调好参数,出图之后颜色明显不同,他反复调、反复发现不对这个坑的严重性在于它让「调整」这件事失去了意义——用户花时间调的是一个假的效果。修法是把滤镜定义成在预览通道和出图通道都能精确实现的形式,而不是各自用「差不多的效果」;并且加预览截图与成品出图的一致性对比测试,对每种滤镜都做教训是:所见即所得的前提是「所见」和「所得」用的是同一套算法或严格等价的算法——否则预览就是在骗用户。还有一个相关的坑:用户发布前想调一下滤镜,我们把已经处理过的图作为输入再处理一次,结果又劣化了一次而且裁剪没法撤回修法是保留原图与处理参数,重新编辑时回到参数状态而不是图片状态——参数化处理的完整形态包括「保留原始输入」,只保留参数不保留原图,参数就没法重新应用。
    Q:低端机上处理多图撑不住怎么办? A:三条约束一起上,而且要按「最差的目标机型」设计而不是按开发机。一、处理前先按目标尺寸降采样——这是收益最大的一条,内存与耗时都是量级的改善二、按机型限制并发处理数:多图不能同时处理,低端机上一张一张来三、按机型限制最大边长,低端机上即使降采样也要更保守。我们踩过的坑后果很严重:连续处理五六张图,内存持续累积到应用被系统杀掉,而当时草稿机制还不完善,用户写了半小时的笔记全没了教训是:内存问题的后果不是「慢一点」而是「进程被杀」,它会直接摧毁用户的工作成果——所以在创作类页面里它的优先级比一般性能问题高得多。配套的体验设计也要做多图处理时给整体进度(「正在处理第 3 张,共 6 张」比一个转圈有用得多);处理期间不要阻塞用户继续编辑文字(处理是后台任务,用户可以一边等一边写正文);多图的顺序调整不要触发重新处理,只改顺序。
    Q:图片处理失败了怎么办? A:降级为使用原图并明确提示,绝不中断发布流程——依据是「用户的真实目的」。端上图像处理在低端机或异常情况下会失败。技术上失败了就报错最直接,但用户来这里的目的是发笔记,滤镜是锦上添花因为滤镜失败让他发不出去,是把次要目标凌驾于主要目标之上。所以降级为原图,同时明确提示「滤镜未能应用」——既保住了主流程,又诚实告知了结果这个判断的一般形式是:辅助功能的失败不应该阻断主流程,但必须明确告知而不是静默降级。「明确告知」这半句很重要:静默降级会让用户以为滤镜生效了,发出去之后才发现不对,那比直接告诉他更糟验证上这一项要做成确定性用例:模拟处理失败,检查降级为使用原图、发布流程不中断、且有明确提示——三项都要验,因为很容易做到前两项而漏掉提示度量上处理耗时与失败率都要按机型分组报,低端机才是约束条件,均值会把它平掉。

模块三:发布的可靠性与草稿

  1. 发布的可靠性与草稿(发布任务化与可恢复 + 已上传图片复用不重传 + clientId 幂等防重复发布 + 草稿自动保存与冲突处理 + 失败原因分层可操作)★★★
    简历这样写 笔记发布的可靠性与草稿保护(发布拆为任务并持久化进度以支持中断后继续 + 已成功上传的图片记录凭据复用不重传 + 以 clientId 为幂等键防止重复发布 + 草稿定时与关键节点自动保存并处理多端冲突 + 失败按原因分层给出可操作提示 + 退出前的未保存提醒):发布是「多图上传加内容提交」的组合长动作,中途切后台、断网、被系统回收都可能中断,早期一旦中断需从第一张图重新上传且重复点击会产生多条重复笔记;因此把发布拆为可持久化的任务(每张图的上传结果与整体阶段落盘),中断恢复时复用已成功上传的图片凭据只续传剩余部分;提交以clientId 为幂等键,重复提交由服务端去重而非依赖前端禁用按钮;草稿在定时与关键节点(切后台、插入图片前、退出前)自动保存,并对多端草稿做冲突提示由用户选择而非静默覆盖;失败按原因分层给出可操作提示(网络失败可重试、内容违规定位到具体块、图片过大提示重选)。改造后中断后的重复上传由凭据复用消除,重复发布由幂等键拦下,「写了半小时内容丢失」的反馈由多节点自动保存消除
    展开完整拆解
    为什么要这么设计

    这个模块的出发点是一句话:发布器是社区产品里最不能出错的页面。用户在这里投入了几十分钟的创作,丢一次内容他可能就不再发了——而创作者流失对社区的伤害是长期的。

    而「发布」这个动作恰恰是最容易中断的:它不是一次请求,是「上传 N 张图 + 提交内容」的组合长动作,可能持续几十秒到几分钟。这期间用户可能切后台去回个消息、可能进电梯断网、可能被系统回收进程。

    第一版的问题很集中。

    第一是中断后从头再来。用户发九张图的笔记,传到第七张时断网了,恢复后要从第一张重新传。移动网络下这是很实际的流量与时间浪费,而且用户会反复失败反复重试。

    第二是重复发布。用户点了发布没反应(其实在传图),他又点了一次,结果发出了两条一样的笔记。第一版靠「点击后禁用按钮」防重,但页面被重建后按钮又是可点的,防不住。

    第三也是最严重的:内容丢失。用户写了半小时,切到相册选图时页面被重建、或者处理图片时应用被回收,内容全没了。这类反馈的情绪是最强的。

    所以四个设计。

    第一是把发布做成可持久化的任务。不是一个「点了就等」的请求,而是一个有阶段、有进度、进度落盘的任务:每张图上传成功就把它的上传凭据记下来落盘,整体阶段(准备中 / 上传中 / 提交中 / 已完成)也落盘。中断恢复时读回任务状态,只续传剩余部分。

    第二是幂等。发布提交带一个端上生成的 clientId重复提交由服务端根据它去重不能只依赖前端禁用按钮——页面重建、用户从草稿再次发布、网络重试,都会绕过按钮状态。这和 IM 消息用 clientMsgId 幂等是同一个模式:幂等键要绑「业务上的同一次操作」而不是「同一次点击」。

    第三是草稿的多节点自动保存。不只定时保存,还要在关键节点保存:切后台前、插入图片前(因为要跳到相册)、退出页面前这几个节点正好覆盖了页面最可能被重建的时刻。

    草稿还要处理多端冲突:用户在手机上存了草稿,又在另一台设备编辑过。不要静默覆盖——提示用户「云端有一份更新的草稿」让他选。因为两份都是他的创作,静默丢掉任何一份都不可接受。

    第四是失败原因分层且可操作。「发布失败」这样的提示没用。网络失败 → 可重试(而且是续传不是重传);内容违规 → 定位到具体块;图片过大或格式不支持 → 提示重选那一张;账号受限 → 说明原因每种失败都要有明确的下一步。

    最后一个小但重要的:退出前提醒未保存。用户误点返回时要问一句,而且要提供「保存草稿」而不只是「确认离开」

    整体链路
    出发点:发布器是最不能出错的页面 ├─ 用户在这里投入了几十分钟的创作 ├─ 丢一次内容他可能就不再发了 └─ 而创作者流失对社区的伤害是长期的 而发布恰恰最容易中断 ├─ 它不是一次请求,是「上传 N 张图 + 提交内容」的组合长动作 ├─ 可能持续几十秒到几分钟 └─ 这期间:切后台回消息 / 进电梯断网 / 被系统回收 第一版的三个问题 ├─ 中断后从头再来 │ 九张图传到第七张断网,恢复后从第一张重传 │ 移动网络下是实际的流量与时间浪费 ├─ 重复发布 │ 点了没反应(其实在传图),又点一次 → 发出两条 │ 靠「点击后禁用按钮」防重,页面重建后按钮又可点 └─ 内容丢失(最严重) 写了半小时,切相册选图时页面被重建 → 全没了 或处理图片时应用被回收 → 全没了 → 这类反馈的情绪是最强的 一、发布做成可持久化的任务 │ ├─ 不是「点了就等」的请求 ├─ 是有阶段、有进度、进度落盘的任务 │ 每张图上传成功 → 记下上传凭据并落盘 │ 整体阶段落盘:准备中 / 上传中 / 提交中 / 已完成 │ └─ 中断恢复时读回任务状态,只续传剩余部分 二、幂等(不能只靠禁用按钮) ├─ 提交带端上生成的 clientId ├─ 重复提交由服务端根据它去重 ├─ 为什么不能只靠禁用按钮 │ 页面重建、从草稿再次发布、网络重试 │ 都会绕过按钮状态 └─ 和 IM 用 clientMsgId 幂等同一个模式 幂等键要绑「业务上的同一次操作」 而不是「同一次点击」 三、草稿的多节点自动保存 │ ├─ 定时保存 ├─ 切后台前 ├─ 插入图片前(因为要跳到相册) ├─ 退出页面前 │ → 这几个节点正好覆盖页面最可能被重建的时刻 │ └─ 多端冲突:不要静默覆盖 提示「云端有一份更新的草稿」让用户选 因为两份都是他的创作 静默丢掉任何一份都不可接受 四、失败原因分层且可操作 ├─ 网络失败 → 可重试(续传,不是重传) ├─ 内容违规 → 定位到具体块 ├─ 图片过大/格式 → 提示重选那一张 ├─ 账号受限 → 说明原因 └─ 「发布失败」这样的提示没用,每种失败都要有下一步 退出保护 ├─ 退出前提醒未保存 └─ 要提供「保存草稿」而不只是「确认离开」 发布成功之后 ├─ 清理草稿与任务状态 ├─ 清理已处理的临时图片文件 └─ 跳转到笔记详情,让用户看到成品
    分步拆解
    1. 先建立认知:发布器是最不能出错的页面。用户投入了几十分钟,丢一次内容他可能就不再发了,而创作者流失对社区的伤害是长期的
    2. 把发布理解为「组合长动作」而不是一次请求。它是「上传 N 张图加提交内容」,可能持续几十秒到几分钟,这期间中断的概率很高。
    3. 把发布做成有阶段、有进度、进度落盘的任务。不是「点了就等」。
    4. 每张图上传成功就记下上传凭据并落盘。这是续传的基础——中断恢复时只传剩余的。
    5. 整体阶段也要落盘(准备中/上传中/提交中/已完成)。否则恢复时不知道该从哪一步继续。
    6. 提交带端上生成的 clientId 做幂等,由服务端去重。
    7. 不能只依赖前端禁用按钮防重。页面重建、从草稿再次发布、网络重试都会绕过按钮状态——和 IM 用 clientMsgId 是同一个模式。
    8. 草稿要定时保存,也要在关键节点保存。切后台前、插入图片前、退出页面前。
    9. 这几个关键节点是精选的:它们正好覆盖页面最可能被重建的时刻。只做定时保存会漏掉「刚打了一段字就切相册」这种情况。
    10. 多端草稿冲突要提示用户选择,不要静默覆盖。两份都是他的创作,静默丢掉任何一份都不可接受
    11. 失败原因要分层且每种都有下一步。网络失败可续传重试、内容违规定位到块、图片问题提示重选那一张、账号受限说明原因。
    12. 网络失败的重试是续传而不是重传。这一点要在提示里说清(「继续上传剩余 3 张」),让用户知道不会白费
    13. 退出前提醒未保存,并提供「保存草稿」选项。不要只给「确认离开」和「取消」。
    14. 发布成功后清理草稿、任务状态、临时图片文件。临时文件最容易漏,它会持续占用用户的存储空间。
    15. 发布成功后跳转到笔记详情。让用户看到成品,这是对他几十分钟投入的确认
    关键决策与取舍

    把发布做成可持久化的任务,代价是复杂度明显上升。「点了就等一个请求」的实现最简单;任务化要维护阶段、要落盘进度、要处理恢复时的各种中间态(图传了一半、图全传完但内容没提交、提交了但没收到响应)。但这个复杂度是必须付的——因为发布链路的中断不是异常情况而是常态(用户切后台去回消息是最自然的行为)。判断依据是「这个动作的持续时间和中断概率」:几秒内完成的操作可以「点了就等」,持续几十秒到几分钟的组合动作必须任务化

    幂等键放在端上生成而不是服务端下发。服务端下发(先申请一个发布单号再提交)更「正规」,但它多一次往返,而且如果申请成功后端上崩了,那个单号就悬着。端上生成的 clientId 在内容创建时就确定并随草稿一起落盘所以从草稿恢复后重新发布,用的还是同一个 clientId——这正是防重复的关键场景。取舍是端上生成要保证足够的唯一性,而这不难做到。

    多端草稿冲突提示用户选择,而不是「最后写入者胜」或「按时间取新」。自动选择实现简单,但「按时间取新」会丢掉另一台设备上的创作,而用户可能觉得旧那份写得更好创作内容的价值是主观的,系统没有资格替用户判断哪份更重要。取舍是多一步交互,但这一步只在真的有冲突时才出现,频率很低这和「协同冲突要显式呈现不做静默取胜」是同一条判断。

    踩过的坑:中断后从第一张图重新上传。用户发九张图,传到第七张断网,恢复后从头再来;移动网络下流量和时间都是实际成本,而且网络不稳的用户会反复失败反复重传,最后放弃发布。修法是每张图上传成功就记凭据落盘,恢复时只续传剩余部分教训是:由多个独立子步骤组成的长流程,必须记录子步骤的完成状态——否则失败的唯一补救是整体重做,而整体重做在成功率不高的环境下会导致永远完不成

    踩过的坑二:靠禁用按钮防重,页面重建后失效,用户发出了两条一样的笔记。用户点了发布没反应(其实在传图),他又点了一次;更常见的是页面被回收重建后按钮又可点,他从草稿再发一次,结果社区里出现两条重复笔记,他还要自己删一条。修法是clientId 幂等,由服务端去重教训是:防重复不能依赖 UI 状态——UI 状态会随页面生命周期消失,而幂等键随数据一起持久化,才能跨越页面重建。这和 IM 消息重发、库存扣减、打包下单的幂等是完全同一条。

    踩过的坑三:只做定时保存草稿,「刚打完一段就切相册」的内容丢了。定时保存的间隔内用户切到相册选图,页面被重建,最后一段内容没保存上。修法是在关键节点也保存:切后台前、插入图片前、退出页面前教训是:自动保存的触发时机要覆盖「页面可能被销毁的时刻」,而不只是按固定间隔——而这些时刻在创作类页面里是可以枚举出来的(跳相册、跳授权、切后台、返回)。

    踩过的坑四:发布成功后没清理临时图片文件,占用用户存储。端上处理产生的临时文件一直留在本地,重度用户积累了不小的占用,有人反馈「这个应用越用越占空间」。修法是发布成功后清理草稿、任务状态、临时文件教训是:产生临时文件的流程必须有对应的清理,而且清理要挂在「流程完成」和「流程放弃」两个出口上——只在成功时清理,失败放弃的那些就永远留着了。

    没做的部分:没做后台发布(用户可以退出页面让发布在后台继续)。技术上在 App 端可行、小程序端受限,而且跨端行为不一致会让用户困惑,当时选择让用户停留在发布页并显示进度。也没做定时发布,属于产品没排的需求。

    数字是怎么测的

    中断恢复的续传:确定性验证——上传到中途断网,恢复后检查只续传剩余图片、已上传的不重传。同时报「重复上传的流量」的减少,用「已上传张数不再重传」这个结构性描述而不是流量数值。

    重复发布:重复笔记的产生次数(绝对数),改造前后对比;同时报服务端幂等拦下的重复提交次数两个数字要一起报——前者是用户可见的结果,后者证明幂等在真实生效。

    内容丢失:「内容丢失」类反馈或工单的数量,改造前后对比。这是这个模块最该报的业务指标,而且工单数是外部可核查的。

    草稿保存的触发分布:各触发节点(定时、切后台、插入图片前、退出前)的保存次数占比如果「插入图片前」占比很高,正好证明加这个节点是必要的——它说明用户确实频繁在这个时刻离开页面。

    发布成功率与失败原因分布:按原因分开报(网络、内容违规、图片问题、账号受限)。并且要报「失败后重试并最终成功的比例」——这个数字说明分层提示与续传真的帮用户走完了流程。

    多端草稿冲突:冲突提示的出现次数与用户的选择分布如果用户经常选「用另一份」,说明「按时间取新」的自动策略会丢掉他要的内容,这正好支撑了「让用户选」的决定。

    临时文件占用:清理机制上线前后本地占用的变化

    不要报「发布成功率 99.9%」。成功率主要受网络与内容合规影响,不完全是这个模块的成果也不要报「零内容丢失」——本地存储被清理、设备故障这些情况仍会丢。正确表述是「续传由用例保证、重复提交的幂等拦截次数、内容丢失类工单的下降、草稿各触发节点的占比、失败后重试最终成功的比例」。

    面试追问
    Q:用户发九张图的笔记,传到第七张断网了怎么办? A:只续传剩余的两张,而这要求把发布做成可持久化的任务。第一版是「点了就等一个请求」,断网后要从第一张重新传;移动网络下流量和时间都是实际成本,而且网络不稳的用户会反复失败反复重传,最后放弃发布。改法是把发布拆为有阶段、有进度、进度落盘的任务每张图上传成功就把它的上传凭据记下来落盘,整体阶段(准备中/上传中/提交中/已完成)也落盘,中断恢复时读回任务状态、只续传剩余部分教训是:由多个独立子步骤组成的长流程,必须记录子步骤的完成状态——否则失败的唯一补救是整体重做,而整体重做在成功率不高的环境下会导致永远完不成代价要承认:任务化的复杂度明显上升(要处理各种中间态:图传了一半、图全传完但内容没提交、提交了但没收到响应)。但这个复杂度必须付,因为发布链路的中断不是异常而是常态——用户切后台去回消息是最自然的行为。判断依据是「这个动作的持续时间和中断概率」:几秒完成的可以「点了就等」,持续几十秒到几分钟的组合动作必须任务化
    Q:用户连点两次发布,会不会发出两条? A:第一版会,因为我们只靠「点击后禁用按钮」防重——而 UI 状态防不住。用户点了发布没反应(其实在传图)又点一次;更常见的是页面被回收重建后按钮又可点,他从草稿再发一次,结果社区里出现两条重复笔记,他还要自己删一条。修法是提交带端上生成的 clientId 做幂等,由服务端去重教训是:防重复不能依赖 UI 状态——UI 状态会随页面生命周期消失,而幂等键随数据一起持久化,才能跨越页面重建。这和 IM 消息重发用 clientMsgId、库存扣减、打包下单的幂等是完全同一条。这里有个设计细节值得说幂等键放在端上生成而不是服务端下发。服务端下发(先申请一个发布单号再提交)更「正规」,但多一次往返,而且申请成功后端上崩了那个单号就悬着端上生成的 clientId 在内容创建时就确定并随草稿一起落盘,所以从草稿恢复后重新发布用的还是同一个 clientId——这恰恰是防重复最关键的场景,服务端下发的方案在这里反而做不到。
    Q:草稿什么时候保存?只做定时保存够吗? A:不够,我们踩过——「刚打完一段就切相册」的内容丢了。定时保存的间隔内用户切到相册选图,页面被重建,最后一段内容没保存上。修法是除定时保存外,在关键节点也保存:切后台前、插入图片前(因为要跳到相册)、退出页面前这几个节点是精选的——它们正好覆盖了页面最可能被重建的时刻,而这些时刻在创作类页面里是可以枚举出来的(跳相册、跳授权、切后台、返回)。教训是:自动保存的触发时机要覆盖「页面可能被销毁的时刻」,而不只是按固定间隔。度量上我会报各触发节点的保存次数占比——如果「插入图片前」占比很高,正好证明加这个节点是必要的,它说明用户确实频繁在这个时刻离开页面。草稿还要处理多端冲突:用户在手机上存了草稿、又在另一台设备编辑过。我们的做法是提示「云端有一份更新的草稿」让用户选,不做静默覆盖也不按时间自动取新——因为两份都是他的创作,而创作内容的价值是主观的,系统没有资格替用户判断哪份更重要
    Q:发布失败了怎么提示用户? A:按原因分层,而且每种失败都要有明确的下一步——「发布失败」这样的提示没用。四类:网络失败 → 可重试,而且要说清是续传(提示「继续上传剩余 3 张」,让用户知道之前的不会白费);内容违规 → 定位到具体块(笔记有十几个块,让他自己找不现实);图片过大或格式不支持 → 提示重选那一张(而不是笼统说「图片有问题」);账号受限 → 说明原因度量上我会报「失败后重试并最终成功的比例」——这个数字说明分层提示与续传真的帮用户走完了流程,比单纯的成功率有信息量。另外两件收尾的事容易被漏退出前提醒未保存,并且要提供「保存草稿」而不只是「确认离开」发布成功后要清理草稿、任务状态、临时图片文件——临时文件最容易漏,我们踩过,重度用户积累了不小的占用,有人反馈「这个应用越用越占空间」教训是:产生临时文件的流程必须有对应的清理,而且清理要挂在「流程完成」和「流程放弃」两个出口上——只在成功时清理,失败放弃的那些就永远留着了。

没有匹配的内容,换个关键词试试。

项目拆解 · 笔记发布与图文编辑(uni-app)· 共 3 个模块 · 数字均为示例,需替换成自己项目的真实数据