目录

“有效的 Dart:风格”

编写良好的代码的一个出乎意料的重要部分是良好的风格。一致的命名、排序和格式化有助于使相同的代码看起来相同。它利用了我们大多数人眼部系统中强大的模式匹配硬件。如果我们在整个 Dart 生态系统中使用一致的风格,那么我们大家就更容易互相学习和贡献代码。

标识符

#

Dart 中的标识符有三种形式。

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

  • lowerCamelCase 命名法将每个单词的首字母大写, 除了 第一个单词,第一个单词始终小写,即使它是首字母缩略词。

  • lowercase_with_underscores 命名法只使用小写字母,即使是首字母缩略词,并用 _ 分隔单词。

使用 UpperCamelCase 为类型命名

#

Linter 规则:

类、枚举类型、typedef 和类型参数应将每个单词的首字母大写(包括第一个单词),并且不使用分隔符。

gooddart
class SliderMenu { ... }

class HttpRequest { ... }

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

这甚至包括旨在用于元数据注释的类。

gooddart
class Foo {
  const Foo([Object? arg]);
}

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

@Foo()
class B { ... }

如果注释类的构造函数不接受任何参数,您可能需要为其创建一个单独的 lowerCamelCase 常量。

gooddart
const foo = Foo();

@foo
class C { ... }

使用 UpperCamelCase 为扩展命名

#

Linter 规则:

与类型一样, 扩展 应将每个单词的首字母大写(包括第一个单词),并且不使用分隔符。

gooddart
extension MyFancyList<T> on List<T> { ... }

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

使用 lowercase_with_underscores 为包、目录和源文件命名

#

Linter 规则:

某些文件系统不区分大小写,因此许多项目要求文件名全部小写。使用分隔字符允许名称仍然以该形式可读。使用下划线作为分隔符可确保名称仍然是有效的 Dart 标识符,如果该语言以后支持符号导入,这可能会有所帮助。

good
my_package
└─ lib
   └─ file_system.dart
   └─ slider_menu.dart
bad
mypackage
└─ lib
   └─ file-system.dart
   └─ SliderMenu.dart

使用 lowercase_with_underscores 为导入前缀命名

#

Linter 规则:

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

使用 lowerCamelCase 为其他标识符命名

#

Linter 规则:

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

gooddart
var count = 3;

HttpRequest httpRequest;

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

优先使用 lowerCamelCase 为常量命名

#

Linter 规则:

在新代码中,对常量变量使用 lowerCamelCase ,包括枚举值。

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

class Dice {
  static final numberGenerator = Random();
}
baddart
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 。为避免这种情况,请像普通单词一样大写大多数首字母缩略词和缩写。如果指的是前者,则此标识符将为 HttpsFtp ,如果指的是后者,则为 HttpSftp

两个字母的缩写和首字母缩略词是例外。如果两个字母在英语中都是大写的,那么在标识符中使用时,它们都应该保持大写。否则,像单词一样大写它。

gooddart
// 超过两个字母,所以总是像一个单词:
Http // "hypertext transfer protocol"
Nasa // "national aeronautics and space administration"
Uri // "uniform resource identifier"
Esq // "esquire"
Ave // "avenue"

// 两个字母,在英语中大写,所以在标识符中大写:
ID // "identifier"
TV // "television"
UI // "user interface"

// 两个字母,在英语中没有大写,所以在标识符中像一个单词:
Mr // "mister"
St // "street"
Rd // "road"
baddart
HTTP // "hypertext transfer protocol"
NASA // "national aeronautics and space administration"
URI // "uniform resource identifier"
esq // "esquire"
Ave // "avenue"

Id // "identifier"
Tv // "television"
Ui // "user interface"

MR // "mister"
ST // "street"
RD // "road"

当任何形式的缩写出现在 lowerCamelCase 标识符的开头时,缩写应全部小写:

dart
var httpConnection = connect();
var tvSet = Television();
var mrRogers = 'hello, neighbor';

优先使用 ___ 等用于未使用的回调参数

#

有时回调函数的类型签名需要一个参数,但回调实现不 使用 该参数。在这种情况下,习惯上将未使用的参数命名为 _ 。如果函数有多个未使用的参数,请使用额外的下划线来避免名称冲突: _____ 等。

gooddart
futureOfVoid.then((_) {
  print('Operation complete.');
});

此指南仅适用于既是 匿名 又是 局部 的函数。这些函数通常立即在一个上下文中使用,在这个上下文中,清楚地说明了未使用的参数代表什么。相反,顶级函数和方法声明没有这样的上下文,因此必须命名它们的参数,以便清楚地说明每个参数的用途,即使它没有被使用。

不要对不是私有的标识符使用前导下划线

#

Dart 使用标识符中的前导下划线将成员和顶级声明标记为私有。这会训练用户将前导下划线与其中一种声明相关联。他们看到 "_" 并认为“私有”。

局部变量、参数、局部函数或库前缀没有“私有”的概念。当其中一个的名称以下划线开头时,它会向读者发送一个令人困惑的信号。为避免这种情况,请不要在这些名称中使用前导下划线。

不要使用前缀字母

#

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

gooddart
defaultTimeout
baddart
kDefaultTimeout

不要显式命名库

#

在技术上可以将名称附加到 library 指令,但这是一种遗留功能,不鼓励使用。

Dart 基于库的路径和文件名生成每个库的唯一标记。命名库会覆盖此生成的 URI。没有 URI,工具就更难以找到相关的库主文件。

baddart
library my_library;
gooddart
/// A really great test library.
@TestOn('browser')
library;

排序

#

为了保持文件序言的整洁,我们规定了指令应出现的顺序。每个“部分”都应以空行分隔。

单个棉绒规则处理所有排序指南: directives_ordering.

dart: 导入放在其他导入之前

#

Linter 规则:

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

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

package: 导入放在相对导入之前

#

Linter 规则:

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

import 'util.dart';

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

#

Linter 规则:

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

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

按字母顺序排列各节

#

Linter 规则:

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

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

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

格式化

#

像许多语言一样,Dart 忽略空格。但是, 人类 不会。拥有一个一致的空格样式有助于确保人类读者以与编译器相同的方式查看代码。

使用 dart format 格式化你的代码

#

格式化是一项繁琐的工作,在重构期间尤其耗时。幸运的是,您不必担心这个问题。我们提供了一个名为 dart format 的复杂的自动代码格式化程序来为您完成这项工作。Dart 的官方空格处理规则是 dart format 生成的任何内容格式化程序常见问题解答 可以提供对其强制执行的样式选择的更多见解。

其余的格式化指南适用于 dart format 无法为您修复的少数几件事。

考虑更改你的代码以使其更易于格式化

#

格式化程序会尽其所能处理您提交的任何代码,但它无法创造奇迹。如果您的代码具有特别长的标识符、深度嵌套的表达式、不同类型的运算符的混合等,则格式化的输出可能仍然难以阅读。

发生这种情况时,请重新组织或简化您的代码。考虑缩短局部变量名或将表达式提升到新的局部变量中。换句话说,进行与您手动格式化代码并尝试使其更易于阅读时所做的相同的修改。将 dart format 视为一种合作关系,您可以在其中一起工作,有时是迭代地,以生成漂亮的代码。

避免超过 80 个字符的行

#

Linter 规则:

可读性研究表明,长文本行更难阅读,因为当您的眼睛移动到下一行的开头时,它必须移动得更远。这就是报纸和杂志使用多列文本的原因。

如果您真的发现自己想要超过 80 个字符的行,我们的经验是您的代码可能过于冗长,可以更简洁一些。主要违规者通常是 VeryLongCamelCaseClassNames 。问问自己:“该类型名称中的每个单词是否都告诉我一些关键信息或防止名称冲突?”如果不是,请考虑省略它。

请注意, dart format 为您完成了 99% 的工作,但剩下的 1% 是您。它不会拆分长字符串文字以适应 80 列,因此您必须手动执行此操作。

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

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

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

#

Linter 规则:

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

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

例外: 当您有一个没有 else 子句的 if 语句并且整个 if 语句都适合一行时,如果您愿意,可以省略大括号:

gooddart
if (arg == null) return defaultValue;

但是,如果主体换行到下一行,请使用大括号:

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