@cursorrules.mdc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. ---
  2. description:
  3. globs:
  4. alwaysApply: false
  5. ---
  6. # 鸿蒙ArkTS编程规则与最佳实践
  7. ## 语法差异规则:ArkTS vs TypeScript
  8. 在需要更新经验规则记忆的时候,请及时更新.cursor\rules\cursorrules.mdc文件
  9. ### 1. 空值检查规则
  10. ```typescript
  11. // ❌ 错误: 未对可能为null的对象执行检查
  12. this.dbObject.executeSql(sql);
  13. // ✅ 正确: 执行null检查
  14. if (this.dbObject) {
  15. this.dbObject.executeSql(sql);
  16. }
  17. ```
  18. ### 2. 解构赋值规则
  19. ```typescript
  20. // ❌ 错误: ArkTS不支持解构赋值语法
  21. for (const [key, value] of Object.entries(obj)) {
  22. // 处理逻辑
  23. }
  24. // ✅ 正确: 使用传统循环方式
  25. const keys = Object.keys(obj);
  26. for (let i = 0; i < keys.length; i++) {
  27. const key = keys[i];
  28. const value = obj[key];
  29. // 处理逻辑
  30. }
  31. ```
  32. ### 3. 异步API调用规则
  33. ```typescript
  34. // ❌ 错误: 回调参数类型不匹配
  35. dbStore.executeSql(sql, (err, result: ResultSet) => {
  36. // 处理逻辑
  37. });
  38. // ✅ 正确: 使用Promise模式
  39. dbStore.executeSql(sql)
  40. .then(() => {
  41. // 成功处理
  42. })
  43. .catch((err: Error) => {
  44. // 错误处理
  45. console.log(err.message);
  46. });
  47. ```
  48. ### 4. 计算属性名规则
  49. ```typescript
  50. // ❌ 错误: 不支持计算属性名语法
  51. const obj = { [CONSTANT.KEY]: value };
  52. // ✅ 正确: 使用对象属性赋值语法
  53. const obj = {};
  54. obj[CONSTANT.KEY] = value;
  55. ```
  56. ### 5. 数据类型规则
  57. ```typescript
  58. // ❌ 错误: 使用any或未指定泛型类型
  59. const items = new Set();
  60. const map = new Map();
  61. // ✅ 正确: 明确指定泛型类型
  62. const items = new Set<string>();
  63. const map = new Map<string, number>();
  64. ```
  65. ### 6. 错误对象类型规则
  66. ```typescript
  67. // ❌ 错误: 使用隐式any类型的错误对象
  68. try {
  69. // 代码
  70. } catch (e) {
  71. console.log(`错误: ${e}`);
  72. }
  73. // ✅ 正确: 明确指定错误对象的类型
  74. try {
  75. // 代码
  76. } catch (e: Error) {
  77. console.log(`错误: ${e.message}`);
  78. }
  79. ```
  80. ### 7. 类型声明与对象字面量规则
  81. ```typescript
  82. // ❌ 错误: 对象字面量不能用作类型声明
  83. const metadataToUpdate: {
  84. duration?: string,
  85. mimeType?: string,
  86. sampleRate?: string,
  87. trackCount?: string
  88. } = { /* 值 */ };
  89. // ✅ 正确: 使用接口或类型别名定义类型
  90. interface MediaMetadata {
  91. duration?: string;
  92. mimeType?: string;
  93. sampleRate?: string;
  94. trackCount?: string;
  95. }
  96. const metadataToUpdate: MediaMetadata = { /* 值 */ };
  97. ```
  98. ## 数据库操作最佳实践
  99. ### 1. 表结构升级
  100. ```typescript
  101. // ❌ 错误: 依赖回调处理的表结构升级
  102. this.rdbStore.executeSql(tableInfoQuery, (err, result) => {
  103. // 处理逻辑
  104. });
  105. // ✅ 正确: 使用Promise模式并独立处理每个列的添加操作
  106. this.rdbStore.executeSql(tableInfoQuery)
  107. .then(() => {
  108. // 为每个需要添加的列单独执行ALTER TABLE
  109. Object.keys(columnsToAdd).forEach(column => {
  110. const type = columnsToAdd[column];
  111. this.rdbStore.executeSql(`ALTER TABLE ${tableName} ADD COLUMN ${column} ${type}`)
  112. .then(() => { /* 成功处理 */ })
  113. .catch((err: Error) => { /* 错误处理 */ });
  114. });
  115. });
  116. ```
  117. ### 2. 资源释放
  118. ```typescript
  119. // ❌ 错误: 未关闭ResultSet
  120. this.rdbStore.query(predicates, (resultSet) => {
  121. // 处理逻辑
  122. });
  123. // ✅ 正确: 确保关闭ResultSet
  124. this.rdbStore.query(predicates, (resultSet) => {
  125. try {
  126. // 处理逻辑
  127. } finally {
  128. resultSet.close();
  129. }
  130. });
  131. ```
  132. ### 3. 数据库查询安全处理
  133. ```typescript
  134. // ❌ 错误: 不安全的列访问
  135. const value = resultSet.getString(resultSet.getColumnIndex(columnName));
  136. // ✅ 正确: 安全的列访问
  137. const safeGet = (col: string) => {
  138. const index = resultSet.getColumnIndex(col);
  139. return index >= 0 ? resultSet.getString(index) || '' : '';
  140. };
  141. const value = safeGet(columnName);
  142. ```
  143. ## 对象字面量规则
  144. ### 1. 复杂对象初始化
  145. ```typescript
  146. // ❌ 错误: 不支持复杂对象字面量初始化
  147. const config = {
  148. complex: {
  149. nested: {
  150. value: someValue
  151. }
  152. }
  153. };
  154. // ✅ 正确: 分步创建复杂对象
  155. const config = {};
  156. config.complex = {};
  157. config.complex.nested = {};
  158. config.complex.nested.value = someValue;
  159. ```
  160. ### 2. 接口实现
  161. ```typescript
  162. // ❌ 错误: 在类内部定义接口
  163. class MediaTable {
  164. interface MediaMetadata {
  165. duration?: string;
  166. mimeType?: string;
  167. }
  168. }
  169. // ✅ 正确: 在类外部定义接口并导出
  170. export interface MediaMetadata {
  171. duration?: string;
  172. mimeType?: string;
  173. }
  174. class MediaTable {
  175. // 类实现
  176. }
  177. ```
  178. ## UI数据处理规则
  179. ### 1. 空值处理
  180. ```typescript
  181. // ❌ 错误: 未处理空值或无效值
  182. Text(this.currentSong.sampleRate)
  183. // ✅ 正确: 安全处理空值和无效值
  184. Text(Utility.convertToKHz(this.currentSong?.sampleRate || ''))
  185. ```
  186. ### 2. 数据转换函数
  187. ```typescript
  188. // ❌ 错误: 直接在UI中转换数据
  189. Text(`${Number(this.sampleRate) / 1000} KHz`)
  190. // ✅ 正确: 使用工具函数处理数据格式化
  191. static convertToKHz(sampleRateHz: string|undefined): string {
  192. if(StrUtil.isEmpty(sampleRateHz) || sampleRateHz === undefined) {
  193. return '未知';
  194. }
  195. try {
  196. const sampleRateNum = Number(sampleRateHz);
  197. if (isNaN(sampleRateNum) || sampleRateNum <= 0) {
  198. return '未知';
  199. }
  200. return `${(sampleRateNum / 1000).toFixed(1)} KHz`;
  201. } catch (err) {
  202. return '未知';
  203. }
  204. }
  205. // 在UI中使用
  206. Text(Utility.convertToKHz(this.sampleRate))
  207. ```
  208. ## 命名规范
  209. - 类名: PascalCase (例如 MediaTable)
  210. - 方法名: camelCase (例如 queryByParentPath)
  211. - 常量: UPPER_SNAKE_CASE (例如 DB_COLUMNS.FILE_PATH)
  212. - 私有属性: _camelCase (例如 _dbStore)
  213. - 接口名: PascalCase带I前缀或不带 (例如 IMediaMetadata 或 MediaMetadata)
  214. ## 异步编程最佳实践
  215. ### 1. 避免回调地狱
  216. ```typescript
  217. // ❌ 错误: 嵌套回调
  218. methodA(() => {
  219. methodB(() => {
  220. methodC(() => {
  221. // 更多嵌套...
  222. });
  223. });
  224. });
  225. // ✅ 正确: 使用Promise链或async/await
  226. methodA()
  227. .then(() => methodB())
  228. .then(() => methodC())
  229. .catch(error => console.error(error));
  230. // 或使用async/await
  231. async function process() {
  232. try {
  233. await methodA();
  234. await methodB();
  235. await methodC();
  236. } catch (error) {
  237. console.error(error);
  238. }
  239. }
  240. ```
  241. ### 2. 异步状态更新
  242. ```typescript
  243. // ❌ 错误: 在异步回调中直接更新状态而没有检查组件是否已销毁
  244. fetchData(() => {
  245. this.data = result; // 组件可能已被销毁
  246. });
  247. // ✅ 正确: 添加组件生命周期检查
  248. fetchData(() => {
  249. if (!this.mDestroyPage) {
  250. this.data = result;
  251. }
  252. });
  253. ```
  254. ## 文件和代码组织
  255. ### 1. 导入导出规则
  256. ```typescript
  257. // ❌ 错误: 混合默认导出和命名导出
  258. export default class MediaTable { ... }
  259. export interface MediaMetadata { ... }
  260. // ✅ 正确: 明确区分默认导出和命名导出
  261. // MediaTable.ets
  262. export default class MediaTable { ... }
  263. // MediaTypes.ets
  264. export interface MediaMetadata { ... }
  265. export interface AudioMetadata extends MediaMetadata { ... }
  266. ```
  267. ### 2. 文件结构规则
  268. ```
  269. // ✅ 推荐的文件结构
  270. /common
  271. /constants // 常量定义
  272. /interfaces // 接口定义
  273. /util // 工具类
  274. /viewmodel // 视图模型
  275. /view // UI组件
  276. /controller // 控制器
  277. ```