Dart 风格指南

Dart 风格指南

好的代码令人惊讶的重要部分是好的样式。一致的命名,排序和格式有助于代码是相同的外观相同。如果我们在整个Dart生态系统中使用一致的样式,那么这将使我们所有人更容易学习彼此的代码并做出贡献。

风格类型

标识符在Dart中具有三种风格。

  • UpperCamelCase 大驼峰名称将每个单词的首字母大写,包括第一个。

  • lowerCamelCase 小驼峰名称为每个字的第一个字母,除了 第一它总是小写,即使它是一个缩写。

  • lowercase_with_underscores 小写字母+下划线。仅使用小写字母(即使是首字母缩写词),并使用下划线分隔单词。

类型使用大驼峰法

Classes, enums, typedefs, 和 Type parameters 使用大驼峰,并且不使用分隔符。

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 { ... }

子类使用大驼峰法

1
2
3
extension MyFancyList<T> on List<T> { ... }

extension SmartIterable<T> on Iterable<T> { ... }

Version note: Extensions are a Dart 2.7 language feature

libraries, packages, directories, and source files using 小写字母+下划线

1
2
3
4
5
/// good
library peg_parser.source_scanner;

import 'file_system.dart';
import 'slider_menu.dart';
1
2
3
4
5
/// bad
library pegparser.SourceScanner;

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

import 别名使用小写字母+下划线

1
2
3
4
5
/// good
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
/// bad
import 'dart:math' as Math;
import 'package:angular_components/angular_components'
as angularComponents;
import 'package:js/js.dart' as JS;

标识符使用小驼峰法

1
2
3
4
5
6
7
var item;

HttpRequest httpRequest;

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

常量标识符优先使用小驼峰

1
2
3
4
5
6
7
8
/// good
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
/// bad
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');

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

您可以使用 SCREAMING_CAPS 来保持与现有代码的一致性

  • 当往已经使用了 SCREAMING_CAPS 风格的代码中新增时,继续使用 SCREAMING_CAPS 风格。

  • 当生成与Java代码并行的Dart代码时——例如,在由protobufs生成的枚举类型中,继续使用 SCREAMING_CAPS 风格。

提示: 最开始版本使用的是 Java 的 SCREAMING_CAPS 风格,由于以下原因,改为小驼峰:

  • SCREAMING_CAPS在很多情况下看起来都很糟糕,特别是对于CSS颜色之类的东西来说,它的enum值就更糟糕了。
  • 常量经常被更改为最终的非const变量,这就需要频繁更改名称。
  • 在enum类型上自动定义的值属性是const和小写的。

首字母缩写词和缩写词的首字母都要大写

首字母缩写词很难读懂,而且多个相邻的首字母缩写词会导致名称模糊。例如,给定一个以HTTPSFTP开头的名称,就无法判断它是引用HTTPSFTP还是HTTPSFTP。

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

1
2
3
4
5
6
7
/// good
HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DB
1
2
3
4
5
6
7
/// bad
HTTPConnection
UiHandler
IoStream
HTTPRequest
ID
Db

不要为非私有的标识符使用前导下划线

默认下划线开头的都是私有成员。

不要使用前缀字母

匈牙利符号和其他方案出现在BCPL时代,那时编译器并没有帮助您理解代码。因为Dart可以告诉您声明的类型、范围、可变性和其他属性,所以没有理由将这些属性编码到标识符名称中。

1
2
/// good
defaultTimeout
1
2
/// bad
kDefaultTimeout

顺序

为了保持你的文件的前言整洁,我们有一个规定的顺序,指示的部分应该出现在该出现的位置上。每个“部分”之间应该用空行隔开。

使用 improt 时,优先将 dart 系统包放在第三方包之前

BAD:

1
2
3
4
5
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'dart:async'; // LINT
import 'dart:html'; // LINT

BAD:

1
2
3
4
5
import 'dart:html';  // OK
import 'package:bar/bar.dart';

import 'dart:async'; // LINT
import 'package:foo/foo.dart';

GOOD:

1
2
3
4
5
import 'dart:async';  // OK
import 'dart:html'; // OK

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

将“package:”导入放在相对路径导入之前

BAD:

1
2
3
4
5
import 'a.dart';
import 'b.dart';

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

BAD:

1
2
3
4
5
import 'package:bar/bar.dart';  // OK
import 'a.dart';

import 'package:foo/foo.dart'; // LINT
import 'b.dart';

GOOD:

1
2
3
4
5
import 'package:bar/bar.dart';  // OK
import 'package:foo/foo.dart'; // OK

import 'a.dart';
import 'b.dart';

优先将“第三方”“包”导入放在其他导入之前

BAD:

1
2
3
4
5
import 'package:myapp/io.dart';
import 'package:myapp/util.dart';

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

GOOD:

1
2
3
4
5
import 'package:bar/bar.dart';  // OK
import 'package:foo/foo.dart'; // OK

import 'package:myapp/io.dart';
import 'package:myapp/util.dart';

在所有导入之后,请在单独的部分中指定导出

BAD:

1
2
3
import 'src/error.dart';
export 'src/error.dart'; // LINT
import 'src/string_source.dart';

GOOD:

1
2
3
4
import 'src/error.dart';
import 'src/string_source.dart';

export 'src/error.dart'; // OK

按字母顺序对各部分进行排序

BAD:

1
2
3
4
5
import 'package:foo/bar.dart'; // OK
import 'package:bar/bar.dart'; // LINT

import 'a/b.dart'; // OK
import 'a.dart'; // LINT

GOOD:

1
2
3
4
5
import 'package:bar/bar.dart'; // OK
import 'package:foo/bar.dart'; // OK

import 'a.dart'; // OK
import 'a/b.dart'; // OK

格式化

与许多语言一样,Dart忽略空白。然而,人类没有。具有一致的空白样式有助于确保人类读者以与编译器相同的方式查看代码。

使用 dartfmt 工具格式化你的代码

格式化是一项繁琐的工作,在重构过程中尤其耗费时间。幸运的是,你不必为此担心。我们提供了一种复杂的自动代码格式化程序,称为dartfmt,它可以为您完成这项工作。我们有一些关于它所适用的规则的文档,但是dart的官方空格处理规则是由dartfmt生成的。

剩下的格式化指导方针是针对一些dartfmt无法为您解决的问题手动处理。

考虑更改你的代码,使其对格式更友好

格式化程序尽其所能地处理你扔给它的任何代码,但它不能创造奇迹。如果您的代码有特别长的标识符、深度嵌套的表达式、不同类型的操作符的混合等,格式化的输出可能仍然难以阅读。

当这种情况发生时,重新组织或简化您的代码。考虑缩短局部变量名或将表达式提升为新的局部变量。换句话说,进行与手工格式化代码并使其更具可读性相同的修改。可以将dartmt看作是一种伙伴关系,在这种关系中,您可以一起工作,有时是迭代地工作,以生成漂亮的代码。

避免行超过80个字符

易读性研究表明,长行文字很难阅读,因为当你移动到下一行的开头时,你的眼睛不得不走得更远。这就是报纸和杂志使用多栏文本的原因。

如果您真的希望行长度超过80个字符,我们的经验是,您的代码可能太过冗长,而且可能更紧凑一些。最主要的冒犯者通常是很长的名字。问问自己,“该类型名称中的每个单词都告诉了我一些重要的信息,还是防止了名称冲突?”如果不是,那就考虑忽略它。

注意,dartfmt为你做了99%的事情,但剩下的1%是你自己。它不会分割长字符串来容纳80个列,所以您必须手动进行分割。

例外:当URI或文件路径出现在注释或字符串中(通常在导入或导出中),即使它导致行超过80个字符,它也可能保持完整。这使得在源文件中搜索路径变得更加容易。

例外:多行字符串可以包含超过80个字符的行,因为换行在字符串中很重要,将行分割成更短的行可以改变程序。

对所有流控制语句使用大括号

这样做可以避免悬空else问题。

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

例外:当你有一个没有else子句的if语句,并且整个if语句适合在一行,你可以省略大括号,如果你喜欢:

if (arg == null) return defaultValue;

如果正文换行,则使用大括号:

1
2
3
4
/// good
if (overflowChars != other.overflowChars) {
return overflowChars < other.overflowChars;
}
1
2
3
/// bad
if (overflowChars != other.overflowChars)
return overflowChars < other.overflowChars;
============ END ============