目錄

高效 Dart 語言指南:程式碼風格

好的程式碼有一個非常重要的特點就是擁有好的風格。一致的命名、一致的順序、以及一致的格式讓程式碼看起來是一樣的。這非常有利於發揮我們視力系統強大的模式匹配能力。如果我們整個 Dart 生態系統中都使用統一的風格,那麼這將讓我們彼此之間更容易的互相學習和互相貢獻。它使我們所有人都可以更容易地學習併為彼此的程式碼做出貢獻。

識別符號

在 Dart 中識別符號有三種類型。

  • UpperCamelCase 每個單詞的首字母都大寫,包含第一個單詞。

  • lowerCamelCase 除了第一個字母始終是小寫(即使是縮略詞),每個單詞的首字母都大寫。

  • lowercase_with_underscores 只使用小寫字母單詞,即使是縮略詞,並且單詞之間使用 _ 連線。

使用 UpperCamelCase 風格命名型別。

Linter rule: camel_case_types

Classes(類別名稱)、 enums(列舉型別)、 typedefs(型別定義)、以及 type parameters(型別引數)應該把每個單詞的首字母都大寫(包含第一個單詞),不使用分隔符。

class SliderMenu { ... }

class HttpRequest { ... }

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

這裡包括使用元資料註解的類別。

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

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

@Foo()
class B { ... }

如果註解類別的建構函式是無參函式,則可以使用一個 lowerCamelCase 風格的常量來初始化這個註解。

const foo = Foo();

@foo
class C { ... }

使用 UpperCamelCase 風格型別作為副檔名

Linter rule: camel_case_extensions

與型別命名一樣,擴充 的名稱也應大寫每個單詞的首字母(包括第一個單詞),並且不使用分隔符。

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

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

package資料夾原始檔 中使用 lowercase_with_underscores 方式命名。

Linter rules: file_names, package_names

一些檔案系統不區分大小寫,所以很多專案要求檔名必須是小寫字母。使用分隔符這種形式可以保證命名的可讀性。使用下劃線作為分隔符可確保名稱仍然是有效的Dart識別符號,如果語言後續支援符號匯入,這將會起到非常大的幫助。

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

如果你 選擇命名庫,本準則給定了 如何 為庫取名。如果需要,可以在檔案中 省略 庫指令。

</aside>

lowercase_with_underscores 風格命名庫和原始檔名。

Linter rule: library_prefixes

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

使用 lowerCamelCase 風格來命名其他的識別符號。

Linter rule: non_constant_identifier_names

類成員、最上層定義、變數、引數以及命名引數等 除了第一個單詞,每個單詞首字母都應大寫,並且不使用分隔符。

var count = 3;

HttpRequest httpRequest;

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

推薦 使用 lowerCamelCase 來命名常量。

Linter rule: constant_identifier_names

在新的程式碼中,使用 lowerCamelCase 來命名常量,包括列舉的值。

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 與現有程式碼保持一致,比如:

  • 將程式碼新增到已使用 SCREAMING_CAPS 的檔案或庫時。

  • 產生與 Java 程式碼並行的 Dart 程式碼時。例如,來自 protobufs 的列舉型別。

把超過兩個字母的首字母大寫縮略詞和縮寫詞當做一般單詞來對待。

首字母大寫縮略詞比較難閱讀,特別是多個縮略詞連載一起的時候會引起歧義。例如,一個以 HTTPSFTP 開頭的名字,沒有辦法判斷它是指 HTTPS FTP 還是 HTTP SFTP。

為了避免上面的情況,縮略詞和縮寫詞要像普通單詞一樣首字母大寫。

例外情況 兩個字母情況下,類似 IO (input/output) 這樣的 縮略詞 要全大寫。另外,兩個字母的 縮寫詞 比如 ID (identification) 與其他常規單詞一樣,首字母大寫即可: Id

class HttpConnection {}
class DBIOPort {}
class TVVcr {}
class MrRogers {}

var httpRequest = ...
var uiHandler = ...
var userId = ...
Id id;
class HTTPConnection {}
class DbIoPort {}
class TvVcr {}
class MRRogers {}

var hTTPRequest = ...
var uIHandler = ...
var userID = ...
ID iD;

PREFER using _, __, etc. for unused callback parameters

Sometimes the type signature of a callback function requires a parameter, but the callback implementation doesn’t use the parameter. In this case, it’s idiomatic to name the unused parameter _. If the function has multiple unused parameters, use additional underscores to avoid name collisions: __, ___, etc.

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

This guideline is only for functions that are both anonymous and local. These functions are usually used immediately in a context where it’s clear what the unused parameter represents. In contrast, top-level functions and method declarations don’t have that context, so their parameters must be named so that it’s clear what each parameter is for, even if it isn’t used.

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, local functions, 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.

不要使用字首字母

在編譯器無法幫助你瞭解自己程式碼的時, 匈牙利命名法 和其他方案出現在了 BCPL ,但是因為 Dart 可以提示你宣告的型別,範圍,可變性和其他屬性,所以沒有理由在識別符號名稱中對這些屬性進行編碼。

defaultTimeout
kDefaultTimeout

DON’T explicitly name libraries

Appending a name to the library directive is technically possible, but is a legacy feature and discouraged.

Dart generates a unique tag for each library based on its path and filename. Naming libraries overrides this generated URI. Without the URI, it can be harder for tools to find the main library file in question.

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

順序

為了使檔案前面部分保持整潔,我們規定了關鍵字出現順序的規則。每個“部分”應該使用空行分割。

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

把 “dart:” 匯入陳述式放到其他匯入陳述式之前。

Linter rule: directives_ordering

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

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

把 “package:” 匯入陳述式放到專案相關匯入陳述式之前。

Linter rule: directives_ordering

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

import 'util.dart';

把匯出 (export) 陳述式作為一個單獨的部分放到所有匯入陳述式之後。

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';

按照字母順序來排序每個部分中的陳述式。

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';

格式化

和其他大部分語言一樣, Dart 忽略空格。但是,不會。具有一致的空格風格有助於幫助我們能夠用編譯器相同的方式理解程式碼。

使用 dart format 格式化你的程式碼。

格式化是一項繁瑣的工作,尤其在重構過程中特別耗時。慶幸的是,你不必擔心。我們提供了一個名為 dart format 的優秀的自動程式碼格式化程式,它可以為你完成格式化工作。我們有一些關於它適用的規則的 文件 , Dart 中任何官方的空格處理規則由 dart format 產生

其餘格式指南用於 dart format 無法修復的一些規則。

考慮 修改你的程式碼讓格式更友好。

無論你扔給格式化程式什麼樣程式碼,它都會盡力去處理,但是格式化程式不會創造奇蹟。如果程式碼裡有特別長的識別符號,深層巢狀(Nesting)的表示式,混合的不同型別運算子等。格式化輸出的程式碼可能任然很難閱讀。

當有這樣的情況發生時,那麼就需要重新組織或簡化你的程式碼。考慮縮短區域變數名或者將表示式抽取為一個新的區域變數。換句話說,你應該做一些手動格式化並增加程式碼的可讀性的修改。在工作中應該把 dart format 看做一個合作伙伴,在程式碼的編寫和迭代過程中互相協作輸出優質的程式碼。

避免 單行超過 80 個字元。

Linter rule: lines_longer_than_80_chars

可讀性研究表明,長行的文字不易閱讀,長行文字移動到下一行的開頭時,眼睛需要移動更長的距離。這也是為什麼報紙和雜誌會使用多列樣式的文字排版。

如果你真的發現你需要的文字長度超過了 80 個字元,根據我們的經驗,你的程式碼很可能過於冗長,而且有方式可以讓它更緊湊。最常見的的一種情況就是使用 VeryLongCamelCaseClassNames (非常長的類別名稱字和變數名字)。當遇到這種情況時,請自問一下:“那個型別名稱中的每個單詞都會告訴我一些關鍵的內容或阻止名稱衝突嗎?”,如果不是,考慮刪除它。

注意,dart format 能自動處理 99% 的情況,但是剩下的 1% 需要你自己處理。 dart format 不會把很長的字串字面量分割為 80 個字元的列,所以這種情況你需要自己手工確保每行不超過 80 個字元。

例外: 當 URI 及檔案路徑出現在註釋或字串中時(通常在匯入和匯出陳述式中),即使文字超出行限制,也可能會保留在一行中。這樣可以更輕鬆地搜尋給定路徑的原始檔。

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

這樣可以避免 dangling else(else懸掛)的問題。

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

這裡有一個例外:一個沒有 elseif 陳述式,並且這個 if 陳述式以及它的執行體適合在一行中實現。在這種情況下,如果您願意,可以不用括號:

if (arg == null) return defaultValue;

但是,如果執行體包含下一行,請使用大括號:

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