1# 示例代码风格 2本规范适用于文档中ArkTS、JavaScript和C/C++等编程语言的示例代码片段,旨在提高OpenHarmony文档示例代码的可读性、可维护性,以及风格一致性。 3 4## 代码规范 5 6### 【规则】遵守基本编程规范 7 8【描述】 9 10文档的示例代码需要遵循[JavaScript语言编程规范](../OpenHarmony-JavaScript-coding-style-guide.md)、[C语言编程规范](../OpenHarmony-c-coding-style-guide.md)和[C++语言编程规范](../OpenHarmony-cpp-coding-style-guide.md)基本的编码规范,包括命名规范、代码格式和代码规范等。 11 12### 【规则】每个接口(包括方法和组件)均需要提供示例代码 13 14【描述】 15 16API参考中,每个接口(包括方法和组件)均需要提供示例代码。如果多个API存在关联关系,则需要在一个场景化的代码示例中体现。 17 18### 【规则】每个接口提供的示例代码中无须增加`try...catch...` 19 20【描述】 21 22在接口示例代码中,为了保持示例代码的简洁性和易读性,不需要为每个接口调用添加`try...catch...`语句来捕获异常。 23 24示例代码主要用于演示如何正确地使用接口,并提供了正确的处理方式。添加异常处理代码可能会使示例代码变得冗长且难以理解。 25 26【正例】 27 28```ts 29// 正例1: 30let want = { 31 bundleName: 'com.example.myapplication', 32 abilityName: 'EntryAbility' 33}; 34 35this.context.startAbility(want) 36 .then(() => { 37 // 执行正常业务 38 console.info('Succeeded in starting ability.'); 39 }) 40 .catch((err) => { 41 // 处理业务逻辑错误 42 console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); 43 }); 44 45// 正例2: 46let pathDir = ...; // 应用文件路径 47let filePath = pathDir + '/test.txt'; 48let str = fs.readTextSync(filePath, { offset: 1, length: 3 }); 49console.info(`Succeed in reading text, str is ${str}`); 50``` 51 52【反例】 53 54```ts 55// 反例1: 56let want = { 57 bundleName: 'com.example.myapplication', 58 abilityName: 'EntryAbility' 59}; 60 61try { 62 this.context.startAbility(want) 63 .then(() => { 64 // 执行正常业务 65 console.info('Succeeded in starting ability.'); 66 }) 67 .catch((err) => { 68 // 处理业务逻辑错误 69 console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); 70 }); 71} catch (err) { 72 // 处理入参错误异常 73 console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); 74} 75 76// 反例2: 77let pathDir = ...; // 应用文件路径 78let filePath = pathDir + '/test.txt'; 79try { 80 let str = fs.readTextSync(filePath, { offset: 1, length: 3 }); 81 console.info(`Succeed in reading text, str is ${str}`); 82} catch (err) { 83 console.error(`Failed to read text. Code is ${err.code}, message is ${err.message}`); 84} 85``` 86 87### 【规则】示例代码中的变量需要包含定义、使用方法或者来源链接参考或者说明 88 89【描述】 90 91示例代码中的变量需要包含定义、使用方法或者来源链接参考或者说明,以确保开发者能够理解如何使用。例如,如果涉及到应用开发路径,需要提供获取应用开发路径的链接参考或方法。 92 93【正例】 94 95示例中的context的获取方式请参见[获取UIAbility的上下文信息](../../application-dev/application-models/uiability-usage.md#获取uiability的上下文信息)。 96 97```ts 98let context = ...; // UIAbilityContext 99let want = { 100 deviceId: '', // deviceId为空表示本设备 101 bundleName: 'com.example.myapplication', 102 abilityName: 'FuncAbility', 103 moduleName: 'func', // moduleName非必选 104 parameters: { // 自定义信息 105 info: '来自EntryAbility Index页面', 106 }, 107} 108// context为调用方UIAbility的UIAbilityContext 109context.startAbilityForResult(want).then((data) => { 110 ... 111}).catch((err) => { 112 console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); 113}) 114``` 115 116【反例】 117 118```ts 119// 反例1:使用到的context和want变量未进行定义 120// context为调用方UIAbility的UIAbilityContext 121context.startAbilityForResult(want).then((data) => { 122 ... 123}).catch((err) => { 124 console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); 125}) 126 127// 反例2:UIAbilityContext的使用来源不止this.context这一种,这里列举出来就会有展示不全的问题 128let context = this.context; // UIAbilityContext 129let want = { 130 deviceId: '', // deviceId为空表示本设备 131 bundleName: 'com.example.myapplication', 132 abilityName: 'FuncAbility', 133 moduleName: 'func', // moduleName非必选 134 parameters: { // 自定义信息 135 info: '来自EntryAbility Index页面', 136 }, 137} 138// context为调用方UIAbility的UIAbilityContext 139context.startAbilityForResult(want).then((data) => { 140 ... 141}).catch((err) => { 142 console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); 143}) 144``` 145 146### 【建议】一致的依赖包命名风格 147 148【描述】 149 150导入的依赖包的命名与其依赖包的命名空间保持一致,以便于维护和理解代码。 151 152采用一致的依赖包命名风格还可以方便IDE进行提示导入,提高编码效率。 153 154【正例】 155 156```ts 157import promptAction from '@ohos.promptAction'; 158``` 159 160【反例】 161 162```ts 163// 包名和其命名空间不一致,不利于维护和理解代码 164import prompt from '@ohos.promptAction'; 165``` 166 167### 【规则】组件宽高等属性不加单位 168 169【描述】 170 171为了保持代码的一致性和简洁性,在设置组件的宽度、高度等属性时,应该尽量避免添加单位,因为组件的宽度、高度等属性默认以像素为单位。同时,避免添加单位也可以提高代码的可读性和便于维护。 172 173【正例】 174 175```ts 176Image('test.png') 177 .width(100) 178 .height(100) 179 180Text('Hello World') 181 .fontSize(50) 182``` 183 184【反例】 185 186```ts 187Image('test.png') 188 .width('100vp') 189 .height('100vp') 190 191Text('Hello World') 192 .fontSize('50fp') 193``` 194 195## 代码展示 196 197### 【规则】行内代码使用`包裹显示 198 199【描述】 200 201正文描述中涉及代码的内容,比如实际代码中的方法名、参数名或代码文件名等,使用`包裹显示。 202 203【正例】 204 205在`Index.ets`文件中实现页面跳转。 206 207【反例】 208 209在Index.ets文件中实现页面跳转。 210 211### 【规则】代码示例、命令行使用代码样式进行代码染色 212 213【描述】 214 215对代码示例、命令行使用代码样式。在Markdown中,使用```呈现代码样式,同时指定语言类型。 216 217代码染色是指在编辑器中对代码进行不同颜色的标记,以区分不同语法元素的功能。例如在编辑器中对不同的关键字、变量名、注释等使用不同的颜色进行标记,可以让代码更加易读易懂。 218 219 220 221### 【规则】代码格式化 222 223【描述】 224 225在将代码示例放入指南之前,使用DevEco Studio中的代码格式化功能对代码进行格式化,以确保代码的一致性和可读性。 226 227格式化代码的方法包括缩进、空格、换行等,这些方法可以使代码更易于阅读和理解,提高代码的可维护性和扩展性。 228 229【正例】 230 231```ts 232import UIAbility from '@ohos.app.ability.UIAbility'; 233import window from '@ohos.window'; 234 235export default class EntryAbility extends UIAbility { 236 onWindowStageCreate(windowStage: window.WindowStage) { 237 windowStage.loadContent('pages/Index', (err, data) => { 238 }); 239 } 240} 241``` 242 243【反例】 244 245```ts 246import UIAbility from '@ohos.app.ability.UIAbility'; 247import window from '@ohos.window'; 248 249export default class EntryAbility extends UIAbility { 250 onWindowStageCreate(windowStage: window.WindowStage) { 251 // 代码未格式化,没有缩进 252 windowStage.loadContent('pages/Index', (err, data) => { 253 }); 254 } 255} 256``` 257 258### 【规则】省略代码展示 259 260【描述】 261 262当需要在文档或代码中展示省略的部分时,使用统一的省略代码格式。省略代码应该简洁明了,避免冗长或混乱的格式。 263 264【正例】 265 266```ts 267// 正例1: 268... 269 270// 正例2: 271// To do sthing. 272``` 273 274【反例】 275 276```ts 277// 反例1: 278// ... 279 280// 反例2: 281.... 282 283// 反例3: 284...... 285``` 286 287### 【规则】代码注释展示 288 289【描述】 290 291示例代码中的关键内容和逻辑需要添加注释来说明,以确保开发者理解代码的作用。 292 293适时为代码块添加注释,特别是有解释说明、开发建议或注意事项的位置。恰当的注释可有效提升代码块可读性,帮助开发者快速掌握开发过程。 294 295注释符与代码块语法保持一致,禁止自创注释符。注释符与注释内容间统一添加一个空格。例如:对于ArkTS代码块,注释写法为“// 注释内容”。 296 297当一行注释内容过长时,注意断句切分到下一行呈现。 298 299代码注释应该清晰、简洁、有用,能够方便别人理解代码的含义和作用。注释应该写在代码上方或右方。 300 301【正例】 302 303```ts 304// 正例1: 305// 定义生命周期回调对象 306let abilityLifecycleCallback = {}; 307 308// 正例2: 309let abilityLifecycleCallback = {}; // 定义生命周期回调对象 310``` 311 312【反例】 313 314```ts 315// 反例1:注释符与注释内容之间没有添加空格 316//定义生命周期回调对象 317let abilityLifecycleCallback = {}; 318 319// 反例2:注释符与注释内容之间没有添加空格 320let abilityLifecycleCallback = {}; //定义生命周期回调对象 321 322// 反例3:注释符与注释内容之间添加了多个空格 323let abilityLifecycleCallback = {}; // 定义生命周期回调对象 324``` 325 326## 异常处理 327 328### 【规则】在可能出现异常的代码段加上异常捕获,并按不同的异常类型进行分支判断 329 330【描述】 331 332在可能出现异常的代码段加上异常捕获,并按不同的异常类型进行分支判断,针对不同的异常类型需要使用统一的异常分支判断方式。 333 334【正例】 335 336```ts 337// 情形1:err为undefined的场景 338if (err) { 339 ... 340} 341 342// 情形2:err.code为非0的场景 343if (err.code) { 344 ... 345} 346``` 347 348【反例】 349 350```ts 351// 反例1: 352if (err == null) { 353 ... 354} 355 356// 反例2: 357if (err != null) { 358 ... 359} 360 361// 反例3: 362if (err == undefined) { 363 ... 364} 365 366// 反例4: 367if (err === undefined) { 368 ... 369} 370 371// 反例5: 372if (err !== undefined) { 373 ... 374} 375``` 376 377### 【规则】使用console.error输出异常的详细信息,包含错误码和相关的message信息 378 379【描述】 380 381当存在异常情况时,统一使用`console.error(...)`方法将异常信息打印到控制台,以便在调试时能够快速发现问题。 382 383在异常处理中,应该打印出异常的详细信息以便调试。 384 385在打印异常信息时,应该使用模板字符串,并标明异常信息的`code`和`message`参数。 386 387【正例】 388 389```ts 390// 模板: 391console.error(`Failed to do sthing. Code: ${err.code}, message: ${err.message}`); 392 393// 正例: 394notificationManager.publish(notificationRequest, (err) => { 395 if (err) { 396 // 异常分支打印 397 console.error(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); 398 return; 399 } 400 ... 401}); 402``` 403 404【反例】 405 406```ts 407// 反例1:错误日志使用console.log输出,不足以让开发者在调试时快速找到问题 408notificationManager.publish(notificationRequest, (err) => { 409 if (err) { 410 // 异常分支打印 411 console.log(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); 412 return; 413 } 414 ... 415}); 416 417// 反例2:错误日志使用console.info输出,而非console.error,不利于开发者区分日志级别,快速找到问题 418notificationManager.publish(notificationRequest, (err) => { 419 if (err) { 420 // 异常分支打印 421 console.info(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); 422 return; 423 } 424 ... 425}); 426 427// 反例3:异常信息缺乏具体的code和message参数,不利于开发者定位和解决问题 428console.error(`Failed to publish notification, err: ${err}`); 429 430// 反例4:使用单引号而非模板字符串的双引号,导致变量无法解析,输出的日志信息不正确 431console.error('Failed to publish notification, err: ${err}'); 432 433// 反例5:使用字符串拼接和JSON序列化输出错误信息,不够直观和简洁,会增加日志产生量,不利于快速定位问题 434console.error('Failed to publish notification, err: ' + JSON.stringify(err)); 435``` 436 437## 日志打印 438 439### 【规则】正常日志打印使用console.info,以提供程序运行的状态和关键信息 440 441【描述】 442 443在代码编写过程中,为了更好地记录程序运行过程中的信息,开发者需要使用日志打印,以便于在程序出现异常时进行定位和排查。 444 445在正常情况下,使用`console.info(...)`来打印正常日志信息。 446 447【正例】 448 449```ts 450// 模板: 451console.info('Succeeded in doing sthing.'); 452 453// 正例: 454notificationManager.publish(notificationRequest, (err) => { 455 if (err) { 456 ... 457 return; 458 } 459 console.info('Succeeded in publishing notification.'); 460}); 461``` 462 463【反例】 464 465```ts 466// 反例1:使用console.log(...)可能会让程序员产生困惑,无法明确该日志信息是正常日志还是错误日志 467notificationManager.publish(notificationRequest, (err) => { 468 if (err) { 469 ... 470 return; 471 } 472 console.log('Succeeded in publishing notification.'); 473}); 474 475// 反例2:使用了console.error(...)而不是console.info(...)来打印正常日志信息。console.error通常用于打印错误信息,而不是正常的日志信息 476notificationManager.publish(notificationRequest, (err) => { 477 if (err) { 478 ... 479 return; 480 } 481 console.error('Succeeded in publishing notification.'); 482}); 483``` 484 485### 【规则】正常成功日志打印使用succeeded,以表示程序执行成功的结果 486 487【描述】 488 489在日志打印中使用一致的语言,例如成功日志可以使用`succeeded`来表达。 490 491【正例】 492 493```ts 494// 模板: 495console.info('Succeeded in doing sthing.'); 496 497// 正例: 498notificationManager.publish(notificationRequest, (err) => { 499 if (err) { 500 ... 501 return; 502 } 503 console.info('Succeeded in publishing.'); 504}); 505``` 506 507【反例】 508 509```ts 510// 反例1: 511notificationManager.publish(notificationRequest, (err) => { 512 if (err) { 513 ... 514 return; 515 } 516 console.info('Invoke publish success.'); 517}); 518 519// 反例2: 520notificationManager.publish(notificationRequest, (err) => { 521 if (err) { 522 ... 523 return; 524 } 525 console.info('Invoke publish successful.'); 526}); 527 528// 反例3: 529notificationManager.publish(notificationRequest, (err) => { 530 if (err) { 531 ... 532 return; 533 } 534 console.info('Invoke publish successfully.'); 535}); 536``` 537 538## 代码逻辑 539 540### 【规则】回调方法中使用箭头函数替代常规函数 541 542【描述】 543 544在使用回调方法时,应该尽可能使用箭头方法代替常规方法,箭头方法的一个主要优点是可以避免 `this` 指向问题。在箭头方法中,`this` 绑定的是它所处的上下文的 `this` 值,而不是方法被调用时的 `this` 值。这样可以避免在使用常规方法时需要使用 `bind()` 或 `call()` 来绑定 `this` 的问题,从而简化代码并提高可读性。 545 546【正例】 547 548```ts 549notificationManager.publish(notificationRequest, (err) => { 550 if (err) { 551 ... 552 return; 553 } 554 console.info('Succeeded in publishing notification.'); 555}); 556``` 557 558【反例】 559 560```ts 561notificationManager.publish(notificationRequest, function (err) { 562 if (err) { 563 ... 564 return; 565 } 566 console.info('Succeeded in publishing notification.'); 567}); 568``` 569 570### 【规则】禁止使用废弃接口 571 572【描述】 573 574禁止使用废弃接口,因为他们可能会在未来的版本中被删除或更改。使用最新的接口可以确保代码的稳定性和可维护性。 575 576使用废弃接口可能导致代码不稳定,也可能会因为该接口在后续版本的更新中被废弃而引发编译错误、运行错误等问题。 577 578如果在后续的版本中废除了某些接口,所有依赖这些接口的示例代码都需要同步适配修改。 579 580【正例】 581 582```ts 583import fs from '@ohos.file.fs'; 584 585function createFile() { 586 // 获取应用文件路径 587 let context = ...; // UIAbilityContext 588 let filesDir = context.filesDir; 589 590 // 新建并打开文件 591 let file = fs.openSync(filesDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 592 // 写入一段内容至文件 593 let writeLen = fs.writeSync(file.fd, 'Try to write str.'); 594 // 从文件读取一段内容 595 let buf = new ArrayBuffer(1024); 596 let readLen = fs.readSync(file.fd, buf, { offset: 0 }); 597 // 关闭文件 598 fs.closeSync(file); 599} 600``` 601 602【反例】 603 604```ts 605// 使用了废弃的fileio接口,而不是推荐的fs接口 606import fileio from '@ohos.fileio'; 607 608function createFile() { 609 // 获取应用文件路径 610 let context = ...; // UIAbilityContext 611 let filesDir = context.filesDir; 612 613 // 新建并打开文件 614 let fileFD = fileio.openSync(filesDir + '/test.txt', 0o102, 0o640); 615 // 写入一段内容至文件 616 let writeLen = fileio.writeSync(fileFD, 'Try to write str.'); 617 // 从文件读取一段内容 618 let buf = new ArrayBuffer(1024); 619 let readLen = fileio.readSync(fileFD, buf, { offset: 0 }); 620 // 关闭文件 621 fileio.closeSync(fileFD); 622} 623```