高效Dart: 代码风格
好代码尤为重要的部分是良好的风格。连贯的命名、排序和格式化有助让相同的代码看起来是一样的。这利用了我们视觉系统中这一强大的样式匹配硬件。如果我们在整个Dart生态系统中使用连贯的编码风格,那么每个人学习他人代码或是对他人代码进行贡献都更为简单。
标识符
中有三种类型的标识符。
UpperCamelCase
命名中包含第一个单词所有首字母大写。lowerCamelCase
命名中单词首字母大写,但第一个字母小写,即使是缩略词也小写。lowercase_with_underscores
命名中仅使用小写字母,缩略词也不例外,单词中间通过_
分隔。
UpperCamelCase
来命名样式。
要使用Linter规则: 驼峰类型
类、枚举、typedef和类型参数应每个单词的首字母大写(包含第一个单词),无分隔符。
1 2 3 4 5 |
class SliderMenu { ... } class HttpRequest { ... } typedef Predicate<T> = bool Function(T value); |
这也包含用于元数据标的类。
1 2 3 4 5 6 7 8 9 |
class Foo { const Foo([arg]); } @Foo(anArg) class A { ... } @Foo() class B { ... } |
如果标注类的构造函数不接收参数,你可能会希望为其创建一个单独的 lowerCamelCase
常量。
1 2 3 4 |
const foo = Foo(); @foo class C { ... } |
lowercase_with_underscores
来命名库、包、目录和源文件。
要使用
一些文件系统不区分大小写,因此很多项目要求文件名全部小写。使用分隔的字符让名称以该种形式更为易读。使用下划线作为分隔符来确保名称仍为有效的Dart标识符,如果语言后面再支持符号导入的话也会很有帮助。
1 2 3 4 |
library peg_parser.source_scanner; import 'file_system.dart'; import 'slider_menu.dart'; |
lowercase_with_underscores
来命名导入前缀
要使用Linter规则: 库前缀
1 2 3 4 5 |
正确 import 'dart:math' as math; import 'package:angular_components/angular_components' as angular_components; import 'package:js/js.dart' as js; |
1 2 3 4 5 |
错误 import 'dart:math' as Math; import 'package:angular_components/angular_components' as angularComponents; import 'package:js/js.dart' as JS; |
lowerCamelCase
要对其它标识符使用Linter规则: 非常量标识符名称
类成员、顶级定义、变量、参数和命名参数除首个单词应每个单词的首字母大写,且不使用分隔符。
1 2 3 4 5 6 7 |
var item; HttpRequest httpRequest; void align(bool clearItems) { // ... } |
lowerCamelCase
建议对常量名称使用Linter规则: 常量标识符名称
在新的代码中,对常量变量,包括枚举值使用lowerCamelCase
。
1 2 3 4 5 6 7 8 |
正确 const pi = 3.14; const defaultTimeout = 1000; final urlScheme = RegExp('^([a-z]+):'); class Dice { static final numberGenerator = Random(); } |
1 2 3 4 5 6 7 8 |
错误 const PI = 3.14; const DefaultTimeout = 1000; final URL_SCHEME = RegExp('^([a-z]+):'); class Dice { static final NUMBER_GENERATOR = Random(); } |
可以对现有的代码使用 SCREAMING_CAPS
来保持连贯性,如下面的用例:
- 对已使用
SCREAMING_CAPS
的文件或库添加代码 - 在生成与Java对等的Dart代码时 – 例如,在由 protobuf生成的枚举类型中。
要对两个字母以上的缩略词和简写单词进行首字母大写
大写字母的缩略词很难阅读,多个相连的缩略词会导致含糊的名称。例如,一个惟 HTTPSFTP
开头的名称是没有办法知道它是指 HTTPS FTP 还是HTTP SFTP的。
为避免这点,缩略词和简写可以像普通单词一样首字母大写,除两个字母的缩略词外。(两个字母的缩写如ID和Mr.还是像单词里那样大写。)
1 2 3 4 5 6 7 |
正确 HttpConnectionInfo uiHandler IOStream HttpRequest Id DB |
1 2 3 4 5 6 7 |
错误 HTTPConnection UiHandler IoStream HTTPRequest ID Db |
不要在非私有内容的标识符前使用下划线
Dart在标识符中使用前置下划线来标记成员及顶级声明为私有。这训练用户将前置下划线与这类声明进行关联。他们看到“_”时就会想到“私有”。
局部变量、参数或库前缀没有“私有”的概念。这些名称在以下划线开头时,会让读代码的人产生混淆。为避免这点,不要在这些名称中使用前置下划线。
Exception: 一个未使用的参数可命名为 _
, __
, ___
等等。 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.
不要使用前缀字母
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.
1 2 |
正确 defaultTimeout |
1 2 |
错误 kDefaultTimeout |
排序
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:导入放在其它导入之前
Linter rule: directives_ordering
1 2 3 4 5 |
import 'dart:async'; import 'dart:html'; import 'package:bar/bar.dart'; import 'package:foo/foo.dart'; |
要在相对导入前放置package:
Linter rule: directives_ordering
1 2 3 4 |
<span class="kwd">import</span> <span class="str">'package:bar/bar.dart'</span><span class="pun">;</span> <span class="kwd">import</span> <span class="str">'package:foo/foo.dart'</span><span class="pun">;</span> <span class="kwd">import</span> <span class="str">'util.dart'</span><span class="pun">;</span> |
建议将外部package:导入放在其它导入之前
Linter rule: directives_ordering
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.
1 2 3 4 |
import 'package:bar/bar.dart'; import 'package:foo/foo.dart'; import 'util.dart'; |
要在所所有导入之后的单独区域指定导出
Linter rule: directives_ordering
1 2 3 4 5 |
正确 import 'src/error.dart'; import 'src/foo_bar.dart'; export 'src/error.dart'; |
1 2 3 4 |
错误 import 'src/error.dart'; export 'src/error.dart'; import 'src/foo_bar.dart'; |
要按照字母对分区排序
Linter rule: directives_ordering
1 2 3 4 5 6 |
正确 import 'package:bar/bar.dart'; import 'package:foo/foo.dart'; import 'foo.dart'; import 'foo/foo.dart'; |
1 2 3 4 5 6 |
错误 import 'package:foo/foo.dart'; import 'package:bar/bar.dart'; import 'foo/foo.dart'; import 'foo.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
对代码格式化
要使用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.
The remaining formatting guidelines are for the few things dartfmt cannot fix for you.
考虑修改代码让其对格式化器更友好
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.
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个字符
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.
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.
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.
要对所有的流程控制语句使用花括号
Linter rule: curly_braces_in_flow_control_structures
Doing so avoids the dangling else problem.
1 2 3 4 5 |
if (isWeekDay) { print('Bike to work!'); } else { print('Go dancing or read a book!'); } |
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:
1 |
if (arg == null) return defaultValue; |
If the body wraps to the next line, though, use braces:
1 2 3 4 |
正确 if (overflowChars != other.overflowChars) { return overflowChars < other.overflowChars; } |
1 2 3 |
错误 if (overflowChars != other.overflowChars) return overflowChars < other.overflowChars; |