HarmonyOS应用实战-启示散页-57-题库颜色迁移别让旧数据失色:给 colorKey 做兼容映射
HarmonyOS 应用实战 57:题库颜色迁移别让旧数据失色,给 colorKey 做兼容映射
题库颜色看起来只是一个视觉字段,但它一旦写进 Preferences,就变成了需要迁移的持久数据。旧版本如果保存的是 #8AA7FF 或已经废弃的颜色名,新版本页面只认识 colorKey,列表就可能出现透明卡片、深色模式对比不足,或者编辑页与首页颜色不一致。

本文解决四个问题:
- 识别旧色值、旧 key、新 token 三类输入
- 把兼容映射收进主题 owner,而不是散在页面
- 迁移后只让页面消费稳定 colorKey
- 用回归数据证明旧题库不会失色


颜色字段为什么会变成交付风险
早期项目常把颜色当展示细节,页面里直接保存十六进制值。等主题 token、深色模式和色板预览加入后,旧值就不再只是旧样式,而是无法解释的持久状态。迁移要先回答:旧值是否可信、能映射到哪个语义色、映射失败时是否有保底。
故障链:旧 Preferences 保存 colorValue -> 新页面只读 colorKey -> token 解析失败 -> 卡片颜色回退不一致 -> 用户以为题库丢失或主题损坏
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
把颜色身份收敛成迁移输入
迁移函数不要直接返回页面颜色。它应该返回标准化结果,告诉调用方这个值是原生可用、兼容映射、还是只能回退到默认色。
type ColorMigrationKind = 'nativeKey' | 'legacyValue' | 'legacyName' | 'fallback';
interface ColorMigrationDecision {
original: string;
nextColorKey: string;
kind: ColorMigrationKind;
changed: boolean;
reason: string;
}
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
ColorKeyMapper 只做一件事:解释旧值
映射表应归主题 owner 管理。页面不知道历史色值,也不关心深色模式具体颜色;页面只关心最终 colorKey 是否能被 token 系统解析。
class ColorKeyMapper {
private static readonly legacyMap: Record<string, string> = {
'#8AA7FF': 'calmBlue',
'#F6C177': 'warmOrange',
'purple_light': 'softPurple'
};
static migrate(raw: string | undefined): ColorMigrationDecision {
if (!raw) {
return { original: '', nextColorKey: 'defaultDeck', kind: 'fallback', changed: true, reason: '缺少颜色字段' };
}
if (AppColorToken.hasDeckColor(raw)) {
return { original: raw, nextColorKey: raw, kind: 'nativeKey', changed: false, reason: '已是 colorKey' };
}
const mapped = this.legacyMap[raw] ?? 'defaultDeck';
return { original: raw, nextColorKey: mapped, kind: this.legacyMap[raw] ? 'legacyValue' : 'fallback', changed: mapped !== raw, reason: '兼容旧颜色' };
}
}
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
DeckRepository 保存迁移后的最终事实
迁移不应只在页面 render 时临时修正。否则列表页正常、编辑页仍旧、重启后又恢复旧值。更稳的做法是读出题库时完成标准化,并在确实变化时回写最终事实。
class DeckColorMigrationService {
async normalizeDeck(deck: Deck): Promise<Deck> {
const decision = ColorKeyMapper.migrate(deck.colorKey ?? deck.colorValue);
if (!decision.changed) {
return deck;
}
const next: Deck = { ...deck, colorKey: decision.nextColorKey, updatedAt: Date.now() };
await DeckRepository.saveDeck(next);
AppStorage.setOrCreate('deck.changedAt', next.updatedAt);
return next;
}
}
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
编辑页只展示色板,不背历史包袱
色板组件接收的是 colorKey 和候选 token,不接收旧色值。这样旧数据兼容只发生一次,后续页面都按新协议工作。
@Component
struct DeckColorPicker {
@Prop colorKey: string;
@Prop options: string[];
@State private selectedKey: string = this.colorKey;
private selectColor(key: string): void {
if (AppColorToken.hasDeckColor(key)) {
this.selectedKey = key;
}
}
}
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
颜色迁移的验证样本要覆盖三类旧数据
只拿新建题库验证没有意义。要准备旧十六进制值、废弃 key、缺失字段、未知值四种数据,分别看首页、编辑页、深色模式和重启后的表现。
验证样本:
1. colorKey=calmBlue,应不改写。
2. colorValue=#8AA7FF,应映射到 calmBlue。
3. colorKey=purple_light,应映射到 softPurple。
4. colorKey=unknown,应回退到 defaultDeck 并留下迁移记录。
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
颜色问题的排查表
如果迁移后仍有色差,不要先调页面样式,先确认最终事实是否已经落库。
| 现象 | 先看哪里 | 处理 |
|---|---|---|
| 首页有颜色,编辑页无颜色 | 是否只在首页临时映射 | 迁移后回写 Deck |
| 深色模式对比不足 | colorKey 是否有 dark token | 补齐语义 token |
| 旧题库全部变默认色 | legacyMap 是否缺旧值 | 补映射并记录 fallback 数量 |
这段内容的重点是把责任停在正确边界:页面只展示和触发,Service 处理规则,Repository 保存最终事实,发布或诊断相关内容必须留下可复查证据。
题库颜色迁移别让旧数据失色 的交付边界
这篇文章讨论的是 题库颜色迁移别让旧数据失色 这条工程链路,不把建议代码说成已经上线的能力。落地时应先在真实项目里找到对应 owner,再决定是复用现有 Service,还是新增一个很窄的协调层。若当前工程已经有同类能力,优先补验证和边界说明;若工程还没有这项能力,示例代码只能作为设计骨架,不能直接写进发布说明里当作已完成事实。
交付前至少要留下三类证据:第一类是代码证据,能通过搜索定位到唯一写入点和读取点;第二类是行为证据,能说明正常、异常、重进和冷启动分别是什么结果;第三类是发布证据,能证明日志、截图、导出文本或资源目录没有把用户内容和临时素材带出去。缺少任何一类证据,都只能算完成了文章设计,不能算完成了工程闭环。
| 证据类型 | 应该留下什么 | 不足时的风险 |
|---|---|---|
| 代码证据 | 题库颜色迁移别让旧数据失色 对应的 Service、Repository、页面入口 |
后续只能靠搜索和猜测排查 |
| 行为证据 | 正常、异常、重进、冷启动四组结果 | 只证明一次手动点击可用 |
| 发布证据 | 日志、截图、导出或资源检查记录 | 审核或用户反馈时无法复盘 |
验证清单
题库颜色迁移别让旧数据失色 的验证不能只看当前页面。建议把同一条链路拆成四次观察:第一次看正常入口是否能完成;第二次故意制造无效输入或旧数据;第三次离开页面再回来;第四次冷启动后再读一次持久化结果。四次观察对应即时交互、异常兜底、路由重进和跨进程恢复,能覆盖轻量本地应用最常见的状态错位。
| 场景 | 要观察的结果 | 失败时先查哪里 |
|---|---|---|
| 正常入口 | 题库颜色迁移别让旧数据失色 能得到预期结果 |
页面是否调用唯一 Service |
| 异常输入 | 题库颜色迁移别让旧数据失色 返回可解释错误或恢复动作 |
Guard 或 Service 是否吞错 |
| 页面重进 | 展示来自仓储最终事实 | 是否只改了页面状态 |
| 冷启动 | AppStorage 与 Preferences 一致 | 启动水合顺序和默认值 |
| 发布复查 | 日志、截图、导出和资源边界干净 | 是否遗漏发布清单证据 |
如果某个场景无法在当前会话验证,要在文章中说明它仍然是待验证项。不要把本地静态检查、代码片段设计或一次手动点击描述成真机全链路验证。
针对本篇,建议按 颜色字段为什么会变成交付风险、把颜色身份收敛成迁移输入、ColorKeyMapper 只做一件事:解释旧值、DeckRepository 保存迁移后的最终事实 的顺序复核。先确认故障链是否成立,再看模型是否只表达必要事实,然后检查核心 owner 是否唯一,最后验证页面或发布入口是否只消费结果。这个顺序能避免一上来就改 UI,也能防止把临时修补写成长期规则。
评审时还要反向提问:如果 识别旧色值、旧 key、新 token 三类输入 没做到,用户会看到什么;如果 把兼容映射收进主题 owner,而不是散在页面 没做到,数据会在重进或冷启动后怎样变化;如果 题库颜色迁移别让旧数据失色 的失败路径没有证据,后续排障要从哪条日志、哪份截图或哪条本地记录开始。把这些问题写清楚,文章才不是只有代码片段,而是能指导下一次改动的工程记录。
最后把 颜色迁移的验证样本要覆盖三类旧数据 和 颜色问题的排查表 合在一起复盘:前者说明边界有没有被收进一个稳定 owner,后者说明交付时能不能拿出证据。若两者对不上,就不要急着改文案,而是回到 题库颜色迁移别让旧数据失色 的第一处输入、第一处状态保存和第一处展示消费点重新查一遍。文章里的代码示例只负责表达治理方式,真正落到项目时还要补齐命令输出、截图、异常样本和回退路径,这样读者照着做才不会只得到一个看似完整、实际不可验证的实现。
实际改项目时,可以给 题库颜色迁移别让旧数据失色 单独留一条复盘记录:本次改动前是什么状态,改动后由哪个模块负责兜底,失败时用户看到什么提示,开发者能从哪里继续定位。这个记录不需要很长,但要覆盖 迁移后只让页面消费稳定 colorKey 和 用回归数据证明旧题库不会失色 两件事。前者决定读者能不能判断方案边界,后者决定团队能不能在下一次回归时复用同一套检查,而不是重新凭印象翻页面、翻日志、翻资源目录。
对系列文章尤其要这样写,因为 题库颜色迁移别让旧数据失色 一旦只留下结论,下一篇就会重复解释背景;一旦留下输入、owner、证据和限制,后面的内容才能继续向前推进。
小结
颜色迁移的核心不是保留每个旧色值,而是把旧输入解释成新的语义 token。页面只消费 colorKey,迁移服务负责兼容和回写,主题 owner 负责 token 解析;这样旧题库升级后不会失色,也不会把历史字段带进每个组件。
旦只留下结论,下一篇就会重复解释背景;一旦留下输入、owner、证据和限制,后面的内容才能继续向前推进。
小结
颜色迁移的核心不是保留每个旧色值,而是把旧输入解释成新的语义 token。页面只消费 colorKey,迁移服务负责兼容和回写,主题 owner 负责 token 解析;这样旧题库升级后不会失色,也不会把历史字段带进每个组件。
更多推荐

所有评论(0)