


按照仓库的规则,开发同时运行于原生和Web的TypeScript应用
在开发或修改同时运行于原生和Web的TypeScript应用时使用。让AI遵循仓库的脚本和目录结构,复用共享组件,把界面上的文字改成可翻译的形式,并在所有受影响的平台上测试后再收尾。
要使用这个AI规则
## 通过仓库的脚本运行测试、lint和构建
将包清单和仓库脚本视为受支持版本与命令的权威依据。优先使用当前受支持的API;当受影响的范围允许安全替换时,应替换旧式或已弃用的模式。
以下任务应使用包管理器脚本:
- 启动开发服务器和各个受支持的平台;
- 执行单元测试、lint、类型检查和格式化;
- 构建Web版本并生成原生项目。
如果仓库脚本已经存在,绝不得直接调用底层的测试、lint、类型检查或格式化工具。脚本可能会提供直接命令所遗漏的配置或准备步骤。
如果本地化提取或编译任务由CI负责,则不得运行这些命令。只需添加或更新可翻译的源字符串,再由定时流水线更新目录和运行时翻译。
修改代码之前,应明确目标平台、用于验证的规范脚本,以及生成的构件由本地还是自动化流程负责。
## 把新页面和组件放进正确的目录
将共享且不依赖平台的UI元素放入`components`。将完整页面路由放入`screens`。对于跨多个页面组合上下文、数据访问、组件和实用工具的较大功能,应使用`features`。
对于包含私有组件或状态的复杂页面,应创建页面目录并将这些依赖项放在同一位置。在有多个使用方需要某段代码之前,不得将其提升到共享文件夹。
将服务器状态hook、简单的UI偏好、会话状态和持久化放入职责明确的状态模块,或将特定功能的状态与其使用方放在一起。避免使用混合无关职责的大型文件。
对于不属于特定功能的可复用实用工具、API客户端、格式化程序和常量,应使用`lib`。如果现有职责已经适用,则不要新增顶层源代码目录。
不要在旧式view目录中添加新工作。除非迁移属于当前任务,否则应保留现有旧代码;但新页面、新组件和新功能必须放入当前结构。
组件名称使用PascalCase,普通文件、目录、变量和函数使用camelCase。大型模块可以使用`index.tsx`作为入口,并在同级放置组件、hook和实用工具;小型组件应保留在单个文件中。
可选的功能文档和测试可与较大的模块放在一起。注释应解释某个决定为何存在,而不是重复代码已经表达的内容。命名声明使用docblock,多行说明使用块注释,行注释仅用于简短备注。
## 用设计系统和现有组件搭建界面
在创建原始样式或重复组件之前,应先使用既有设计系统。按照以下顺序组合与主题无关的atom、主题atom和例外情况下的原始样式:
1. flex和布局atom;
2. 间距atom;
3. 排版atom;
4. 主题颜色和表面;
5. 作用域严格限定的原始样式。
条件样式使用平台样式工具,响应式布局使用断点hook。应将平台条件保留在样式组合内部,不要将其分散到组件逻辑中。
创建控件之前,应在共享组件库中查找合适的按钮、对话框、菜单、文本字段或排版基础组件。遵循其复合API和已有的规范用法。
声明组件时:
- 除非需要带key的fragment,否则优先使用fragment简写;
- 优先使用函数声明;
- 在参数列表中解构props;
- 在仍然清晰易读时,使用显式的内联props类型;
- 为可选props提供合理的默认值;
- 在可能的情况下,让可复用组件保持平台中立。
如果平台架构使用受控输入时性能较差,应使用带`defaultValue`的非受控文本字段。只有在行为确实需要时才使用受控值。
即使示例中没有,用户生成的文本也可能包含emoji。对用户内容启用排版系统的emoji安全行为,仅对已知的静态文本省略该行为。
## 把界面上的所有文字都改成可翻译的形式
使用项目的翻译宏或翻译组件包裹每个面向用户的字符串。当一个词有多种含义时,例如名词和动词拼写相同,应添加译者注释或上下文。
使用翻译框架支持的现代宏入口。当其他子系统(例如主题系统)已经使用同一个短变量名时,应为翻译函数设置别名。
数量使用翻译框架的复数功能,日期和时间使用其日期格式化程序。当当前语言发生变化时,必须重新执行框架格式化;直接进行区域设置格式化通常不会重新执行。
在面向用户的文本中,优先使用排版正确的Unicode标点。统一遵循项目的标点规范,包括引号和破折号间距。
不得手动编辑生成的区域设置输出,也不得在本地运行由CI负责的提取或编译任务。源代码变更应包含已包裹的消息,以及足以准确翻译的上下文。
## 处理服务器数据、导航和各平台专用文件
远程数据使用既有的服务器状态查询库,简单的UI偏好使用context式状态。通过配对的读取hook和更新hook公开偏好。
使用共享的key辅助程序和参数对象构建查询key。key根应与hook名称一致。查询hook、mutation hook和仅修改缓存的mutation辅助程序应使用一致的命名。
从共享常量中选择stale time。对于游标分页,应使用infinite query,将响应游标作为下一页参数返回,并且仅在使用边界处将页面扁平化。
当持久化的查询数据改变形状时,应提高其持久化版本,使过时的序列化数据被丢弃并重新获取。
对于mutation失败:
- 向用户说明预期的网络故障,但不要在错误日志中重复记录;
- 明确处理有类型的协议错误或API错误;
- 将意外故障连同安全的消息发送到结构化日志。
使用有类型的路由参数,以及项目的导航辅助程序或hook。保持路由配置、路由定义和路由类型同步。
使用`.web`、`.native`、`.ios`和`.android`等文件名后缀表示平台实现。import基础模块路径,由bundler选择具体实现。绝不得使用条件式runtime import来选择平台文件。
优先将平台变体集中在一个模块目录下。runtime平台标志只能用于行为,不得用于模块解析。应使用仓库的绝对import别名,而不是层层向上的深相对路径。
## 避免关闭对话框时的时序问题和多余的记忆化
所有必须在对话框关闭后执行的操作,都应放在对话框控件的close callback内执行。如果先关闭对话框,再立即导航、打开另一个overlay、更新受影响的状态或调用父级callback,可能会与close animation发生竞态。
必须遵循以下顺序:
1. 调用close方法;
2. 向该方法提供callback;
3. 在callback内执行导航、打开overlay、更新状态或调用父级callback。
遵循平台特定的复合组件规范。对话框handle或菜单容器item可能仅供原生平台使用,而close control或菜单divider可能仅供Web使用。应在会执行各个分支的平台上进行测试。
启用编译器自动记忆化时,不得预先添加`useMemo`或`useCallback`。只有在存在具体的引用稳定性要求时才使用它们,例如effect依赖项或非React库的契约。
不得为推测性的优化牺牲可读性。在添加手动记忆化之前,应测量实际问题或满足明确的稳定性要求。
## 完成前在所有受影响的平台上测试
完成前:
1. 确认每个可交互元素都有无障碍标签,并在需要时提供有用的提示;
2. 确认所有面向用户的字符串都已本地化,且含义模糊的消息包含上下文;
3. 确认样式统一使用设计系统atom、主题值和平台工具;
4. 确认服务器状态、UI偏好和会话状态分别使用其预期机制;
5. 确认没有重复创建已有的共享组件;
6. 确认props和路由参数具有显式类型;
7. 在端到端测试需要的位置公开稳定的测试标识符;
8. 运行仓库脚本以执行测试、lint、类型检查和格式化;
9. 测试所有受影响的原生和Web行为。
审查差异,确认没有新增对旧式目录的使用、条件式平台import、对生成的本地化文件的直接编辑、非受控输入与受控输入之间的回归,以及对话框关闭后的竞态。
报告任何无法运行的平台或规范脚本,并说明缺少的前提条件。不得依据单个平台的检查结果声称已完成跨平台验证。
