高效 Dart
在过去的几年里,我们编写了大量的 Dart 代码,并从中学习到了哪些有效,哪些无效。我们与您分享这些经验,以便您也能编写出一致、健壮、快速的代码。其中有两个总体主题:
保持一致性。 当涉及到格式和大小写等问题时,关于哪个更好的争论是主观的,无法解决。我们知道的是,保持一致性客观上是有帮助的。
如果两段代码看起来不同,那应该是因为它们在某种有意义的方式上确实不同。当一段代码引人注目时,它应该有一个有用的理由。
简洁。 Dart 的设计旨在易于上手,因此它继承了 C、Java、JavaScript 和其他语言的许多相同语句和表达式。但我们创建 Dart 是因为这些语言所提供的功能仍有很大的改进空间。我们添加了许多功能,从字符串插值到初始化形式,以帮助您更简单、更容易地表达您的意图。
如果有一种以上的方式来表达某件事,您通常应该选择最简洁的方式。这并不是说您应该像代码高尔夫一样把整个程序塞进一行。目标是代码要经济,而不是密集。
指南
#我们将这些指南分成几个独立的页面,以便于理解:
风格指南 – 这定义了代码布局和组织的规则,或者至少是
dart format
无法为您处理的部分。风格指南还指定了标识符的格式:camelCase
、using_underscores
等。文档指南 – 这告诉您有关注释内容所需的一切知识。包括文档注释和普通的、一般的代码注释。
用法指南 – 这教您如何最佳地利用语言特性来实现行为。如果它在语句或表达式中,这里都会涵盖。
设计指南 – 这是最“软”的指南,但范围最广。它涵盖了我们所学到的关于为库设计一致、可用 API 的知识。如果它在类型签名或声明中,这里都会涉及。
有关所有指南的链接,请参阅摘要。
如何阅读指南
#每个指南都分为几个部分。每个部分包含一个指南列表。每个指南都以以下单词之一开头:
DO(务必) 指南描述了应始终遵循的实践。几乎永远没有偏离它们的有效理由。
DON'T(切勿) 指南是其反面:几乎永远不是好主意的事情。希望我们没有像其他语言那样多的这类指南,因为我们历史包袱较少。
PREFER(优先) 指南是您应该遵循的实践。但是,在某些情况下,可能需要采取其他做法。只是在您这样做时,请务必了解忽略该指南的全部影响。
AVOID(避免) 指南是“优先”的反面:您不应该做的事情,但极少数情况下可能有充分理由去做。
CONSIDER(考虑) 指南是您可能或可能不希望遵循的实践,具体取决于情况、先例和您自己的偏好。
有些指南描述了规则不适用的例外情况。列出的例外可能不详尽——您可能仍需要根据其他情况进行判断。
这听起来像是如果您没有正确系好鞋带,警察就会破门而入。情况没那么糟。这里的大多数指南都是常识,我们都是通情达理的人。目标始终是编写出良好、可读且可维护的代码。
Dart 分析器提供了一个 Linter,可帮助您编写遵循这些及其他指南的良好、一致的代码。如果存在一个或多个可以帮助您遵循指南的linter 规则,则该指南会链接到这些规则。链接使用以下格式:
Linter 规则: unnecessary_getters_setters
要了解如何使用 Linter,请参阅启用 Linter 规则和Linter 规则列表。
术语表
#为了使指南简洁,我们使用了一些简写术语来指代不同的 Dart 结构。
库成员是顶层字段、getter、setter 或函数。基本上,顶层不是类型的所有内容。
类成员是类中声明的构造函数、字段、getter、setter、函数或运算符。类成员可以是实例或静态的,抽象或具体的。
成员是库成员或类成员。
变量,一般而言,指顶层变量、参数和局部变量。它不包括静态或实例字段。
类型是任何命名类型声明:类、typedef 或枚举。
属性是顶层变量、getter(在类中或顶层,实例或静态)、setter(同上)或字段(实例或静态)。大致是指任何“类字段”的命名构造。
所有规则总结
#风格
#标识符
- DO (务必) 使用
UpperCamelCase
命名类型。 - DO (务必) 使用
UpperCamelCase
命名扩展。 - DO (务必) 使用
lowercase_with_underscores
命名包、目录和源文件。 - DO (务必) 使用
lowercase_with_underscores
命名导入前缀。 - DO (务必) 使用
lowerCamelCase
命名其他标识符。 - PREFER (优先) 为常量名使用
lowerCamelCase
。 - DO (务必) 将两个字母以上的缩写和首字母缩略词像单词一样大写。
- PREFER (优先) 对未使用的回调参数使用通配符。
- DON'T (切勿) 对非私有标识符使用前导下划线。
- DON'T (切勿) 使用前缀字母。
- DON'T (切勿) 显式命名库。
排序
- DO (务必) 将
dart:
导入放在其他导入之前。 - DO (务必) 将
package:
导入放在相对导入之前。 - DO (务必) 在所有导入之后,在一个单独的章节中指定导出。
- DO (务必) 按字母顺序排序各章节。
格式化
文档
#注释
文档注释
- DO (务必) 使用
///
文档注释来注释成员和类型。 - PREFER (优先) 为公共 API 编写文档注释。
- CONSIDER (考虑) 编写库级别的文档注释。
- CONSIDER (考虑) 为私有 API 编写文档注释。
- DO (务必) 用一个单句摘要开始文档注释。
- DO (务必) 将文档注释的第一句话独立成一个段落。
- AVOID (避免) 与周围上下文冗余。
- PREFER (优先) 如果函数的或方法的主要目的是产生副作用,则以第三人称动词开头其注释。
- PREFER (优先) 非布尔变量或属性注释以名词短语开头。
- PREFER (优先) 布尔变量或属性注释以“Whether”开头,后跟名词或动名词短语。
- PREFER (优先) 如果返回一个值是函数或方法的主要目的,则使用名词短语或非祈使动词短语。
- DON'T (切勿) 为属性的 getter 和 setter 都编写文档。
- PREFER (优先) 库或类型注释以名词短语开头。
- CONSIDER (考虑) 在文档注释中包含代码示例。
- DO (务必) 在文档注释中使用方括号来引用作用域内的标识符。
- DO (务必) 使用散文解释参数、返回值和异常。
- DO (务必) 将文档注释放在元数据注解之前。
Markdown
编写
用法
#库
- DO (务必) 在
part of
指令中使用字符串。 - DON'T (切勿) 导入其他包的
src
目录中的库。 - DON'T (切勿) 允许导入路径伸入或伸出
lib
目录。 - PREFER (优先) 使用相对导入路径。
空值
- DON'T (切勿) 显式地将变量初始化为
null
。 - DON'T (切勿) 使用显式的默认值
null
。 - DON'T (切勿) 在相等操作中使用
true
或false
。 - AVOID (避免)
late
变量,如果您需要检查它们是否已初始化。 - CONSIDER (考虑) 使用类型提升或空值检查模式来使用可空类型。
字符串
集合
- DO (务必) 尽可能使用集合字面量。
- DON'T (切勿) 使用
.length
来检查集合是否为空。 - AVOID (避免) 将
Iterable.forEach()
与函数字面量一起使用。 - DON'T (切勿) 使用
List.from()
,除非您打算更改结果的类型。 - DO (务必) 使用
whereType()
按类型过滤集合。 - DON'T (切勿) 在附近操作即可时使用
cast()
。 - AVOID (避免) 使用
cast()
。
函数
变量
成员
- DON'T (切勿) 不必要地将字段包装在 getter 和 setter 中。
- PREFER (优先) 使用
final
字段来创建只读属性。 - CONSIDER (考虑) 对简单成员使用
=>
。 - DON'T (切勿) 使用
this.
,除非是重定向到命名构造函数或避免遮蔽。 - DO (务必) 尽可能在声明时初始化字段。
构造函数
- DO (务必) 尽可能使用初始化形参。
- DON'T (切勿) 在构造函数初始化列表可以做到时使用
late
。 - DO (务必) 对空构造函数体使用
;
而不是{}
。 - DON'T (切勿) 使用
new
。 - DON'T (切勿) 冗余地使用
const
。
错误处理
- AVOID (避免) 没有
on
子句的 catch。 - DON'T (切勿) 丢弃没有
on
子句的 catch 中的错误。 - DO (务必) 仅针对程序错误抛出实现
Error
的对象。 - DON'T (切勿) 显式捕获
Error
或实现它的类型。 - DO (务必) 使用
rethrow
重新抛出捕获的异常。
异步
设计
#名称
- DO (务必) 一致地使用术语。
- AVOID (避免) 缩写。
- PREFER (优先) 将最具描述性的名词放在最后。
- CONSIDER (考虑) 让代码读起来像句子。
- PREFER (优先) 对非布尔属性或变量使用名词短语。
- PREFER (优先) 对布尔属性或变量使用非祈使动词短语。
- CONSIDER (考虑) 省略命名布尔参数的动词。
- PREFER (优先) 为布尔属性或变量使用“肯定”名称。
- PREFER (优先) 对主要目的是产生副作用的函数或方法使用祈使动词短语。
- PREFER (优先) 如果返回一个值是函数或方法的主要目的,则使用名词短语或非祈使动词短语。
- CONSIDER (考虑) 如果您想突出其执行的工作,则为函数或方法使用祈使动词短语。
- AVOID (避免) 以
get
开头方法名。 - PREFER (优先) 如果方法将对象的状态复制到一个新对象,则将其命名为
to___()
。 - PREFER (优先) 如果方法返回由原始对象支持的不同表示,则将其命名为
as___()
。 - AVOID (避免) 在函数或方法的名称中描述参数。
- DO (务必) 在命名类型参数时遵循现有助记符约定。
库
类和 Mixin
- AVOID (避免) 在简单的函数可以做到时定义一个只有一个成员的抽象类。
- AVOID (避免) 定义一个只包含静态成员的类。
- AVOID (避免) 扩展一个不打算被子类化的类。
- DO (务必) 使用类修饰符来控制您的类是否可以被扩展。
- AVOID (避免) 实现一个不打算作为接口的类。
- DO (务必) 使用类修饰符来控制您的类是否可以作为接口。
- PREFER (优先) 定义纯
mixin
或纯class
而不是mixin class
。
构造函数
成员
- PREFER (优先) 将字段和顶层变量设为
final
。 - DO (务必) 对概念上访问属性的操作使用 getter。
- DO (务必) 对概念上改变属性的操作使用 setter。
- DON'T (切勿) 定义没有对应 getter 的 setter。
- AVOID (避免) 使用运行时类型测试来伪造重载。
- AVOID (避免) 没有初始化的公共
late final
字段。 - AVOID (避免) 返回可空的
Future
,Stream
和集合类型。 - AVOID (避免) 为了实现链式调用而从方法返回
this
。
类型
- DO (务必) 为没有初始化的变量添加类型注解。
- DO (务必) 如果类型不明显,则为字段和顶层变量添加类型注解。
- DON'T (切勿) 冗余地为已初始化的局部变量添加类型注解。
- DO (务必) 在函数声明上注解返回类型。
- DO (务必) 在函数声明上注解参数类型。
- DON'T (切勿) 在函数表达式上注解推断的参数类型。
- DON'T (切勿) 为初始化形参添加类型注解。
- DO (务必) 为未推断出的泛型调用编写类型参数。
- DON'T (切勿) 为已推断出的泛型调用编写类型参数。
- AVOID (避免) 编写不完整的泛型类型。
- DO (务必) 使用
dynamic
进行注解,而不是让推断失败。 - PREFER (优先) 函数类型注解中的签名。
- DON'T (切勿) 为 setter 指定返回类型。
- DON'T (切勿) 使用旧式的 typedef 语法。
- PREFER (优先) 行内函数类型而非 typedefs。
- PREFER (优先) 对参数使用函数类型语法。
- AVOID (避免) 使用
dynamic
,除非您想禁用静态检查。 - DO (务必) 使用
Future<void>
作为不产生值的异步成员的返回类型。 - AVOID (避免) 使用
FutureOr<T>
作为返回类型。
参数
- AVOID (避免) 位置布尔参数。
- AVOID (避免) 可选位置参数,如果用户可能想省略前面的参数。
- AVOID (避免) 接受特殊“无参数”值的强制参数。
- DO (务必) 使用包含起始和排他结束参数来接受一个范围。
相等性