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