6.2 KiB
6.2 KiB
width-measurement(任务清单)
对应计划:
spec_kit/Text Wrap/modules/width-measurement/plan.md状态含义:
[ ]未完成,[x]已完成。
执行完本清单后,需要在spec_kit/overview.md的Text Wrap条目下补充“已完成编码/任务执行完毕”的标记(见最后一节)。
0. 任务标记规则
- 用勾选框标记执行状态:
[ ]未完成[x]已完成
- 每个任务必须可独立验收(有明确产出与检查方式)。
- 所有代码注释必须为简体中文,并把“缓存 key / 降级语义 / 报错口径”写死,避免后续模块漂移。
1. 前置检查(依赖与约束确认)
-
1.1 确认
availableWidth由设备侧传入(本模块不内置 widgetProfiles)- 验收:在本模块实现中不引入任何固定宽度常量表;仅消费上层传入的宽度与 profile。
-
1.2 确认 APP 测量方案采用成熟库(默认
react-native-text-size)- 验收:
client/package.json中存在该依赖(或团队等价成熟方案),并且测量封装函数仅依赖该库/注入点。
- 验收:
2. 依赖与目录骨架(客户端侧实现)
-
2.1 新建目录
client/src/features/textWrap/measure/- 产出(建议文件):
types.ts(FontSpec/ContextProfile/MeasureResult)errors.ts(缺字段报错)fontSpecKey.tscache.ts(两级缓存 + 上限)measureWidthImpl.ts(对接成熟测量库)measureWidthCached.tsmeasureSliceWidthCached.ts__tests__/widthMeasurement.test.tsindex.ts(统一导出)
- 验收:目录存在,TS 可正常 import(不报路径错误)。
- 产出(建议文件):
-
2.2 新增依赖
react-native-text-size(如项目未安装)- 命令(示例):
cd client && npm install react-native-text-size
- 验收:
- 安装成功
npm test不受影响(后续任务再补充本模块测试)
- 命令(示例):
3. fontSpec 强校验(缺失直接报错)
-
3.1 定义
FontSpec类型(必须字段:fontSize/fontFamily/fontWeight)- 验收:类型层面可表达“必填字段”,并在运行时也做校验。
-
3.2 实现运行时校验与错误(简体中文)
- 要求:
- 缺失任一字段直接抛错
- 错误信息包含:缺失字段名 + 建议调用方补齐的位置(例如
wrapText({ fontSpec: ... })) - 禁止隐式默认值
- 验收:单测断言会抛错且错误信息包含缺失字段名。
- 要求:
4. fontSpecKey 与 contextProfile(确定性 key 体系)
-
4.1 实现
buildFontSpecKey(fontSpec)- 规则:
fontFamily|fontWeight|fontSize(顺序固定) - 验收:同输入同输出;fontSize 非有限数时报错。
- 规则:
-
4.2 定义
contextProfile拼接规则并实现 helper- 要求:
- APP:
APP|<platform>|<scale?>(字段顺序固定) - WIDGET:
WIDGET|<widgetSize?>(无 widgetSize 时为WIDGET)
- APP:
- 验收:输出字符串稳定;不同 profile 必须产生不同缓存 key。
- 要求:
5. 两级缓存(有上限 + 确定性)
-
5.1 实现缓存容器(模块级常驻)
- 要求:
- 字符串测量缓存(textKey)
- 切片测量缓存(sliceKey)
- 容量上限策略:LRU 优先;若不引入依赖,先 Map + 超限清空(并预留打点钩子)
- 验收:单测可验证缓存命中会减少底层测量调用次数。
- 要求:
-
5.2 定义 key 生成规则并实现
- 规则:
textKey = <contextProfile>|<fontSpecKey>|<text>sliceKey = <contextProfile>|<fontSpecKey>|<start>|<end>
- 验收:key 生成不依赖对象遍历顺序;同输入同 key。
- 规则:
6. 测量实现与降级(approx mode)
-
6.1 实现
measureWidthImpl(text, fontSpec)(APP)- 要求:
- 依赖成熟库测量宽度
- 返回值必须校验:有限且非负
- 验收:在单测中用 mock 替代真实库,验证封装逻辑与校验逻辑即可(不做像素级断言)。
- 要求:
-
6.2 实现
measureWidthCached(...)- 行为:
- 正常测量:返回
{ width:number, meta:{ isApprox:false } } - 进入 approx:返回
{ width:null, meta:{ isApprox:true, reason } }
- 正常测量:返回
- approx 触发条件(任一满足):
- 未提供测量能力 / context=WIDGET 且上层不启用测量 ->
WIDTH_UNKNOWN - 抛错/NaN/Infinity/负数 ->
MEASURE_FAILED
- 未提供测量能力 / context=WIDGET 且上层不启用测量 ->
- 验收:单测覆盖两类 reason。
- 行为:
-
6.3 实现
measureSliceWidthCached(...)(切片测量)- 要求:
- 通过
joinTokens(start,end)生成切片文本 - 使用切片缓存避免 DP 反复测量
- 通过
- 验收:单测验证同 sliceKey 不重复调用底层测量。
- 要求:
7. 单元测试(Vitest)
- 7.1 新建
__tests__/widthMeasurement.test.ts并覆盖以下用例- fontSpec 报错:缺字段必抛错(错误信息含字段名)
- 缓存命中:同 key 不重复调用底层测量 mock
- 缓存隔离:不同
contextProfile/fontSpecKey不互相污染 - 降级:
- 缺测量能力 ->
width=null+WIDTH_UNKNOWN - 测量抛错/NaN ->
width=null+MEASURE_FAILED
- 缺测量能力 ->
- 验收:
npm test稳定通过。
8. 最终自检清单(合入前)
-
8.1
npm test通过(包含本模块新增用例)- 验收:不影响现有测试文件。
-
8.2
npx tsc --noEmit通过(或项目既有 TS 检查命令通过)- 验收:无类型错误。
-
8.3 注释与口径自检(简体中文)
- 检查点:
fontSpec缺字段“必须报错”- key 生成规则与 contextProfile 口径
- approx mode 的 reason 语义(WIDTH_UNKNOWN / MEASURE_FAILED)
- 验收:后续模块开发者只看代码也不会产生歧义。
- 检查点:
9. 文档回写(任务清单执行完毕后必须做)
- 9.1 在
spec_kit/overview.md的Text Wrap条目下补充执行状态- 建议写法:
- 增加一行:
- **已完成编码(阶段性)**:width-measurement(宽度测量与降级)
- 增加一行:
- 验收:overview 能反映该子模块已完成,便于全局追踪。
- 建议写法: