示例代码风格
本规范适用于文档中ArkTS、JavaScript和C/C++等编程语言的示例代码片段,旨在提高OpenHarmony文档示例代码的可读性、可维护性,以及风格一致性。
【规则】遵守基本编程规范
Section titled “【规则】遵守基本编程规范”【描述】
文档的示例代码需要遵循OpenHarmony应用ArkTS编程规范、JavaScript语言编程规范、C语言编程规范和C++语言编程规范基本的编码规范,包括命名规范、代码格式和代码规范等。
【规则】每个接口提供示例代码
Section titled “【规则】每个接口提供示例代码”【描述】
API参考中,每个接口(包括方法和组件)均需要提供示例代码。如果多个API存在关联关系,则需要在一个场景化的代码示例中体现。
【规则】开发指南示例代码包含异常处理
Section titled “【规则】开发指南示例代码包含异常处理”【描述】
开发指南旨在提供全面的代码示例,帮助开发者在实际应用中正确使用API。为了确保代码的健壮性和可靠性,示例代码中需要包含异常处理逻辑。这有助于开发者理解如何在不同情况下处理错误,提高代码质量。
通过在开发指南的示例代码中添加异常处理,开发者可以更好地理解如何编写健壮的代码,并在实际开发中应用这些实践。
【正例】
import { BusinessError } from '@kit.BasicServicesKit';
declare function doSthAsync1(): Promise<void>;
declare function doSthAsync2(): Promise<void>;
declare function doSthAsync3(): Promise<void>;
declare function doSthSync1(): void;
declare function doSthSync2(): void;
// 正例1:异步接口,Promise场景,加上`.catch()`相关的分支判断doSthAsync1() .then(() => { }) .catch((err: BusinessError) => { console.error(`Failed to do something. Code is ${err.code}, message is ${err.message}`); });
// 正例2:异步接口,结合async/await使用,增加`try...catch...`包裹async () => { try { await doSthAsync1(); await doSthAsync2(); await doSthAsync3(); } catch (error) { const err: BusinessError = error as BusinessError; // 处理入参错误异常 console.error(`Failed to do something. Code is ${err.code}, message is ${err.message}`); }}
// 正例3:同步接口,增加`try...catch...`包裹try { doSthSync1(); doSthSync2();} catch (error) { const err: BusinessError = error as BusinessError; // 处理入参错误异常 console.error(`Failed to do something. Code is ${err.code}, message is ${err.message}`);}
// 正例4:对于同步接口增加`try...catch...`包裹,按需增加对应的异常错误码判断try { doSthSync1(); doSthSync2();} catch (err) { switch (err.code) { case 401: // Handle parameters exceptions break; case 12300001: // Handle 12300001 exceptions break; case 12300002: case 12300003: case 12300004: console.error(`Failed to do something. Code is ${err.code}, message is ${err.message}`); break; }}【反例】
declare function doSthAsync1(): Promise<void>;
declare function doSthAsync2(): Promise<void>;
declare function doSthAsync3(): Promise<void>;
declare function doSthSync1(): void;
declare function doSthSync2(): void;
// 反例1:异步接口,缺少`.catch()`异常处理doSthAsync1() .then(() => { })
// 反例2:异步接口,结合async/await使用,缺少`try...catch...`异常处理async () => { await doSthAsync1(); await doSthAsync2(); await doSthAsync3();}
// 反例3:同步接口,缺少`try...catch...`异常处理doSthSync1();doSthSync2();【规则】明确变量定义与用途
Section titled “【规则】明确变量定义与用途”【描述】
示例代码中的变量需要包含定义、使用方法或者来源链接参考或者说明,以确保开发者能够理解如何使用。例如,如果涉及到应用开发路径,需要提供获取应用开发路径的链接参考或方法。
【正例】
示例中的context的获取方式请参见获取UIAbility的上下文信息。
import { common, Want } from '@kit.AbilityKit';
const context: common.UIAbilityContext = this.context; // UIAbilityContextlet want: Want = { deviceId: '', // deviceId为空表示本设备 bundleName: 'com.example.myapplication', abilityName: 'FuncAbility', moduleName: 'func', // moduleName非必选 parameters: { // 自定义信息 info: '来自EntryAbility Index页面', },}// context为调用方UIAbility的UIAbilityContextcontext.startAbilityForResult(want) .then((data) => { // ... })【反例】
// 反例:使用到的context和want变量未进行定义// context为调用方UIAbility的UIAbilityContextcontext.startAbilityForResult(want) .then((data) => { // ... })【建议】统一依赖包命名风格
Section titled “【建议】统一依赖包命名风格”【描述】
导入的依赖包的命名与其依赖包的命名空间保持一致,以便于维护和理解代码。
采用一致的依赖包命名风格还可以方便IDE进行提示导入,提高编码效率。
【正例】
import { promptAction } from '@kit.ArkUI';【反例】
// 包名和其命名空间不一致,不利于维护和理解代码import { promptAction as prompt } from '@kit.ArkUI';【规则】统一使用Kit化的导包风格
Section titled “【规则】统一使用Kit化的导包风格”【描述】
针对相关开放能力,可直接导入接口模块来使用该模块内的能力,也可以通过导入Kit的方式使用其开放能力。
对于同时支持这两种方式的模块,使用其相关能力时,推荐使用Kit化的导入方式。
【正例】
// 导入Kit使用对应能力import { UIAbility } from '@kit.AbilityKit';【反例】
// 直接导入接口模块使用对应能力import UIAbility from '@ohos.app.ability.UIAbility';【规则】UI组件宽高等属性不加单位
Section titled “【规则】UI组件宽高等属性不加单位”【描述】
为了保持代码的一致性和简洁性,在设置组件的宽度、高度等属性时,应该尽量避免添加单位(例如vp/fp/px),因为组件的宽度、高度等属性默认以像素为单位。同时,避免添加单位也可以提高代码的可读性和便于维护。
【正例】
Text('Hello World') .width(100) .height(100)
Text('Hello World') .fontSize(50)【反例】
Text('Hello World') .width('100vp') .height('100vp')
Text('Hello World') .width('300px') .height('400px')
Text('Hello World') .fontSize('50fp')【规则】行内代码使用反引号显示
Section titled “【规则】行内代码使用反引号显示”【描述】
正文描述中涉及代码的内容,比如实际代码中的方法名、参数名或代码文件名等,使用`包裹显示。
【正例】
在Index.ets文件中实现页面跳转。
【反例】
在Index.ets文件中实现页面跳转。
【规则】代码示例使用代码块进行代码染色
Section titled “【规则】代码示例使用代码块进行代码染色”【描述】
对代码示例、命令行使用代码样式。在Markdown中,使用```呈现代码样式,同时指定语言类型。
代码染色是指在编辑器中对代码进行不同颜色的标记,以区分不同语法元素的功能。例如在编辑器中对不同的关键字、变量名、注释等使用不同的颜色进行标记,可以让代码更加易读易懂。

【规则】代码格式化
Section titled “【规则】代码格式化”【描述】
在将代码示例放入指南之前,使用DevEco Studio中的代码格式化功能对代码进行格式化,以确保代码的一致性和可读性。
格式化代码的方法包括缩进、空格、换行等,这些方法可以使代码更易于阅读和理解,提高代码的可维护性和扩展性。
【正例】
import { window } from '@kit.ArkUI';import { UIAbility } from '@kit.AbilityKit';import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage) { windowStage.loadContent('pages/Index', (err: BusinessError, data) => { }); }}【反例】
import { window } from '@kit.ArkUI';import { UIAbility } from '@kit.AbilityKit';import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage) { // 代码未格式化,没有缩进 windowStage.loadContent('pages/Index', (err: BusinessError, data) => { }); }}【规则】使用省略符展示代码省略部分
Section titled “【规则】使用省略符展示代码省略部分”【描述】
当需要在文档或代码中展示省略的部分时,使用统一的省略代码格式。省略代码应该简洁明了,避免冗长或混乱的格式。
【正例】
// 正例1// ...
// 正例2// To do something.【反例】
// 反例1...
// 反例2....
// 反例3......【规则】添加清晰的代码注释
Section titled “【规则】添加清晰的代码注释”【描述】
示例代码中的关键内容和逻辑需要添加注释来说明,以确保开发者理解代码的作用。
适时为代码块添加注释,特别是有解释说明、开发建议或注意事项的位置。恰当的注释可有效提升代码块可读性,帮助开发者快速掌握开发过程。
注释符与代码块语法保持一致,禁止自创注释符。注释符与注释内容间统一添加一个空格。例如:对于ArkTS代码块,注释写法为“// 注释内容”。
当一行注释内容过长时,注意断句切分到下一行呈现。
代码注释应该清晰、简洁、有用,能够方便别人理解代码的含义和作用。注释应该写在代码上方或右方。
【正例】
// 正例1// 定义生命周期回调对象let abilityLifecycleCallback = {};
// 正例2let abilityLifecycleCallback = {}; // 定义生命周期回调对象【反例】
// 反例1:注释符与注释内容之间没有添加空格//定义生命周期回调对象let abilityLifecycleCallback = {};
// 反例2:注释符与注释内容之间没有添加空格let abilityLifecycleCallback = {}; //定义生命周期回调对象
// 反例3:注释符与代码行之间添加了多个空格let abilityLifecycleCallback = {}; // 定义生命周期回调对象【规则】同一个代码块中只放置单个文件的代码内容
Section titled “【规则】同一个代码块中只放置单个文件的代码内容”【描述】
在技术文档中展示代码示例时,应遵循单一文件代码块原则。这意味着每个代码块应只包含来自单个文件的代码内容。这种做法有助于提高代码的可读性、可理解性,并且更贴近实际开发环境。混合多个文件的代码可能会导致读者混淆,难以理解代码的结构和上下文关系。通过遵循这一原则,可以确保代码示例清晰、有组织,并且易于复制和实施。
【正例】
将不同文件的代码分别放在独立的代码块中。这种方式清晰地展示了每个文件的内容,使读者能够轻松理解文件结构和组件之间的关系。例如:
-
开发
MyComponent自定义组件。// MyComponent.ets@Componentexport struct MyComponent {@State message: string = 'Hello, World!';build() {Row() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.height('100%')}} -
在
Index.ets文件中引用该自定义组件。// pages/Index.etsimport { MyComponent } from '../common/components/MyComponent';@Entry@Componentstruct Index {build() {Column() {MyComponent()}.width('100%').height('100%')}}
【反例】
将多个文件的代码混合在一个代码块中,这可能会导致读者难以区分不同文件的边界,增加理解难度,同时也不利于代码的复制和实际应用。例如:
开发MyComponent自定义组件。在Index.ets文件中引用该自定义组件。
// MyComponent.ets@Componentexport struct MyComponent { @State message: string = 'Hello, World!';
build() { Row() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) } .height('100%') }}
// pages/Index.etsimport { MyComponent } from '../common/components/MyComponent';@Entry@Componentstruct Index { build() { Column() { MyComponent() } .width('100%') .height('100%') }}【规则】在可能的异常处添加异常捕获
Section titled “【规则】在可能的异常处添加异常捕获”【描述】
在可能出现异常的代码段加上异常捕获,并按不同的异常类型进行分支判断,针对不同的异常类型需要使用统一的异常分支判断方式。
【正例】
// 正例1:情形一、err为undefined的场景if (err) { // ...}
// 正例2:情形二、err.code为非0的场景if (err.code) { // ...}【反例】
// 反例1if (err == null) { // ...}
// 反例2if (err != null) { // ...}
// 反例3if (err == undefined) { // ...}
// 反例4if (err === undefined) { // ...}
// 反例5if (err !== undefined) { // ...}【规则】如果使用console打印日志,请使用console.error输出详细异常信息
Section titled “【规则】如果使用console打印日志,请使用console.error输出详细异常信息”【描述】
当存在异常情况时,统一使用console.error(...)方法将异常信息打印到控制台,以便在调试时能够快速发现问题。
在异常处理中,应该打印出异常的详细信息以便调试。
在打印异常信息时,应该使用模板字符串,并标明异常信息的code和message参数。
【正例】
// 模板console.error(`Failed to do something. Code: ${err.code}, message: ${err.message}`);
// 正例1notificationManager.publish(notificationRequest, (err: BusinessError) => { if (err) { // 异常分支打印 console.error(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); return; } // ...});
// 正例2notificationManager.publish(notificationRequest) .then(() => { // ... }) .catch((err: BusinessError) => { // 异常分支打印 console.error(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); })
// 正例3let pathDir: string = '<pathDir>'; // 应用文件路径let filePath: string = pathDir + '/test.txt';try { let str: string = fs.readTextSync(filePath, { offset: 1, length: 3 }); console.info(`Succeeded in reading text, str is ${str}`);} catch (error) { const err: BusinessError = error as BusinessError; console.error(`Failed to read text. Code is ${err.code}, message is ${err.message}`);}【反例】
// 反例1:错误日志使用console.log输出,不足以让开发者在调试时快速找到问题notificationManager.publish(notificationRequest, (err: BusinessError) => { if (err) { // 异常分支打印 console.log(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); return; } // ...});
// 反例2:错误日志使用console.info输出,而非console.error,不利于开发者区分日志级别,快速找到问题notificationManager.publish(notificationRequest, (err: BusinessError) => { if (err) { // 异常分支打印 console.info(`Failed to publish notification. Code: ${err.code}, message: ${err.message}`); return; } // ...});
// 反例3:异常信息缺乏具体的code和message参数,不利于开发者定位和解决问题console.error(`Failed to publish notification, err: ${err}`);
// 反例4:使用单引号而非模板字符串的双引号,导致变量无法解析,输出的日志信息不正确console.error('Failed to publish notification, err: ${err}');
// 反例5:使用字符串拼接和JSON序列化输出错误信息,不够直观和简洁,会增加日志产生量,不利于快速定位问题console.error('Failed to publish notification, err: ' + JSON.stringify(err));【规则】如果使用console打印日志,使用console.info进行正常日志打印
Section titled “【规则】如果使用console打印日志,使用console.info进行正常日志打印”【描述】
在代码编写过程中,为了更好地记录程序运行过程中的信息,开发者需要使用日志打印,以便于在程序出现异常时进行定位和排查。
在正常情况下,使用console.info(...)来打印正常日志信息。
【正例】
// 模板console.info('Succeeded in doing something.');
// 正例declare function doSthAsync1(): Promise<void>;
doSthAsync1() .then(() => { console.info('Succeeded in publishing notification.'); })【反例】
declare function doSthAsync1(): Promise<void>;
// 反例1:使用console.log(...)可能会让程序员产生困惑,无法明确该日志信息是正常日志还是错误日志doSthAsync1() .then(() => { console.log('Succeeded in publishing notification.'); })
// 反例2:使用了console.error(...)而不是console.info(...)来打印正常日志信息。console.error通常用于打印错误信息,而不是正常的日志信息doSthAsync1() .then(() => { console.error('Succeeded in publishing notification.'); })【规则】打印日志信息时,注意遵循英文语法
Section titled “【规则】打印日志信息时,注意遵循英文语法”【描述】
在日志打印时,注意遵循英文语法,日志信息不要有英文语法错误。
例如成功日志可以使用Succeeded in doing something的句式来表达;也可以使用do something successfully的句式来表达。
例如错误日志可以使用Failed to do something的句式来表达。
【正例】
// 模板console.info('Succeeded in doing something.');
// 正例declare function doSthAsync1(): Promise<void>;
doSthAsync1() .then(() => { console.info('Succeeded in doing something.'); })【反例】
declare function doSthAsync1(): Promise<void>;
// 反例1doSthAsync1() .then(() => { console.info('Invoke do something success.'); })
// 反例2doSthAsync1() .then(() => { console.info('Invoke do something successful.'); })
// 反例3doSthAsync1() .then(() => { console.info('Invoke do something successfully.'); })【规则】回调方法中使用箭头函数
Section titled “【规则】回调方法中使用箭头函数”【描述】
在使用回调方法时,应该尽可能使用箭头方法代替常规方法,箭头方法的一个主要优点是可以避免 this 指向问题。在箭头方法中,this 绑定的是它所处的上下文的 this 值,而不是方法被调用时的 this 值。这样可以避免在使用常规方法时需要使用 bind() 或 call() 来绑定 this 的问题,从而简化代码并提高可读性。
【正例】
notificationManager.publish(notificationRequest, (err: BusinessError) => { if (err) { // ... return; } console.info('Succeeded in publishing notification.');});【反例】
notificationManager.publish(notificationRequest, function (err: BusinessError) { if (err) { // ... return; } console.info('Succeeded in publishing notification.');});【规则】禁止使用废弃接口
Section titled “【规则】禁止使用废弃接口”【描述】
禁止使用废弃接口,因为他们可能会在未来的版本中被删除或更改。使用最新的接口可以确保代码的稳定性和可维护性。
使用废弃接口可能导致代码不稳定,也可能会因为该接口在后续版本的更新中被废弃而引发编译错误、运行错误等问题。
如果在后续的版本中废除了某些接口,所有依赖这些接口的示例代码都需要同步适配修改。
【正例】
import { common } from '@kit.AbilityKit';import { fileIo } from '@kit.CoreFileKit';
/** * 获取应用文件路径 **/const context: common.UIAbilityContext = this.context; // UIAbilityContextlet filesDir: string = context.filesDir;
// 新建并打开文件let file: fileIo.File = fileIo.openSync(filesDir + '/test.txt', fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);// 写入一段内容至文件let writeLen: number = fileIo.writeSync(file.fd, 'Try to write str.');// 从文件读取一段内容let buf: ArrayBuffer = new ArrayBuffer(1024);let readLen: number = fileIo.readSync(file.fd, buf, { offset: 0});// 关闭文件fileIo.closeSync(file);【反例】
// 使用了废弃的fileio接口,而不是推荐的fileIo接口import fileio from '@ohos.fileio';import common from '@ohos.app.ability.common';
// 获取应用文件路径const context: common.UIAbilityContext = this.context; // UIAbilityContextlet filesDir: string = context.filesDir;
// 新建并打开文件let fileFD: number = fileio.openSync(filesDir + '/test.txt', 0o102, 0o640);// 写入一段内容至文件let writeLen: number = fileio.writeSync(fileFD, 'Try to write str.');// 从文件读取一段内容let buf: ArrayBuffer = new ArrayBuffer(1024);let readLen: number = fileio.readSync(fileFD, buf, { offset: 0 });// 关闭文件fileio.closeSync(fileFD);