遷移至 package:web
Dart 的 package:web
提供對瀏覽器 API 的存取,實現 Dart 應用程式與網頁之間的互通性。使用 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 網頁函式庫的一些問題,來改造 Dart 公開網頁 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
更簡潔、更容易理解、更一致,並且更能與未來網頁的發展保持同步。版本控制
由於它是一個套件,
package:web
可以比像dart:html
這樣的函式庫更容易進行版本控制,並避免在發展過程中破壞使用者程式碼。它也使程式碼更不具排他性,並更開放地接受貢獻。開發人員可以建立自己的替代互通宣告,並將它們與package:web
一起使用,而不會產生衝突。
這些改進自然會導致 package:web
和 dart:html
之間的一些實作差異。在後續的遷移章節中,將說明對現有套件影響最大的變更,例如 IDL 重新命名和型別測試。為了簡潔起見,我們僅提及 dart:html
,但相同的遷移模式適用於任何其他 Dart 核心網頁函式庫,例如 dart:svg
。
從 dart:html
遷移
#移除 dart:html
匯入,並將其替換為 package:web/web.dart
import 'dart:html' as html; // Remove
import 'package:web/web.dart' as web; // Add
將 web
新增至您的 pubspec 中的 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 name
」。
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
來使用條件式匯入,以區分原生和網頁
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
來區分原生和網頁
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
helpers 中。如果您的程式碼依賴於該實作的細節,您或許可以複製必要的程式碼。但是,如果您認為這不可行,或者該程式碼對其他使用者也有益,請考慮提出 issue 或上傳 pull request 到 package:web
以支援該成員。
區域
#在 dart:html
中,回呼會自動進行分區。package:web
則不然。目前的區域中沒有回呼的自動綁定。
如果這對您的應用程式很重要,您仍然可以使用區域,但您必須透過綁定回呼自行撰寫。詳情請參閱 #54507。目前還沒有轉換 API 或 helper 可用於自動執行此操作。
輔助程式
#package:web
的核心包含 external
互操作成員,但不提供 dart:html
預設提供的其他功能。為了減輕這些差異,package:web
包含 helpers
,以提供額外支援來處理許多無法透過核心互操作直接取得的使用案例。輔助函式庫包含各種成員,以公開 Dart Web 函式庫中的一些舊版功能。
例如,核心 package:web
僅支援新增和移除事件監聽器。相反地,您可以使用 串流輔助函式,可以輕鬆地使用 Dart Stream
訂閱事件,而無需自行撰寫程式碼。
// 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
的套件範例
除非另有說明,否則本網站上的文件反映的是 Dart 3.6.0。頁面上次更新時間為 2024-11-17。 檢視原始碼 或 回報問題。