目录
Contents
在过去的几年里,我们编写了大量的 Dart 代码,并从中收获了很多经验和教训。我们将与你分享这些经验,这些经验将有助于你编写出一致、健壮、高效的代码。这里包含两主题:
Over the past several years, we’ve written a ton of Dart code and learned a lot about what works well and what doesn’t. We’re sharing this with you so you can write consistent, robust, fast code too. There are two overarching themes:
-
保持一致 当谈论到格式、命名以及参数的相关规则时,哪种规则更好,得出的结论通常是主观且无法达成一致的。但我们知道的是,客观上保持 一致 是非常有益的。
Be consistent. When it comes to things like formatting, and casing, arguments about which is better are subjective and impossible to resolve. What we do know is that being consistent is objectively helpful.
如果两段代码看起来不同,那它们就应该有不同的含义。当一段突出的代码吸引到你的注意时,那它就应该有吸引你的理由。
If two pieces of code look different it should be because they are different in some meaningful way. When a bit of code stands out and catches your eye, it should do so for a useful reason.
-
保持简洁 Dart 会让开发者感到很亲切,因为它继承了许多与 C、Java、JavaScript 及其他语言相同的语句和表达式语法。我们之所以开发 Dart 语言,是因为这些语言依然有很大的改进的空间。我们提供了很多新的特性,比如字符串插值、初始化范式等,以帮助你更简单、更轻松地表达意图。
Be brief. Dart was designed to be familiar, so it inherits many of the same statements and expressions as C, Java, JavaScript and other languages. But we created Dart because there is a lot of room to improve on what those languages offer. We added a bunch of features, from string interpolation to initializing formals, to help you express your intent more simply and easily.
如果有多种方式来描述一件事情,那么你通常应该选择其中最简洁的方式。这并不意味着你要像 Code Golf(代码高尔夫挑战赛)一样将所有代码塞到一行中。而是应该让代码变得 简约 而非 密集。
If there are multiple ways to say something, you should generally pick the most concise one. This is not to say you should code golf yourself into cramming a whole program into a single line. The goal is code that is economical, not dense.
Dart Analyzer 中有一个 Linter 工具,该工具可以帮助你编写优秀的、一致性的代码。如果存在一个 Linter 规则可以帮助你遵循某个指南准则,那么该指南准则将链接到该规则。比如下面的示例:
The Dart analyzer has a linter to help you write good, consistent code. If a linter rule exists that can help you follow a guideline, then the guideline links to that rule. Here’s an example:
Linter rule: prefer_collection_literals
更多关于 开启 Linter 规则 的帮助,请查阅 自定义静态分析 文档。
For help on enabling linter rules, see the documentation for customizing static analysis.
指南
The guides
为了便于理解,这里我们将指南分成了几个部分:
We split the guidelines into a few separate pages for easy digestion:
-
风格指南 – 该部分定义了布局和组织代码的规则,或者说是 dartfmt 不能为你格式化的那些代码的布局和组织规则。风格指南还为你指定了标识符的格式,比如:驼峰式大小写、下划线的使用等等。
Style Guide – This defines the rules for laying out and organizing code, or at least the parts that dartfmt doesn’t handle for you. The style guide also specifies how identifiers are formatted:
camelCase
,using_underscores
, etc. -
注释指南 – 该部分会告诉你注释中应该包含哪些内容。包括文档注释以及常规的普通代码注释。
Documentation Guide – This tells you everything you need to know about what goes inside comments. Both doc comments and regular, run-of-the-mill code comments.
-
使用指南 – 该部分将教你如何充分利用语言特性来实现相关功能。比如你可以在该部分找到如何利用语句或表达式来实现某个功能。
Usage Guide – This teaches you how to make the best use of language features to implement behavior. If it’s in a statement or expression, it’s covered here.
-
设计指南 – 该部分是最易理解但是覆盖范围最广的。其涵盖了我们所总结的为库设计一致、可用 API 的经验。比如这些 API 的类型签名或声明的说明则会在这里找到。
Design Guide – This is the softest guide, but the one with the widest scope. It covers what we’ve learned about designing consistent, usable APIs for libraries. If it’s in a type signature or declaration, this goes over it.
有关所有指南的链接,请查阅 概览。
For links to all the guidelines, see the summary.
如何阅读这些指南
How to read the guides
每个指南都分为了几个部分。每个部分包含一些详细的准则。每条准则都以下面其中的一个词作为开头:
Each guide is broken into a few sections. Sections contain a list of guidelines. Each guideline starts with one of these words:
-
要 准则所描述的内容应该始终被遵守。不应该以任何的理由来偏离违背这些准则。
DO guidelines describe practices that should always be followed. There will almost never be a valid reason to stray from them.
-
不要 准则所描述的内容是相反的:描述的准则不是一个好注意。幸运的是,我们不会像其他语言有那么多这样的准则,因为我们没有太多的历史包袱。
DON’T guidelines are the converse: things that are almost never a good idea. Hopefully, we don’t have as many of these as other languages do because we have less historical baggage.
-
推荐 准则所描述的内容 应该 被遵守。但是在有些情况下,可能有更好的或者更合理的做法。请确保在你完全理解准则的情况下,再忽视这些准则。
PREFER guidelines are practices that you should follow. However, there may be circumstances where it makes sense to do otherwise. Just make sure you understand the full implications of ignoring the guideline when you do.
-
避免 该单词描述的准则与 “推荐” 描述的准则相反:显然,这些事不应该做,但不排除在极少数场合下有充分的理由可以做。
AVOID guidelines are the dual to “prefer”: stuff you shouldn’t do but where there may be good reasons to on rare occasions.
-
考虑 准则所描述的内容可以遵守也可以不遵守。取决于具体的情况、习惯做法以及自己的偏好。
CONSIDER guidelines are practices that you might or might not want to follow, depending on circumstances, precedents, and your own preference.
某些准则描述了规则 不 适用的 例外情况。当这些例外列出时,也有可能不是详尽的—你可能还需要对其它的情况作出判断。
Some guidelines describe an exception where the rule does not apply. When listed, the exceptions may not be exhaustive—you might still need to use your judgement on other cases.
这听起来好像有点小题大做。其实并没有那么糟糕。大部分的准则都是常识,也符合我们的认知。最终要达到的目标是写出优雅,可读,可维护的代码。
This sounds like the police are going to beat down your door if you don’t have your laces tied correctly. Things aren’t that bad. Most of the guidelines here are common sense and we’re all reasonable people. The goal, as always, is nice, readable and maintainable code.
术语表
Glossary
为了使指南保持简洁,我们使用一些简写术语来指代不同的 Dart 结构。
To keep the guidelines brief, we use a few shorthand terms to refer to different Dart constructs.
-
库成员 表示一个顶层字段、Getter 或 Setter 方法、或函数。基本而言,任何顶层的东西都不会是一种类型。
A library member is a top-level field, getter, setter, or function. Basically, anything at the top level that isn’t a type.
-
类成员 表示类内部声明的构造函数、字段、Getter 或 Setter 方法、函数或操作符。类成员可以是实例或静态的,也可以是抽象或具体的。
A class member is a constructor, field, getter, setter, function, or operator declared inside a class. Class members can be instance or static, abstract or concrete.
-
成员 可以表示是一个库成员或者类成员。
A member is either a library member or a class member.
-
变量 通常指的是顶层变量、参数和局部变量。它不包括静态或实例字段。
A variable, when used generally, refers to top-level variables, parameters, and local variables. It doesn’t include static or instance fields.
-
类型 表示所有命名类型的声明:一个类、typedef 或枚举。
A type is any named type declaration: a class, typedef, or enum.
-
属性 表示顶层变量、Getter 和 Setter 方法(位于类中或顶层,可以是实例或静态的)或字段(实例或静态的)。基本上是任何类似字段的命名结构都可以称为属性。
A property is a top-level variable, getter (inside a class or at the top level, instance or static), setter (same), or field (instance or static). Roughly any “field-like” named construct.
所有准则摘要
Summary of all rules
Style
Identifiers
- DO name types using
UpperCamelCase
. - DO name extensions using
UpperCamelCase
. - DO name libraries, packages, directories, and source files using
lowercase_with_underscores
. - DO name import prefixes using
lowercase_with_underscores
. - DO name other identifiers using
lowerCamelCase
. - PREFER using
lowerCamelCase
for constant names. - DO capitalize acronyms and abbreviations longer than two letters like words.
- PREFER using
_
,__
, etc. for unused callback parameters. - DON’T use a leading underscore for identifiers that aren’t private.
- DON’T use prefix letters.
Ordering
- DO place “dart:” imports before other imports.
- DO place “package:” imports before relative imports.
- DO specify exports in a separate section after all imports.
- DO sort sections alphabetically.
Formatting
Documentation
Comments
Doc comments
- DO use
///
doc comments to document members and types. - PREFER writing doc comments for public APIs.
- CONSIDER writing a library-level doc comment.
- CONSIDER writing doc comments for private APIs.
- DO start doc comments with a single-sentence summary.
- DO separate the first sentence of a doc comment into its own paragraph.
- AVOID redundancy with the surrounding context.
- PREFER starting function or method comments with third-person verbs.
- PREFER starting variable, getter, or setter comments with noun phrases.
- PREFER starting library or type comments with noun phrases.
- CONSIDER including code samples in doc comments.
- DO use square brackets in doc comments to refer to in-scope identifiers.
- DO use prose to explain parameters, return values, and exceptions.
- DO put doc comments before metadata annotations.
Markdown
- AVOID using markdown excessively.
- AVOID using HTML for formatting.
- PREFER backtick fences for code blocks.
Writing
Usage
Libraries
- DO use strings in
part of
directives. - DON’T import libraries that are inside the
src
directory of another package. - DON’T allow an import path to reach into or out of
lib
. - PREFER relative import paths.
Null
- DON’T explicitly initialize variables to
null
. - DON’T use an explicit default value of
null
. - PREFER using
??
to convertnull
to a boolean value. - AVOID
late
variables if you need to check whether they are initialized. - CONSIDER copying a nullable field to a local variable to enable type promotion.
Strings
- DO use adjacent strings to concatenate string literals.
- PREFER using interpolation to compose strings and values.
- AVOID using curly braces in interpolation when not needed.
Collections
- DO use collection literals when possible.
- DON’T use
.length
to see if a collection is empty. - CONSIDER using higher-order methods to transform a sequence.
- AVOID using
Iterable.forEach()
with a function literal. - DON’T use
List.from()
unless you intend to change the type of the result. - DO use
whereType()
to filter a collection by type. - DON’T use
cast()
when a nearby operation will do. - AVOID using
cast()
.
Functions
- DO use a function declaration to bind a function to a name.
- DON’T create a lambda when a tear-off will do.
- DO use
=
to separate a named parameter from its default value.
Variables
- DO follow a consistent rule for
var
andfinal
on local variables. - AVOID storing what you can calculate.
Members
- DON’T wrap a field in a getter and setter unnecessarily.
- PREFER using a
final
field to make a read-only property. - CONSIDER using
=>
for simple members. - DON’T use
this.
except to redirect to a named constructor or to avoid shadowing. - DO initialize fields at their declaration when possible.
Constructors
- DO use initializing formals when possible.
- DON’T use
late
when a constructor initializer list will do. - DO use
;
instead of{}
for empty constructor bodies. - DON’T use
new
. - DON’T use
const
redundantly.
Error handling
- AVOID catches without
on
clauses. - DON’T discard errors from catches without
on
clauses. - DO throw objects that implement
Error
only for programmatic errors. - DON’T explicitly catch
Error
or types that implement it. - DO use
rethrow
to rethrow a caught exception.
Asynchrony
Design
Names
- DO use terms consistently.
- AVOID abbreviations.
- PREFER putting the most descriptive noun last.
- CONSIDER making the code read like a sentence.
- PREFER a noun phrase for a non-boolean property or variable.
- PREFER a non-imperative verb phrase for a boolean property or variable.
- CONSIDER omitting the verb for a named boolean parameter.
- PREFER the “positive” name for a boolean property or variable.
- PREFER an imperative verb phrase for a function or method whose main purpose is a side effect.
- PREFER a noun phrase or non-imperative verb phrase for a function or method if returning a value is its primary purpose.
- CONSIDER an imperative verb phrase for a function or method if you want to draw attention to the work it performs.
- AVOID starting a method name with
get
. - PREFER naming a method
to___()
if it copies the object’s state to a new object. - PREFER naming a method
as___()
if it returns a different representation backed by the original object. - AVOID describing the parameters in the function’s or method’s name.
- DO follow existing mnemonic conventions when naming type parameters.
Libraries
Classes and mixins
- AVOID defining a one-member abstract class when a simple function will do.
- AVOID defining a class that contains only static members.
- AVOID extending a class that isn’t intended to be subclassed.
- DO document if your class supports being extended.
- AVOID implementing a class that isn’t intended to be an interface.
- DO document if your class supports being used as an interface.
- DO use
mixin
to define a mixin type. - AVOID mixing in a type that isn’t intended to be a mixin.
Constructors
Members
- PREFER making fields and top-level variables
final
. - DO use getters for operations that conceptually access properties.
- DO use setters for operations that conceptually change properties.
- DON’T define a setter without a corresponding getter.
- AVOID using runtime type tests to fake overloading.
- AVOID public
late final
fields without initializers. - AVOID returning nullable
Future
,Stream
, and collection types. - AVOID returning
this
from methods just to enable a fluent interface.
Types
- DO type annotate variables without initializers.
- DO type annotate fields and top-level variables if the type isn’t obvious.
- DON’T type annotate initialized local variables.
- DO annotate return types on function declarations.
- DO annotate parameter types on function declarations.
- DON’T annotate inferred parameter types on function expressions.
- DON’T type annotate initializing formals.
- DO write type arguments on generic invocations that aren’t inferred.
- DON’T write type arguments on generic invocations that are inferred.
- AVOID writing incomplete generic types.
- DO annotate with
dynamic
instead of letting inference fail. - PREFER signatures in function type annotations.
- DON’T specify a return type for a setter.
- DON’T use the legacy typedef syntax.
- PREFER inline function types over typedefs.
- PREFER using function type syntax for parameters.
- AVOID using
dynamic
unless you want to disable static checking. - DO use
Future<void>
as the return type of asynchronous members that do not produce values. - AVOID using
FutureOr<T>
as a return type.
Parameters
- AVOID positional boolean parameters.
- AVOID optional positional parameters if the user may want to omit earlier parameters.
- AVOID mandatory parameters that accept a special “no argument” value.
- DO use inclusive start and exclusive end parameters to accept a range.
Equality