• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
209Index.ets文件中实现页面跳转。
210
211### 【规则】代码示例、命令行使用代码样式进行代码染色
212
213【描述】
214
215对代码示例、命令行使用代码样式。在Markdown中,使用```呈现代码样式,同时指定语言类型。
216
217代码染色是指在编辑器中对代码进行不同颜色的标记,以区分不同语法元素的功能。例如在编辑器中对不同的关键字、变量名、注释等使用不同的颜色进行标记,可以让代码更加易读易懂。
218
219![代码块示例](figures/code-block-example.png)
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```