目录

Contents

高效 Dart 语言指南:代码风格

目录

Contents

好的代码有一个非常重要的特点就是拥有好的风格。一致的命名、一致的顺序、以及一致的格式让代码看起来是一样的。这非常有利于发挥我们视力系统强大的模式匹配能力。如果我们整个 Dart 生态系统中都使用统一的风格,那么这将让我们彼此之间更容易的互相学习和互相贡献。它使我们所有人都可以更容易地学习并为彼此的代码做出贡献。

A surprisingly important part of good code is good style. Consistent naming, ordering, and formatting helps code that is the same look the same. It takes advantage of the powerful pattern-matching hardware most of us have in our ocular systems. If we use a consistent style across the entire Dart ecosystem, it makes it easier for all of us to learn from and contribute to each others’ code.

标识符

Identifiers

在 Dart 中标识符有三种类型。

Identifiers come in three flavors in Dart.

  • UpperCamelCase 每个单词的首字母都大写,包含第一个单词。

    UpperCamelCase names capitalize the first letter of each word, including the first.

  • lowerCamelCase 除了第一个字母始终是小写(即使是缩略词),每个单词的首字母都大写。

    lowerCamelCase names capitalize the first letter of each word, except the first which is always lowercase, even if it’s an acronym.

  • lowercase_with_underscores 只是用小写字母单词,即使是缩略词,并且单词之间使用 _ 连接。

    lowercase_with_underscores use only lowercase letters, even for acronyms, and separate words with _.

使用 UpperCamelCase 风格命名类型。

DO name types using UpperCamelCase.

Linter rule: camel_case_types

Classes(类名)、 enums(枚举类型)、 typedefs(类型定义)、以及 type parameters(类型参数)应该把每个单词的首字母都大写(包含第一个单词),不使用分隔符。

Classes, enums, typedefs, and type parameters should capitalize the first letter of each word (including the first word), and use no separators.

class SliderMenu { ... }

class HttpRequest { ... }

typedef Predicate<T> = bool Function(T value);

这里包括使用元数据注解的类。

This even includes classes intended to be used in metadata annotations.

class Foo {
  const Foo([arg]);
}

@Foo(anArg)
class A { ... }

@Foo()
class B { ... }

如果注解类的构造函数是无参函数,则可以使用一个 lowerCamelCase 风格的常量来初始化这个注解。

If the annotation class’s constructor takes no parameters, you might want to create a separate lowerCamelCase constant for it.

const foo = Foo();

@foo
class C { ... }

文件夹源文件中使用 lowercase_with_underscores 方式命名。

DO name libraries, packages, directories, and source files using lowercase_with_underscores.

Linter rules: library_names, file_names

一些文件系统不区分大小写,所以很多项目要求文件名必须是小写字母。使用分隔符这种形式可以保证命名的可读性。使用下划线作为分隔符可确保名称仍然是有效的Dart标识符,如果语言后续支持符号导入,这将会起到非常大的帮助。

Some file systems are not case-sensitive, so many projects require filenames to be all lowercase. Using a separating character allows names to still be readable in that form. Using underscores as the separator ensures that the name is still a valid Dart identifier, which may be helpful if the language later supports symbolic imports.

library peg_parser.source_scanner;

import 'file_system.dart';
import 'slider_menu.dart';
library pegparser.SourceScanner;

import 'file-system.dart';
import 'SliderMenu.dart';

lowercase_with_underscores 风格命名库和源文件名。

DO name import prefixes using lowercase_with_underscores.

Linter rule: library_prefixes

import 'dart:math' as math;
import 'package:angular_components/angular_components'
    as angular_components;
import 'package:js/js.dart' as js;
import 'dart:math' as Math;
import 'package:angular_components/angular_components'
    as angularComponents;
import 'package:js/js.dart' as JS;

使用 lowerCamelCase 风格来命名其他的标识符。

DO name other identifiers using lowerCamelCase.

Linter rule: non_constant_identifier_names

类成员、顶级定义、变量、参数以及命名参数等 除了第一个单词,每个单词首字母都应大写,并且不使用分隔符。

Class members, top-level definitions, variables, parameters, and named parameters should capitalize the first letter of each word except the first word, and use no separators.

var item;

HttpRequest httpRequest;

void align(bool clearItems) {
  // ...
}

推荐 使用 lowerCamelCase 来命名常量。

PREFER using lowerCamelCase for constant names.

Linter rule: constant_identifier_names

在新的代码中,使用 lowerCamelCase 来命名常量,包括枚举的值。

In new code, use lowerCamelCase for constant variables, including enum values.

const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');

class Dice {
  static final numberGenerator = Random();
}
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');

class Dice {
  static final NUMBER_GENERATOR = Random();
}

您可以使用 SCREAMING_CAPS 与现有代码保持一致,比如:

You may use SCREAMING_CAPS for consistency with existing code, as in the following cases:

  • 将代码添加到已使用 SCREAMING_CAPS 的文件或库时。

    When adding code to a file or library that already uses SCREAMING_CAPS.

  • 生成与 Java 代码并行的 Dart 代码时。例如,来自 protobufs 的枚举类型

    When generating Dart code that’s parallel to Java code — for example, in enumerated types generated from protobufs.

把超过两个字母的首字母大写缩略词和缩写词当做一般单词来对待。

DO capitalize acronyms and abbreviations longer than two letters like words.

首字母大写缩略词比较难阅读,特别是多个缩略词连载一起的时候会引起歧义。例如,一个以 HTTPSFTP 开头的名字,没有办法判断它是指 HTTPS FTP 还是 HTTP SFTP。

Capitalized acronyms can be hard to read, and multiple adjacent acronyms can lead to ambiguous names. For example, given a name that starts with HTTPSFTP, there’s no way to tell if it’s referring to HTTPS FTP or HTTP SFTP.

为了避免上面的情况,缩略词和缩写词要像普通单词一样首字母大写,两个字母的单词除外。(像 ID 和 Mr. 这样的双字母缩写词仍然像一般单词一样首字母大写。)

To avoid this, acronyms and abbreviations are capitalized like regular words, except for two-letter acronyms. (Two-letter abbreviations like ID and Mr. are still capitalized like words.)

HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DB
HTTPConnection
UiHandler
IoStream
HTTPRequest
ID
Db

DON’T use a leading underscore for identifiers that aren’t private.

Dart uses a leading underscore in an identifier to mark members and top-level declarations as private. This trains users to associate a leading underscore with one of those kinds of declarations. They see “_” and think “private”.

There is no concept of “private” for local variables, parameters, or library prefixes. When one of those has a name that starts with an underscore, it sends a confusing signal to the reader. To avoid that, don’t use leading underscores in those names.

Exception: An unused parameter can be named _, __, ___, etc. This happens in things like callbacks where you are passed a value but you don’t need to use it. Giving it a name that consists solely of underscores is the idiomatic way to indicate the value isn’t used.

不要使用前缀字母

DON’T use prefix letters.

在编译器无法帮助你了解自己代码的时, 匈牙利命名法 和其他方案出现在了 BCPL ,但是因为 Dart 可以提示你声明的类型,范围,可变性和其他属性,所以没有理由在标识符名称中对这些属性进行编码。

Hungarian notation and other schemes arose in the time of BCPL, when the compiler didn’t do much to help you understand your code. Because Dart can tell you the type, scope, mutability, and other properties of your declarations, there’s no reason to encode those properties in identifier names.

defaultTimeout
kDefaultTimeout

顺序

Ordering

为了使文件前面部分保持整洁,我们规定了关键字出现顺序的规则。每个“部分”应该使用空行分割。

To keep the preamble of your file tidy, we have a prescribed order that directives should appear in. Each “section” should be separated by a blank line.

A single linter rule handles all the ordering guidelines: directives_ordering.

把 “dart:” 导入语句放到其他导入语句之前。

DO place “dart:” imports before other imports.

Linter rule: directives_ordering

import 'dart:async';
import 'dart:html';

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

把 “package:” 导入语句放到项目相关导入语句之前。

DO place “package:” imports before relative imports.

Linter rule: directives_ordering

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'util.dart';

推荐 把外部扩展 “package:” 导入语句放到其他语句之前。

PREFER placing external “package:” imports before other imports.

Linter rule: directives_ordering

如果你使用了多个 “package:” 导入语句来导入自己的包以及其他外部扩展包,推荐将自己的包分开放到一个额外的部分。

If you have a number of “package:” imports for your own package along with other external packages, place yours in a separate section after the external ones.

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'package:my_package/util.dart';

把导出(export)语句作为一个单独的部分放到所有导入语句之后。

DO specify exports in a separate section after all imports.

Linter rule: directives_ordering

import 'src/error.dart';
import 'src/foo_bar.dart';

export 'src/error.dart';
import 'src/error.dart';
export 'src/error.dart';
import 'src/foo_bar.dart';

按照字母顺序来排序每个部分中的语句。

DO sort sections alphabetically.

Linter rule: directives_ordering

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'foo.dart';
import 'foo/foo.dart';
import 'package:foo/foo.dart';
import 'package:bar/bar.dart';

import 'foo/foo.dart';
import 'foo.dart';

格式化

Formatting

和其他大部分语言一样, Dart 忽略空格。但是,不会。具有一致的空格风格有助于帮助我们能够用编译器相同的方式理解代码。

Like many languages, Dart ignores whitespace. However, humans don’t. Having a consistent whitespace style helps ensure that human readers see code the same way the compiler does.

使用 dartfmt 格式化你的代码。

DO format your code using dartfmt.

格式化是一项繁琐的工作,尤其在重构过程中特别耗时。庆幸的是,你不必担心。我们提供了一个名为 dartfmt 的优秀的自动代码格式化程序,它可以为你完成格式化工作。我们有一些关于它适用的规则的 文档 , Dart 中任何官方的空格处理规则由 dartfmt 生成

Formatting is tedious work and is particularly time-consuming during refactoring. Fortunately, you don’t have to worry about it. We provide a sophisticated automated code formatter called dartfmt that does do it for you. We have some documentation on the rules it applies, but the official whitespace-handling rules for Dart are whatever dartfmt produces.

其余格式指南用于 dartfmt 无法修复的一些规则。

The remaining formatting guidelines are for the few things dartfmt cannot fix for you.

考虑 修改你的代码让格式更友好。

CONSIDER changing your code to make it more formatter-friendly.

无论你扔给格式化程序什么样代码,它都会尽力去处理,但是格式化程序不会创造奇迹。如果代码里有特别长的标识符,深层嵌套的表达式,混合的不同类型运算符等。格式化输出的代码可能任然很难阅读。

The formatter does the best it can with whatever code you throw at it, but it can’t work miracles. If your code has particularly long identifiers, deeply nested expressions, a mixture of different kinds of operators, etc. the formatted output may still be hard to read.

当有这样的情况发生时,那么就需要重新组织或简化你的代码。考虑缩短局部变量名或者将表达式抽取为一个新的局部变量。换句话说,你应该做一些手动格式化并增加代码的可读性的修改。在工作中应该把 dartfmt 看做一个合作伙伴,在代码的编写和迭代过程中互相协作输出优质的代码。

When that happens, reorganize or simplify your code. Consider shortening a local variable name or hoisting out an expression into a new local variable. In other words, make the same kinds of modifications that you’d make if you were formatting the code by hand and trying to make it more readable. Think of dartfmt as a partnership where you work together, sometimes iteratively, to produce beautiful code.

避免 单行超过 80 个字符。

AVOID lines longer than 80 characters.

Linter rule: lines_longer_than_80_chars

可读性研究表明,长行的文字不易阅读,长行文字移动到下一行的开头时,眼睛需要移动更长的距离。这也是为什么报纸和杂志会使用多列样式的文字排版。

Readability studies show that long lines of text are harder to read because your eye has to travel farther when moving to the beginning of the next line. This is why newspapers and magazines use multiple columns of text.

如果你真的发现你需要的文字长度超过了 80 个字符,根据我们的经验,你的代码很可能过于冗长,而且有方式可以让它更紧凑。最常见的的一种情况就是使用 VeryLongCamelCaseClassNames (非常长的类名字和变量名字)。当遇到这种情况时,请自问一下:“那个类型名称中的每个单词都会告诉我一些关键的内容或阻止名称冲突吗?”,如果不是,考虑删除它。

If you really find yourself wanting lines longer than 80 characters, our experience is that your code is likely too verbose and could be a little more compact. The main offender is usually VeryLongCamelCaseClassNames. Ask yourself, “Does each word in that type name tell me something critical or prevent a name collision?” If not, consider omitting it.

注意,dartfmt 能自动处理 99% 的情况,但是剩下的 1% 需要你自己处理。 dartfmt 不会把很长的字符串字面量分割为 80 个字符的列,所以这种情况你需要自己手工确保每行不超过 80 个字符。

Note that dartfmt does 99% of this for you, but the last 1% is you. It does not split long string literals to fit in 80 columns, so you have to do that manually.

例外: 当情况出现在注释或字符串是(通常在导入和导出语句中),即使文字超出行限制,也可能会保留在一行中。这样可以更轻松地搜索给定路径的源文件。

Exception: When a URI or file path occurs in a comment or string (usually in an import or export), it may remain whole even if it causes the line to go over 80 characters. This makes it easier to search source files for a path.

Exception: Multi-line strings can contain lines longer than 80 characters because newlines are significant inside the string and splitting the lines into shorter ones can alter the program.

对所有流控制结构使用花括号。

DO use curly braces for all flow control statements.

Linter rule: curly_braces_in_flow_control_structures

这样可以避免 dangling else(else悬挂)的问题。

Doing so avoids the dangling else problem.

if (isWeekDay) {
  print('Bike to work!');
} else {
  print('Go dancing or read a book!');
}

这里有一个例外:一个没有 elseif 语句,并且这个 if 语句以及它的执行体适合在一行中实现。在这种情况下,如果您愿意,可以不用括号:

Exception: When you have an if statement with no else clause and the whole if statement fits on one line, you can omit the braces if you prefer:

if (arg == null) return defaultValue;

但是,如果执行体包含下一行,请使用大括号:

If the body wraps to the next line, though, use braces:

if (overflowChars != other.overflowChars) {
  return overflowChars < other.overflowChars;
}
if (overflowChars != other.overflowChars)
  return overflowChars < other.overflowChars;