深色模式
Dart 语法基础
变量
创建并初始化变量示例
以下是一个创建变量并对其进行初始化的示例:
dart
var name = 'Bob';变量存储的是引用。名为 name 的变量包含一个对值为 "Bob" 的 String 对象的引用。
name 变量的类型会被推断为 String,但你可以通过显式指定类型来改变它。如果一个对象不局限于单一类型,可以指定 Object 类型(必要时也可使用 dynamic 类型)。
dart
Object name = 'Bob';另一种选择是显式声明会被推断出的类型:
dart
String name = 'Bob';提示: 本页面遵循风格指南的建议,对于局部变量使用 var 而非类型注解。
空安全
Dart 语言强制采用健全的空安全特性。
空安全可避免因意外访问被设置为 null 的变量而导致的错误,这种错误被称为空引用错误。当你对一个求值为 null 的表达式访问其属性或调用其方法时,就会发生空引用错误。不过,当 null 支持该属性或方法(如 toString() 或 hashCode)时,这一规则会有例外。有了空安全特性,Dart 编译器会在编译时检测到这些潜在错误。
例如,假设你想获取一个 int 类型变量 i 的绝对值。如果 i 为 null,调用 i.abs() 会导致空引用错误。在其他语言中,这样做可能会导致运行时错误,但 Dart 编译器会禁止此类操作。因此,Dart 应用程序不会出现这类运行时错误。
空安全特性带来了三个关键变化:
- 类型可空性控制:当你为变量、参数或其他相关组件指定类型时,可以控制该类型是否允许为
null。要使类型可为空,需在类型声明末尾添加?。
dart
String? name // 可为空的类型。可以是 `null` 或字符串。
String name // 不可为空的类型。不能为 `null`,但可以是字符串。- 变量初始化要求:在使用变量之前,必须对其进行初始化。可为空的变量默认值为
null,因此它们会默认完成初始化。Dart 不会为不可为空的类型设置初始值,而是强制你设置初始值。Dart 不允许你使用未初始化的变量,这能防止你在接收者类型可能为null但null又不支持所使用的方法或属性时,访问其属性或调用其方法。 - 可空类型访问限制:你不能对具有可空类型的表达式访问其属性或调用其方法。不过,当属性或方法是
null支持的(如hashCode或toString())时,同样存在例外情况。
健全的空安全特性将潜在的运行时错误转变为编辑时分析错误。当一个不可为空的变量出现以下两种情况时,空安全机制会标记错误:
- 未使用非空值进行初始化。
- 被赋值为
null。
这种检查机制让你能够在部署应用程序之前修复这些错误。
默认值
未初始化的可空类型变量的初始值为 null。即使是数值类型的变量,初始值也为 null,因为在 Dart 中,和其他所有事物一样,数字也是对象。
dart
int? lineCount;
assert(lineCount == null);提示: 在生产代码中,assert() 调用会被忽略。但在开发过程中,如果 assert(condition) 中的 condition 为 false,则会抛出异常。详细信息请查看“断言”部分。
有了空安全特性,在使用不可为空的变量之前,必须对其进行初始化:
dart
int lineCount = 0;你不必在声明局部变量时就对其进行初始化,但在使用该变量之前,必须为其赋值。例如,以下代码是有效的,因为 Dart 可以检测到在将 lineCount 传递给 print() 时,它的值不为 null:
dart
int lineCount;
if (weLikeToCount) {
lineCount = countLines();
} else {
lineCount = 0;
}
print(lineCount);顶级变量和类变量是延迟初始化的,初始化代码会在变量首次被使用时运行。
延迟变量
late 修饰符有两个使用场景:
- 声明后初始化的不可为空变量:声明一个不可为空的变量,但在声明之后再对其进行初始化。
- 延迟初始化变量:延迟初始化一个变量。
通常情况下,Dart 的控制流分析可以检测到一个不可为空的变量在使用之前是否被赋值为非空值,但有时分析会失败。两种常见的情况是顶级变量和实例变量:Dart 通常无法确定它们是否被赋值,因此不会进行尝试。
如果你确定一个变量在使用之前已经被赋值,但 Dart 不这么认为,可以通过将该变量标记为 late 来修复错误:
dart
late String description;
void main() {
description = 'Feijoada!';
print(description);
}注意: 如果你未能初始化一个标记为 late 的变量,在使用该变量时会发生运行时错误。
当你将一个变量标记为 late 并在声明时对其进行初始化时,初始化器会在变量首次被使用时运行。这种延迟初始化在以下两种情况下很有用:
- 变量可能不会被使用,并且初始化该变量的成本较高。
- 你正在初始化一个实例变量,并且其初始化器需要访问
this。
在以下示例中,如果 temperature 变量从未被使用,那么昂贵的 readThermometer() 函数将永远不会被调用:
dart
// 这是程序中唯一一次调用 readThermometer()。
late String temperature = readThermometer(); // 延迟初始化。final 和 const
如果你不打算改变一个变量的值,可以使用 final 或 const,既可以替代 var,也可以与类型一起使用。final 变量只能被赋值一次;const 变量是编译时常量(const 变量隐式为 final)。
提示: 实例变量可以是 final 类型,但不能是 const 类型。
以下是创建并设置 final 变量的示例:
dart
final name = 'Bob'; // 不使用类型注解
final String nickname = 'Bobby';你不能改变 final 变量的值:
dart
name = 'Alice'; // 错误:final 变量只能被赋值一次。对于你希望是编译时常量的变量,可以使用 const。如果 const 变量是类级别的,需要将其标记为 static const。在声明变量时,将其值设置为编译时常量,如数或字符串字面量、const 变量,或者常量数字的算术运算结果:
dart
const bar = 1000000; // 压力单位(达因/平方厘米)
const double atm = 1.01325 * bar; // 标准大气压const 关键字不仅用于声明常量变量,还可以用于创建常量值,以及声明用于创建常量值的构造函数。任何变量都可以具有常量值。
dart
var foo = const [];
final bar = const [];
const baz = []; // 等同于 `const []`在 const 声明的初始化表达式中,可以省略 const,如上面的 baz 所示。详细信息请参阅“不要冗余使用 const”。
你可以改变非 final、非 const 变量的值,即使它曾经具有 const 值:
dart
foo = [1, 2, 3]; // 之前是 const []你不能改变 const 变量的值:
dart
baz = [42]; // 错误:常量变量不能被赋值。你可以定义使用类型检查和类型转换(is 和 as)、集合条件判断(if)和展开运算符(... 和 ...?)的常量:
dart
const Object i = 3; // 其中 i 是一个具有 int 值的 const Object...
const list = [i as int]; // 使用类型转换。
const map = {if (i is int) i: 'int'}; // 使用 is 和集合 if。
const set = {if (list is List<int>) ...list}; // ...和展开运算符。提示: 虽然 final 对象本身不能被修改,但它的字段可以被更改。相比之下,const 对象及其字段都不能被更改,它们是不可变的。
有关使用 const 创建常量值的更多信息,请参阅“列表、映射和类”部分。
通配符变量
版本说明: 通配符变量需要至少 3.7 的语言版本。
名为 _ 的通配符变量用于声明一个非绑定的局部变量或参数,本质上是一个占位符。如果有初始化器,它仍然会被执行,但该值无法被访问。在同一命名空间中,可以存在多个名为 _ 的声明,而不会出现冲突错误。
可能影响库隐私的顶级声明或成员不能使用通配符变量。在块作用域内的局部声明(如以下示例)可以声明通配符:
- 局部变量声明
dart
main() {
var _ = 1;
int _ = 2;
}for循环变量声明
dart
for (var _ in list) {}catch子句参数
dart
try {
throw '!';
} catch (_) {
print('oops');
}- 泛型类型和函数类型参数
dart
class T<_> {}
void genericFunction<_>() {}
takeGenericCallback(<_>() => true);- 函数参数
dart
Foo(_, this._, super._, void _()) {}
list.where((_) => true);
void f(void g(int _, bool _)) {}
typedef T = void Function(String _, String _);提示: 启用 unnecessary_underscores 检查规则,以识别何时可以用单个非绑定通配符变量 _ 替代之前为避免名称冲突而使用多个绑定下划线(如 __、___ 等)的做法。
操作符
Dart 支持下表中所示的运算符。该表展示了 Dart 运算符的结合性和优先级,从高到低排列,这大致体现了 Dart 运算符之间的关系。你可以将其中许多运算符作为类成员来实现。
| 描述 | 运算符 | 结合性 |
|---|---|---|
| 一元后缀 | expr++ expr-- () [] ?[] . ?. ! | 无 |
| 一元前缀 | -expr !expr ~expr ++expr --expr await expr | 无 |
| 乘法类 | * / % ~/ | 左 |
| 加法类 | + - | 左 |
| 移位 | << >> >>> | 左 |
| 按位与 | & | 左 |
| 按位异或 | ^ | 左 |
| 按位或 | | | 左 |
| 关系和类型测试 | >= > <= < as is is! | 无 |
| 相等性 | == != | 无 |
| 逻辑与 | && | 左 |
| 逻辑或 | || | 左 |
| 空值判断 | ?? | 左 |
| 条件 | expr1 ? expr2 : expr3 | 右 |
| 级联 | .. ?.. | 左 |
| 赋值 | = *= /= += -= &= ^= 等 | 右 |
| 展开(见注释) | ... ...? | 无 |
警告: 上表仅作为参考。运算符优先级和结合性的概念只是对语言语法中实际规则的近似描述。你可以在 Dart 语言规范定义的语法中找到 Dart 运算符关系的权威行为。
使用运算符时,你会创建表达式。以下是一些运算符表达式的示例:
dart
a++
a + b
a = b
a == b
c ? a : b
a is T运算符优先级示例
在运算符表中,每个运算符的优先级都高于后续行中的运算符。例如,乘法运算符 % 的优先级高于相等运算符 ==(因此会先执行),而相等运算符 == 的优先级又高于逻辑与运算符 &&。这种优先级意味着以下两行代码的执行方式相同:
dart
// 括号提高了可读性。
if ((n % i == 0) && (d % i == 0)) {
// ...
}
// 可读性较差,但效果相同。
if (n % i == 0 && d % i == 0) {
// ...
}警告: 对于需要两个操作数的运算符,最左边的操作数决定使用哪个方法。例如,如果你有一个 Vector 对象和一个 Point 对象,那么 aVector + aPoint 会使用 Vector 的加法运算符 +。
算术运算符
Dart 支持常见的算术运算符,如下表所示。
| 运算符 | 含义 |
|---|---|
+ | 加法 |
- | 减法 |
-expr | 一元负号,也称为取反(反转表达式的符号) |
* | 乘法 |
/ | 除法 |
~/ | 除法,返回整数结果 |
% | 获取整数除法的余数(取模) |
示例:
dart
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // 结果是双精度浮点数
assert(5 ~/ 2 == 2); // 结果是整数
assert(5 % 2 == 1); // 余数
assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');Dart 还支持前缀和后缀的自增和自减运算符。
| 运算符 | 含义 |
|---|---|
++var | var = var + 1(表达式的值为 var + 1) |
var++ | var = var + 1(表达式的值为 var) |
--var | var = var - 1(表达式的值为 var - 1) |
var-- | var = var - 1(表达式的值为 var) |
示例:
dart
int a;
int b;
a = 0;
b = ++a; // 在 b 获取值之前递增 a。
assert(a == b); // 1 == 1
a = 0;
b = a++; // 在 b 获取值之后递增 a。
assert(a != b); // 1 != 0
a = 0;
b = --a; // 在 b 获取值之前递减 a。
assert(a == b); // -1 == -1
a = 0;
b = a--; // 在 b 获取值之后递减 a。
assert(a != b); // -1 != 0相等性和关系运算符
下表列出了相等性和关系运算符的含义。
| 运算符 | 含义 |
|---|---|
== | 相等;见下文讨论 |
!= | 不相等 |
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
要测试两个对象 x 和 y 是否表示相同的事物,使用 == 运算符。(在极少数情况下,你需要知道两个对象是否是完全相同的对象,则使用 identical() 函数。)== 运算符的工作方式如下:
- 如果
x或y为null,若两者都为null则返回true,若只有一个为null则返回false。 - 返回在
x上调用==方法并传入参数y的结果。(没错,像==这样的运算符是在其第一个操作数上调用的方法。详细信息请参阅“运算符”部分。)
以下是使用每个相等性和关系运算符的示例:
dart
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);类型测试运算符
as、is 和 is! 运算符在运行时检查类型时非常有用。
| 运算符 | 含义 |
|---|---|
as | 类型转换(也用于指定库前缀) |
is | 如果对象具有指定的类型,则为 true |
is! | 如果对象不具有指定的类型,则为 true |
如果 obj 实现了 T 指定的接口,则 obj is T 的结果为 true。例如,obj is Object? 始终为 true。
当且仅当你确定对象是特定类型时,使用 as 运算符将对象转换为该类型。示例:
dart
(employee as Person).firstName = 'Bob';如果你不确定对象是否为 T 类型,则在使用对象之前使用 is T 检查类型。
dart
if (employee is Person) {
// 类型检查
employee.firstName = 'Bob';
}注意: 这两段代码并不等价。如果 employee 为 null 或不是 Person 类型,第一个示例会抛出异常;第二个示例则不执行任何操作。
赋值运算符
如你所见,你可以使用 = 运算符赋值。若要仅在被赋值的变量为 null 时进行赋值,使用 ??= 运算符。
dart
// 给 a 赋值
a = value;
// 如果 b 为 null,则给 b 赋值;否则,b 保持不变
b ??= value;复合赋值运算符(如 +=)将操作和赋值结合起来。
= | *= | %= | >>>= | ^= |
+= | /= | <<= | &= | ` |
-= | ~/= | >>= |
复合赋值运算符的工作方式如下:
运算符 op | a op= b | a = a op b |
|---|---|---|
| 示例 | a += b | a = a + b |
以下示例使用了赋值和复合赋值运算符:
dart
var a = 2; // 使用 = 赋值
a *= 3; // 赋值并相乘:a = a * 3
assert(a == 6);逻辑运算符
你可以使用逻辑运算符对布尔表达式进行取反或组合。
| 运算符 | 含义 |
|---|---|
!expr | 反转后续表达式(将 false 变为 true,反之亦然) |
|| | 逻辑或 |
&& | 逻辑与 |
以下是使用逻辑运算符的示例:
dart
if (!done && (col == 0 || col == 3)) {
// ...执行某些操作...
}按位和移位运算符
在 Dart 中,你可以操作数字的各个位。通常,你会将这些按位和移位运算符与整数一起使用。
| 运算符 | 含义 |
|---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~expr | 一元按位取反(0 变为 1;1 变为 0) |
<< | 左移 |
>> | 右移 |
>>> | 无符号右移 |
注意: 对于大的或负的操作数,按位运算的行为在不同平台上可能有所不同。要了解更多信息,请查看“按位运算的平台差异”。
以下是使用按位和移位运算符的示例:
dart
final value = 0x22;
final bitmask = 0x0f;
assert((value & bitmask) == 0x02); // 按位与
assert((value & ~bitmask) == 0x20); // 按位与非
assert((value | bitmask) == 0x2f); // 按位或
assert((value ^ bitmask) == 0x2d); // 按位异或
assert((value << 4) == 0x220); // 左移
assert((value >> 4) == 0x02); // 右移
// 右移示例,在 Web 上会有不同行为
// 因为操作数的值在掩码为 32 位时会改变:
assert((-value >> 4) == -0x03);
assert((value >>> 4) == 0x02); // 无符号右移
assert((-value >>> 4) > 0); // 无符号右移版本说明: >>> 运算符(也称为三移位或无符号移位)需要至少 2.14 的语言版本。
条件表达式
Dart 有两个运算符可让你简洁地计算那些原本可能需要 if - else 语句的表达式:
condition ? expr1 : expr2:如果condition为true,则计算expr1(并返回其值);否则,计算并返回expr2的值。expr1 ?? expr2:如果expr1不为null,则返回其值;否则,计算并返回expr2的值。
当你需要根据布尔表达式赋值时,可以考虑使用条件运算符 ? 和 :。
dart
var visibility = isPublic ? 'public' : 'private';如果布尔表达式用于测试 null,可以考虑使用空值判断运算符 ??(也称为空合并运算符)。
dart
String playerName(String? name) => name ?? 'Guest';前面的示例至少可以用另外两种方式编写,但都不如这样简洁:
dart
// 稍长的版本使用 ?: 运算符。
String playerName(String? name) => name != null ? name : 'Guest';
// 很长的版本使用 if - else 语句。
String playerName(String? name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}级联表示法
级联运算符(..、?..)允许你对同一个对象进行一系列操作。除了访问实例成员,你还可以在同一个对象上调用实例方法。这通常可以省去创建临时变量的步骤,让你编写更流畅的代码。
考虑以下代码:
dart
var paint =
Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;构造函数 Paint() 返回一个 Paint 对象。级联表示法后面的代码对该对象进行操作,忽略可能返回的任何值。
前面的示例等同于以下代码:
dart
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;如果级联操作的对象可能为 null,则对第一个操作使用空值短路级联运算符(?..)。以 ?.. 开头可确保不会对 null 对象尝试任何级联操作。
dart
document.querySelector('#confirm') // 获取一个对象。
?..textContent =
'Confirm' // 使用其成员。
..classList.add('important')
..onClick.listen((e) => window.alert('Confirmed!'))
..scrollIntoView();前面的代码等同于以下代码:
dart
final button = document.querySelector('#confirm');
button?.textContent = 'Confirm';
button?.classList.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();你还可以嵌套级联操作。例如:
dart
final addressBook =
(AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone =
(PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();要注意在返回实际对象的函数上构建级联操作。例如,以下代码会失败:
dart
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // 错误:方法 'write' 未为 'void' 定义。sb.write() 调用返回 void,而你不能在 void 上构建级联操作。
注意: 严格来说,级联操作的“双点”表示法不是运算符,它只是 Dart 语法的一部分。
展开运算符
展开运算符计算一个产生集合的表达式,解包结果值,并将它们插入到另一个集合中。
展开运算符实际上不是运算符表达式。.../...? 语法是集合字面量本身的一部分。因此,你可以在“集合”页面上了解更多关于展开运算符的信息。
由于它不是运算符,该语法没有任何“运算符优先级”。实际上,它具有最低的“优先级” —— 任何类型的表达式都可以作为展开目标,例如:
dart
[...a + b]其他运算符
你在其他示例中已经见过大多数剩余的运算符:
| 运算符 | 名称 | 含义 |
|---|---|---|
() | 函数调用 | 表示函数调用 |
[] | 下标访问 | 表示调用可重写的 [] 运算符;示例:fooList[1] 将整数 1 传递给 fooList 以访问索引为 1 的元素 |
?[] | 条件下标访问 | 类似于 [],但最左边的操作数可以为 null;示例:fooList?[1] 将整数 1 传递给 fooList 以访问索引为 1 的元素,除非 fooList 为 null(在这种情况下,表达式的值为 null) |
. | 成员访问 | 引用表达式的属性;示例:foo.bar 从表达式 foo 中选择属性 bar |
?. | 条件成员访问 | 类似于 .,但最左边的操作数可以为 null;示例:foo?.bar 从表达式 foo 中选择属性 bar,除非 foo 为 null(在这种情况下,foo?.bar 的值为 null) |
! | 非空断言运算符 | 将表达式转换为其底层的非可空类型,如果转换失败则抛出运行 |
注释
Dart 支持单行注释、多行注释和文档注释。
单行注释
单行注释以 // 开头。// 到行尾之间的所有内容都会被 Dart 编译器忽略。
dart
void main() {
// 待办事项:重构为一个抽象羊驼问候工厂?
print('欢迎来到我的羊驼农场!');
}多行注释
多行注释以 /* 开头,以 */ 结尾。/* 和 */ 之间的所有内容都会被 Dart 编译器忽略(除非该注释是文档注释,见下一节)。多行注释可以嵌套。
dart
void main() {
/*
* 这活儿太累了,要不考虑养鸡吧。
Llama larry = Llama();
larry.feed();
larry.exercise();
larry.clean();
*/
}文档注释
文档注释是多行或单行注释,以 /// 或 /** 开头。在连续的行上使用 /// 与多行文档注释具有相同的效果。
在文档注释中,分析器会忽略所有文本,除非文本被方括号括起来。使用方括号,你可以引用类、方法、字段、顶级变量、函数和参数。方括号中的名称会在被记录的程序元素的词法作用域中解析。
以下是一个包含对其他类和参数引用的文档注释示例:
dart
/// 一种驯化的南美骆驼科动物(羊驼属)。
///
/// 自前西班牙殖民时代以来,安第斯文化就一直将羊驼用作肉类来源和驮兽。
///
/// 和其他动物一样,羊驼也需要进食,
/// 所以别忘了用 [Food] 来 [feed] 它们。
class Llama {
String? name;
/// 用 [food] 喂养你的羊驼。
///
/// 一般的羊驼每周吃一捆干草。
void feed(Food food) {
// ...
}
/// 让你的羊驼进行 [activity] 活动,持续 [timeLimit] 分钟。
void exercise(Activity activity, int timeLimit) {
// ...
}
}在该类生成的文档中,[feed] 会变成指向 feed 方法文档的链接,[Food] 会变成指向 Food 类文档的链接。
若要解析 Dart 代码并生成 HTML 文档,你可以使用 Dart 的文档生成工具 dart doc。关于生成文档的示例,请参阅 Dart API 文档。关于如何组织注释的建议,请参阅《高效 Dart:文档》。
元数据
使用元数据可以为你的代码提供额外的信息。元数据注解以 @ 字符开头,后面可以是对编译时常量(如 deprecated)的引用,也可以是对常量构造函数的调用。
所有 Dart 代码都可以使用四种注解:@Deprecated、@deprecated、@override 和 @pragma。有关使用 @override 的示例,请参阅“扩展类”部分。以下是使用 @Deprecated 注解的示例:
dart
class Television {
/// 请使用 [turnOn] 来开启电源。
@Deprecated('请使用 turnOn 代替')
void activate() {
turnOn();
}
/// 开启电视电源。
void turnOn() {
...
}
// ···
}如果你不想指定消息,可以使用 @deprecated。不过,我们建议始终使用 @Deprecated 并指定消息。
你可以定义自己的元数据注解。以下是一个定义 @Todo 注解的示例,该注解接受两个参数:
dart
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}以下是使用 @Todo 注解的示例:
dart
@Todo('Dash', '实现这个函数')
void doSomething() {
print('做些事情');
}元数据可以出现在库、类、类型定义、类型参数、构造函数、工厂方法、函数、字段、参数或变量声明之前,也可以出现在 import 或 export 指令之前。
库 & 导入
import 和 library 指令能帮助你创建模块化且可共享的代码库。库不仅能提供 API,还是隐私的基本单位:以下划线(_)开头的标识符仅在库内部可见。每个 Dart 文件(及其部分)都是一个库,即使它没有使用 library 指令。
库可以通过包来分发。
注意: 若想了解 Dart 为何使用下划线而非 public 或 private 等访问修饰符关键字,请查阅 SDK 问题 33383。
使用库
使用 import 来指定一个库的命名空间如何在另一个库的作用域中使用。
例如,Dart Web 应用通常会使用 dart:js_interop 库,可以像这样导入:
dart
import 'dart:js_interop';import 唯一必需的参数是指定库的 URI。对于内置库,URI 使用特殊的 dart: 方案。对于其他库,你可以使用文件系统路径或 package: 方案。package: 方案用于指定由包管理器(如 pub 工具)提供的库。例如:
dart
import 'package:test/test.dart';注意: URI 代表统一资源标识符。URL(统一资源定位符)是一种常见的 URI。
指定库前缀
如果你导入了两个具有冲突标识符的库,那么可以为其中一个或两个库指定前缀。例如,如果 library1 和 library2 都有一个 Element 类,那么你可能会有如下代码:
dart
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// 使用 lib1 中的 Element。
Element element1 = Element();
// 使用 lib2 中的 Element。
lib2.Element element2 = lib2.Element();使用通配符名称 _ 作为导入前缀是非绑定的,但可以访问该库中的非私有扩展。
仅导入库的部分内容
如果你只想使用库的一部分,可以有选择地导入该库。例如:
dart
// 仅导入 foo。
import 'package:lib1/lib1.dart' show foo;
// 导入除 foo 之外的所有名称。
import 'package:lib2/lib2.dart' hide foo;懒加载库
延迟加载(也称为懒加载)允许 Web 应用在需要时按需加载库。当你有以下一个或多个需求时,可以使用延迟加载:
- 减少 Web 应用的初始启动时间。
- 进行 A/B 测试,例如尝试算法的替代实现。
- 加载很少使用的功能,如可选屏幕和对话框。
这并不意味着 Dart 会在启动时加载所有延迟组件。Web 应用可以在需要时通过网络下载延迟组件。
dart 工具不支持除 Web 之外的目标平台的延迟加载。如果你正在构建 Flutter 应用,请参考 Flutter 延迟组件指南中关于延迟加载的实现。
若要懒加载一个库,首先使用 deferred as 导入它。
dart
import 'package:greetings/hello.dart' deferred as hello;当你需要该库时,使用库的标识符调用 loadLibrary()。
dart
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}在上述代码中,await 关键字会暂停执行,直到库加载完成。有关 async 和 await 的更多信息,请参阅异步支持部分。
你可以多次调用库的 loadLibrary() 方法,不会有问题。库只会加载一次。
使用延迟加载时,请记住以下几点:
- 延迟库中的常量在导入文件中不是常量。请记住,这些常量在延迟库加载之前是不存在的。
- 你不能在导入文件中使用延迟库中的类型。相反,可以考虑将接口类型移动到一个同时被延迟库和导入文件导入的库中。
- Dart 会隐式地将
loadLibrary()插入到你使用deferred as namespace定义的命名空间中。loadLibrary()函数返回一个Future。
library 指令
若要指定库级别的文档注释或元数据注解,可以将它们附加到文件开头的库声明上。
dart
/// 一个非常棒的测试库。
@TestOn('browser')
library;关键字
下表列出了 Dart 语言保留供自身使用的词汇。除非另有说明,否则这些词汇不能用作标识符。即使允许使用,将关键字用作标识符也可能会让阅读你代码的其他开发者感到困惑,因此应该避免。若要了解更多关于标识符使用的信息,请点击相应术语。
abstract ² | as ² | assert | async ³ |
await ¹ | base ³ | break | case |
catch | class | const | continue |
covariant ² | default | deferred ² | do |
dynamic ² | else | enum | export ² |
extends | extension ² | external ² | factory ² |
false | final(变量) | final(类) | finally |
for | Function ² | get ² | hide ³ |
if | implements ² | import ² | in |
interface ² | is | late ² | library ² |
mixin ² | new | null | of ³ |
on ³ | operator ² | part ² | required ² |
rethrow | return | sealed ³ | set ² |
show ³ | static ² | super | switch |
sync ³ | this | throw | true |
try | type ² | typedef ² | var |
void | when ³ | with | while |
yield ¹ |
- ¹ 此关键字可根据上下文用作标识符。
- ² 此关键字不能用作类型(类、混入、枚举、扩展类型或类型别名)的名称、扩展的名称或导入前缀。在其他所有情况下,它都可以用作标识符。
- ³ 此关键字可无限制地用作标识符。
