迁移到 package:web
Dart 的 package:web
提供了对浏览器 API 的访问,从而实现了 Dart 应用程序与 Web 之间的互操作。使用 package:web
可以与浏览器交互并操作 DOM 中的对象和元素。
import 'package:web/web.dart';
void main() {
final div = document.querySelector('div')!;
div.text = 'Text set at ${DateTime.now()}';
}
package:web
与 dart:html
#package:web
的目标是通过解决现有 Dart Web 库的几个问题来改进 Dart 公开 Web API 的方式:
Wasm 兼容性
只有使用
dart:js_interop
和dart:js_interop_unsafe
的包才能与 Wasm 兼容。package:web
基于dart:js_interop
,因此默认情况下,它在dart2wasm
上受支持。保持现代化
package:web
使用 Web IDL 自动生成每个 IDL 声明的 互操作成员 和 互操作类型 。与dart:html
中的附加成员和抽象相反,直接生成引用使package:web
更简洁、更容易理解、更一致,并且能够更好地与 Web 开发的未来保持同步。版本控制
作为包,
package:web
比dart:html
等库更容易进行版本控制,并且可以在其发展过程中避免破坏用户代码。它还使代码不那么排他性,更容易为其做出贡献。开发人员可以创建他们自己的 替代互操作声明 ,并将其与package:web
一起使用而不会发生冲突。
这些改进自然会导致 package:web
和 dart:html
之间的一些实现差异。对现有包影响最大的更改(如 IDL 重命名 和 类型测试 )将在后续的迁移部分中进行介绍。为简洁起见,我们只提及 dart:html
,但相同的迁移模式也适用于任何其他 Dart 核心 Web 库,例如 dart:svg
。
从 dart:html
迁移
#移除 dart:html
导入并将其替换为 package:web/web.dart
:
import 'dart:html' as html; // Remove
import 'package:web/web.dart' as web; // Add
在你的 pubspec 中将 web
添加到 dependencies
中:
dart pub add web
以下部分介绍了从 dart:html
到 package:web
的一些常见迁移问题。
对于任何其他迁移问题,请查看 dart-lang/web 仓库并提交问题。
重命名
#dart:html
中的许多符号已从其原始 IDL 声明重命名为更符合 Dart 风格。例如, appendChild
变为 append
, HTMLElement
变为 HtmlElement
等。
相反,为了减少混淆, package:web
使用 IDL 定义中的原始名称。可以使用 dart fix
来转换 dart:html
和 package:web
之间已重命名的类型。
更改导入后,任何重命名的对象都会出现新的“未定义”错误。您可以通过以下任一方式解决这些问题:
- 从 CLI 运行
dart fix --dry-run
。 - 在您的 IDE 中,选择
dart fix
: 重命名为“package:web 名称
” 。
dart fix
包含许多常见的类型重命名。如果您遇到没有 dart fix
来重命名的 dart:html
类型,请先通过提交 问题 让我们知道。
然后,您可以尝试通过查找其定义来手动发现现有 dart:html
成员的 package:web
类型名称。 dart:html
成员定义上的 @Native
注解的值告诉编译器将任何该类型的 JS 对象视为其注释的 Dart 类。例如, @Native
注解告诉我们 dart:html
的 HtmlElement
成员的原生 JS 名称是 HTMLElement
,因此 package:web
名称也将是 HTMLElement
:
@Native("HTMLElement")
class HtmlElement extends Element implements NoncedElement { }
要查找 package:web
中未定义成员的 dart:html
定义,请尝试以下任一方法:
- 在 IDE 中按住 Ctrl 或 command 并单击未定义的名称,然后选择 转到定义 。
- 在
dart:html
API 文档 中搜索名称,并在其页面下查看 注释 。
同样地,您可能会发现一个未定义的 package:web
API,其对应的 dart:html
成员的定义使用关键字 native
。检查定义是否使用 @JSName
注解进行重命名;注解的值将告诉您成员在 package:web
中使用的名称:
@JSName('appendChild')
Node append(Node node) native;
在此上下文中, native
是一个与 external
含义相同的内部关键字。
类型测试
#使用 dart:html
的代码通常会使用 is
之类的运行时检查。当与 dart:html
对象一起使用时, is
和 as
会验证该对象是否是 @Native
注解中的 JS 类型。相反,所有 package:web
类型都被具体化为 JSObject
。这意味着 dart:html
和 package:web
类型之间的运行时类型测试将产生不同的行为。
为了能够执行类型测试,请将使用 is
类型测试的任何 dart:html
代码迁移为使用 互操作方法 (例如 instanceOfString
)或更方便且类型化的 isA
帮助程序(从 Dart 3.4 开始可用)。JS 类型页面的 兼容性、类型检查和强制转换 部分详细介绍了替代方法。
obj is Window; // Remove
obj.instanceOfString('Window'); // Add
类型签名
#dart:html
中的许多 API 在其类型签名中支持各种 Dart 类型。因为 dart:js_interop
限制了可以编写的类型,所以 package:web
中的某些成员现在需要您在调用成员之前 转换 该值。从 JS 类型页面的 转换 部分了解如何使用互操作转换方法。
window.addEventListener('click', callback); // Remove
window.addEventListener('click', callback.toJS); // Add
通常,您可以发现哪些方法需要转换,因为它们将被标记为某种类型的异常:
A value of type '...' can't be assigned to a variable of type 'JSFunction?'
条件导入
#代码通常会根据是否支持 dart:html
来使用条件导入,以区分原生和 Web:
export 'src/hw_none.dart'
if (dart.library.io) 'src/hw_io.dart'
if (dart.library.html) 'src/hw_html.dart';
但是,由于编译为 Wasm 时不支持 dart:html
,因此正确的替代方法现在是使用 dart.library.js_interop
来区分原生和 Web:
export 'src/hw_none.dart'
if (dart.library.io) 'src/hw_io.dart'
if (dart.library.js_interop) 'src/hw_web.dart';
虚拟分派和模拟
#dart:html
类支持虚拟分派,但由于 JS 互操作使用扩展类型,因此[无法]进行虚拟分派。同样,使用 package:web
类型的 dynamic
调用也不会按预期工作(或者,它们可能只是偶然地继续工作,但在删除 dart:html
后将停止工作),因为它们的成员只能静态使用。迁移所有依赖于虚拟分派的代码以避免此问题。
虚拟分派的一种用例是模拟。如果您有一个模拟类来 implements
一个 dart:html
类,则它不能用于实现 package:web
类型。相反,最好模拟 JS 对象本身。有关更多信息,请参阅模拟教程。
非 native
API
#dart:html
类也可能包含具有非平凡实现的 API。这些成员可能存在于 package:web
的 帮助程序 中,也可能不存在。如果您的代码依赖于该实现的细节,您可以复制必要的代码。但是,如果您认为这是不可行的,或者该代码对其他用户也有益,请考虑提交问题或上传拉取请求到 package:web
以支持该成员。
区域
#在 dart:html
中,回调会自动进行区域化。 package:web
中并非如此。当前区域中没有回调的自动绑定。
如果这对您的应用程序很重要,您仍然可以使用区域,但是您必须通过绑定回调 自己编写它们 。有关详细信息,请参见 #54507。目前还没有可用的转换 API 或 帮助程序 来自动执行此操作。
帮助程序
#package:web
的核心包含 external
互操作成员,但不提供 dart:html
默认提供的其他功能。为了减轻这些差异, package:web
包含 helpers
以在处理许多通过核心互操作无法直接获得的用例方面提供额外的支持。帮助程序库包含各种成员,用于公开来自 Dart Web 库的某些旧版功能。
例如,核心 package:web
仅支持添加和删除事件侦听器。相反,您可以使用 流帮助程序 ,它使使用 Dart Stream
订阅事件变得容易,而无需自己编写该代码。
// dart:html 版本
InputElement htmlInput = InputElement();
await htmlInput.onBlur.first;
// package:web 版本
HTMLInputElement webInput = document.createElement('input') as HTMLInputElement;
await webInput.onBlur.first;
您可以在 package:web/helpers
的存储库中找到所有帮助程序及其文档。它们将不断更新,以帮助用户迁移并简化 Web API 的使用。
示例
#以下是一些已从 dart:html
迁移到 package:web
的包示例:
除非另有说明,否则本网站上的文档反映的是 Dart 3.6.0。页面最后更新于 2025-02-05。 查看源代码 或 报告问题.