目次

package:webへの移行

Dartのpackage:webは、ブラウザAPIへのアクセスを提供し、DartアプリケーションとWeb間の相互運用を可能にします。 package:webを使用して、ブラウザと対話し、DOM内のオブジェクトと要素を操作します。

dart
import 'package:web/web.dart';

void main() {
  final div = document.querySelector('div')!;
  div.text = 'Text set at ${DateTime.now()}';
}

package:web vs dart:html

#

package:webの目標は、既存のDart Webライブラリに関するいくつかの懸念事項に対処することにより、DartがWeb APIを公開する方法を刷新することです。

  1. Wasm互換性

    パッケージは、Wasmと互換性があるのは、dart:js_interopdart:js_interop_unsafeを使用している場合のみです。 package:webdart:js_interopに基づいているため、デフォルトではdart2wasmでサポートされています。

    dart:htmldart:svgなどのDartコアWebライブラリは、Wasmにコンパイルする場合は**サポートされていません**。

  2. 最新の状態を保つ

    package:webは、Web IDLを使用して、IDLの各宣言の相互運用メンバー相互運用型を自動的に生成します。 dart:htmlの追加メンバーや抽象化とは対照的に、参照を直接生成することで、package:webはより簡潔で、理解しやすく、より一貫性があり、Web開発の将来に合わせて最新の状態を維持しやすくなります。

  3. バージョン管理

    パッケージであるため、package:webは、dart:htmlのようなライブラリよりも簡単にバージョン管理でき、進化してもユーザーコードを壊すことを回避できます。 また、コードの排他性を軽減し、貢献しやすくなります。 開発者は、独自の代替相互運用宣言を作成し、package:webと競合することなく使用できます。


これらの改善により、package:webdart:htmlの間には、実装にいくつかの違いが生じます。 IDLの名前変更型テストなど、既存のパッケージに最も影響を与える変更については、以下の移行セクションで説明します。 簡潔にするためにdart:htmlのみを参照していますが、同じ移行パターンは、dart:svgなどの他のDartコアWebライブラリにも適用されます。

dart:htmlからの移行

#

dart:htmlインポートを削除し、package:web/web.dartに置き換えます

dart
import 'dart:html' as html; // Remove
import 'package:web/web.dart' as web; // Add

pubspecのdependencieswebを追加します

dart pub add web

以下のセクションでは、dart:htmlからpackage:webへの一般的な移行の問題について説明します.

その他の移行の問題については、dart-lang/webリポジトリを確認し、問題を報告してください。

名前の変更

#

dart:htmlの多くのシンボルは、Dartスタイルとの整合性を高めるために、元のIDL宣言から名前が変更されました。 たとえば、appendChildappendになり、HTMLElementHtmlElementになりました。

これに対し、混乱を減らすため、package:webはIDL定義の元の名前を使用します。 dart fixは、dart:htmlpackage:webの間で名前が変更された型を変換するために使用できます。

インポートを変更した後、名前が変更されたオブジェクトはすべて新しい「未定義」エラーになります。 これらは、次のいずれかの方法で対処できます。

  • CLIから、dart fix --dry-runを実行します。
  • IDEで、dart fixを選択します:**'package:web名'に名前変更**。

dart fixは、一般的な型の名前変更の多くを網羅しています。 名前の変更を行うためのdart fixがないdart:html型に遭遇した場合は、まずissueを提出してお知らせください。

次に、既存のdart:htmlメンバーの定義を調べることで、package:webの型名を手動で見つけることができます。 dart:htmlメンバー定義の@Nativeアノテーションの値は、コンパイラーに、その型のJSオブジェクトをアノテーションが付けられたDartクラスとして扱うように指示します。 たとえば、@Nativeアノテーションは、dart:htmlHtmlElementメンバーのネイティブJS名がHTMLElementであることを示しているため、package:webの名前もHTMLElementになります。

dart
@Native("HTMLElement")
class HtmlElement extends Element implements NoncedElement { }

package:webで未定義のメンバーのdart:html定義を見つけるには、次のいずれかの方法を試してください。

  • IDEで未定義の名前をCtrlキーまたはCommandキーを押しながらクリックし、**定義へ移動**を選択します。
  • dart:html APIドキュメントで名前を検索し、*アノテーション*の下にあるページを確認します。

同様に、対応するdart:htmlメンバーの定義でキーワードnativeを使用している、未定義のpackage:web APIが見つかる場合があります。 定義で名前変更のために@JSNameアノテーションが使用されているかどうかを確認します。 アノテーションの値は、メンバーがpackage:webで使用する名前を示します。

dart
@JSName('appendChild')
Node append(Node node) native;

nativeは内部キーワードであり、このコンテキストではexternalと同じ意味です。

型テスト

#

dart:htmlを使用するコードでは、isのようなランタイムチェックを利用するのが一般的です。 dart:htmlオブジェクトで使用する場合、isasは、オブジェクトが@Nativeアノテーション内のJS型であることを確認します。 これに対し、すべてのpackage:web型はJSObjectに具体化されます。 つまり、ランタイム型テストの結果は、dart:html型とpackage:web型で異なる動作になります。

型テストを実行できるようにするには、is 型テストを使用している dart:html コードを、instanceOfString などの 相互運用メソッド、またはより便利で型付けされた isA ヘルパー (Dart 3.4 以降で使用可能) を使用するように移行してください。JS 型ページの 互換性、型チェック、およびキャスト セクションでは、代替方法について詳しく説明しています。

dart
obj is Window; // Remove
obj.instanceOfString('Window'); // Add

型シグネチャ

#

dart:html の多くの API は、型シグネチャでさまざまな Dart 型をサポートしています。dart:js_interop記述できる型を制限しているため、package:web の一部のメンバーでは、メンバーを呼び出す前に値を*変換*する必要があります。JS 型ページの 変換 セクションから、相互運用変換メソッドの使用方法を学習してください。

dart
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 がサポートされているかどうかによって条件付きインポートを使用するコードは一般的です

dart
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 を使用することです

dart
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 が削除されると停止します)。これは、メンバーが静的にのみ使用可能であるためです。 この問題を回避するために、仮想ディスパッチに依存するすべてのコードを移行してください。

仮想ディスパッチのユースケースの 1 つはモッキングです。dart:html クラスを implements するモッキングクラスがある場合、package:web 型を実装するために使用することはできません。 代わりに、JS オブジェクト自体をモックすることをお勧めします。 詳細については、モッキングチュートリアル を参照してください。

native ではない API

#

dart:html クラスには、重要な実装を持つ API が含まれている場合があります。 これらのメンバーは、package:webヘルパー に存在する場合と存在しない場合があります。 コードがその実装の specifics に依存している場合は、必要なコードをコピーできる場合があります。 ただし、それが難しいと思われる場合、またはそのコードが他のユーザーにも役立つと思われる場合は、問題を報告するか、package:web にプルリクエストをアップロードして、そのメンバーをサポートすることを検討してください。

ゾーン

#

dart:html では、コールバックは自動的にゾーン化されます。 package:web ではそうではありません。 現在のゾーンでのコールバックの自動バインディングはありません。

これがアプリケーションにとって重要な場合は、ゾーンを使用できますが、コールバックをバインドすることで 自分で記述する 必要があります。 詳細については、#54507 を参照してください。 これを自動的に行うための変換 API や ヘルパー はまだありません。

ヘルパー

#

package:web のコアには external 相互運用メンバーが含まれていますが、dart:html がデフォルトで提供していた他の機能は提供していません。 これらの違いを軽減するために、package:web には、コアの相互運用では直接利用できない多くのユースケースの処理をさらにサポートするための ヘルパー が含まれています。 ヘルパーライブラリには、Dart ウェブライブラリのレガシー機能を公開するためのさまざまなメンバーが含まれています。

たとえば、コアの package:web は、イベントリスナーの追加と削除のみをサポートしています。 代わりに、Dart Stream を使用してイベントを簡単に subscribe できる ストリームヘルパー を使用できます。そのコードを自分で記述する必要はありません。

dart
// dart:html version
InputElement htmlInput = InputElement();
await htmlInput.onBlur.first;

// package:web version
HTMLInputElement webInput = document.createElement('input') as HTMLInputElement;
await webInput.onBlur.first;

すべてヘルパーとそのドキュメントは、リポジトリの package:web/helpers にあります。 これらは、ユーザーの移行を支援し、Web API を使いやすくするために継続的に更新されます。

#

dart:html から package:web に移行されたパッケージの例を次に示します