0%

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣布 Travis CI 上 Dart 的 Windows 支援

最近,Travis CI——一個用於 GitHub repos 的持續整合 (CI) 服務——宣布 對 Windows 作業系統的早期版本支援。今天,我們宣布 Windows 上的 Travis CI 支援 Dart SDK,因此您可以在所有三大桌面作業系統:Linux、Mac 和 Windows 上運行 Dart 建置和測試作業。

為多個作業系統設定 Travis CI 作業

如果您已經使用 Travis CI 建置或測試您的 Dart 程式碼,您可以輕鬆地在所有三個作業系統上運行您的作業。只需在您的 repo 的 .travis.yml 檔案中加入(或擴展)os: 部分:

1
2
3
4
os:
- linux
- osx
- windows

然後,每當 Travis CI 運行時,您都會看到在每個啟用的作業系統上運行的每個測試設定的項目。以下是來自 grpc-dart repo 的範例,在我們將 Windows 加入測試矩陣之後:

在 Linux、MacOS 和 Windows 上運行的 Travis CI 測試矩陣

由於 Travis CI 上的 Windows 支援仍處於早期版本階段,我們建議您在將 Windows 加入到 Travis 作業之前,先查看已知問題

將 Travis CI 測試支援加入到專案

如果您尚未使用 Travis CI 測試 Dart,則很容易開始使用。最小的 .travis.yml 檔案只包含一行:

1
language: dart

此設定等同於 pub run test

dart_task: 標籤下列出任何其他任務。這些任務包括運行靜態分析和程式碼格式檢查(有關完整詳細資訊,請參閱Travis 文件)。

以下是運行靜態分析的方法,它會檢查您的程式碼中是否存在分析錯誤:

1
2
3
language: dart
dart_task:
- dartanalyzer

以下是確保所有 Dart 檔案都已正確格式化的方法:

1
2
3
language: dart
dart_task:
- dartfmt

如果您有一個包含多個 Dart 套件的更複雜的 repo,則需要更詳細的設定。mono_repo 工具中的 travis 命令是建立此設定的一種可能的解決方案。

Travis CI 的替代方案

Travis CI 是持續整合的幾個熱門供應商之一。其他支援 Dart SDK 的供應商包括 AppVeyor範例)和 Codeship。對於 Flutter 應用程式,選項包括 NeverCodeCirrusCIBitrise

目前就這樣。我們希望您喜歡 Dart 的這個新的 CI 支援。


宣布 Travis CI 上 Dart 的 Windows 支援 最初發佈於 Medium 上的 Dart,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

讓 Dart 成為更好的 UI 語言

Dart 團隊中,我們正忙於實作一些讓我非常興奮的語言變更。它們都與集合字面量有關,即用於建立列表、映射和集合的內建語法:

如果您今天沒有編寫 Dart 程式碼,這可能與您和您的人生目標沒有太大關係,但我還是希望您能繼續閱讀。我認為這些功能本身就很有趣,而且它們背後的執行模型可能會以有用和/或引人入勝的方式拓展您的大腦。我總是覺得學習新的語言知識很有趣,即使是用我目前沒有使用的語言。

Flutter 使用者如何建構他們的 UI

如果您在過去一年中聽說過 Dart,那可能是在 Flutter 的上下文中。如果您沒聽說過這個名字,Flutter 是一個用於建構跨平台行動應用程式的 UI 架構。我無法在這裡詳細介紹它,但點選連結,它會回答您心中的每一個問題。(好吧,至少是關於 Flutter 的每一個問題。它不會告訴您為什麼您高中時暗戀的人從未回電。)

任何 UI 架構都會做出的一個關鍵選擇是如何定義基本的視覺化 UI 元素 - 按鈕、顏色、文字、佈局等。您是在某種單獨的「模板」或「標記」格式中編寫這些內容,還是在定義 UI 行為的可執行程式碼中編寫這些內容?每十五年左右,業界就會對哪個答案是正確的進行一次翻轉。

Angular 和大多數 Web 架構都遵循 HTML 的腳步並使用模板。React 將 UI 放入您的 JavaScript 中,但也新增了一個名為 JSX 的嵌入式 DSL,使其看起來像 HTML。我猜,試圖魚與熊掌兼得,儘管並非每個人都會將 HTML 描述為特別像甜點。

Flutter 使用普通的 Dart 表達式語法將 UI 直接放入您的 Dart 程式碼中。請看:

在 return 關鍵字之後的所有內容都是一個大的巢狀表達式,它會產生一部分使用者介面。使用 Dart 執行此操作有一些實質上的好處:

  • 只需學習一種語言:Dart。 由於 Dart 的設計讓來自其他語言的人感到熟悉,因此希望不會太難。
  • 在建構 UI 時,您可以使用通用程式語言的所有抽象功能。 將片段提取到可重複使用的函數中。為這些函數提供參數以改變生成的 UI。將內容儲存在局部變數中。隨心所欲。
  • 您永遠不會遇到表達能力的限制,而不必移植到其他語言。 如果您曾經使用過宣告式語言,您可能遇到過這樣的情況:您達到了它實際可以表達的極限。此時,您要么放棄您正在嘗試做的事情,要么費力地用低階的、通常是指令式語言重寫整個內容。由於您已經 使用具有完整功能的 Dart 語言中,因此您永遠不會遇到這種限制,並且您的 UI 程式碼可以順利地變得更加複雜。

當然,主要的挑戰,也是人們一開始就使用宣告式語言的原因,是用指令式語言定義內容可能非常繁瑣且難以閱讀。

想像一下,如果不是這一點 HTML:

您必須編寫如下內容:

幸運的是,現代語言和 API 並不 那麼 低階。即使語句是指令式的,表達式 也是相當宣告式的。雖然上面的程式碼很糟糕,但這段程式碼與 HTML 差不多:

現代的反應式範式,您透過從頭開始建構 UI 作為單個表達式,「建構」您的 UI,可以讓您走得很遠。上面 Flutter 範例中的相關部分只是:

它有括弧和方括弧而不是尖括弧,但除此之外與「標記」語言相距不遠。令人驚訝的是,這效果非常好。Dart 的語法基於 JavaScript,而 JavaScript 來自 Java,Java 來自 C。在此過程中,我們加入了方括弧列表字面量語法和命名參數,但這些都是相當 次要的

C 語言的設計目的是在 PDP-11 上實作命令列作業系統。它的符號在行動裝置上建構圖形化 UI 時的縮放比例並不太差,這既證明了 Ritchie 的設計品味,也證明了我們對 C 語法共同的斯德哥爾摩症候群。無論如何,它 大多數情況下 都有效。

在這裡的範例中,建構 UI 不需要任何有趣的執行時 邏輯。所有內容都很好地放入單個巢狀表達式中。但是假設,由於某些原因,您不想在星期二顯示文字的「這是 Flutter」部分。(也許您需要在螢幕上騰出空間來顯示「Taco Tuesday!」橫幅。)

有幾種方法可以表達這一點,但沒有一種方法像上面的範例那樣好和宣告式。以下是一種方法:

我們更接近於驅使人們使用模板的令人討厭的低階指令式程式碼。當我們查看真實的 Flutter 程式碼時,我們很遺憾地看到很多看起來像這樣的程式碼。因此,大約一年前,Flutter 團隊要求我們在 Dart 上提出語言變更,以使用 Dart 編寫的 UI 程式碼更容易編寫、閱讀和維護。

「UI 作為程式碼」

我們將此倡議稱為「UI 作為程式碼」,因為它是關於使用程式碼建構 UI。但最終目標是語言功能盡可能普遍適用於盡可能多的 Dart 程式,無論是否使用 Flutter。(如果您想了解更多背景資訊,這裡有一份我寫的 長篇動機文件。)

在探索了 許多選項 之後,我們決定專注於圍繞集合字面量的一些有針對性的改進。這看起來可能不像將 JSX 之類的東西塞進 Dart 中那麼性感(並不是說我完全排除這種可能性),但它的優點是使用者可以更容易地在他們的程式碼中逐步利用它。

僅僅讓列表字面量變得更有趣,似乎影響有限。但是,如果您查看上面的 Flutter UI 程式碼,它基本上是一個由建構函數調用和列表字面量組成的大樹。列表字面量佔據了很大一部分。(哎呀,整個語言 都是圍繞它們設計的。)如果您深入研究一些例子,在這些例子中,您感覺應該能夠以宣告式的方式編寫某些內容,但卻不得不進行一堆令人討厭的指令式修改,通常是圍繞 列表 的。

如果我們可以讓集合變得更好,我們就可以讓 很多 Dart 程式碼變得更好。為此,我們正在新增三個新功能:

展開運算符

通常,當您建構 Widget 列表時,其中一些 Widget 已經在其他 列表 中。以下是一些 Flutter 程式碼:

buildTab2Conversation() 方法返回一個 Widget 列表,我們希望用標題和頁尾將其圍繞起來。必須以指令式的方式建構結果列表真的很麻煩。它強制程式碼「反向」閱讀,在您看到一堆東西在 children 上亂搞之前,您必須先查看程式碼,才能看到它們是 誰的 子級。

Dart 有一個稱為 方法級聯 的功能,它可以有所幫助。這些功能讓您可以在表達式中間塞入一個修改方法調用,同時產生原始物件。有了這個,您會得到:

這有點好,但仍然很尷尬。那個尾隨的 ..add() 用於附加單個項目,尤其令人震驚。您可能已經猜到我們是如何解決這個問題的,因為許多其他語言已經有了相同的解決方案。(>90% 的語言設計是找出要從其他語言中借用哪些功能。)我們正在新增一個稱為 展開運算符新語法

在集合字面量中,展開運算符會解壓縮另一個集合並將其內容直接插入到位。例如:

列表元素之前的 ... 會導致其元素插入到周圍的列表中。這與 JavaScript 使用的語法相同。Python、Ruby 和其他一些語言使用前綴 * 來表示相同的意思,但我們認為它在視覺上不夠突出。有了這個功能,Flutter 範例變成了:

我相信這是一個真正的改進。列表視圖的所有子級都緊密地嵌套在一個列表字面量中。這看起來更好,並且與類型推斷也更好。有了列表中的所有元素,我們可以在推斷列表的 類型 時使用所有元素。

我這裡展示了一個 Flutter 範例,但我花了很長時間梳理了大量的 Dart 程式碼,以查看這種語法在哪些地方有用,它在所有地方都發揮了作用。特別是,用於調用其他程式的命令列參數列表的程式碼確實受益於展開運算符。

元素

在我介紹最後兩個功能之前,我想深入探討一下展開運算符實際上 是什麼。看起來我好像在過分強調這一點,但我保證清楚這一點在以後會有幫助。以下是一個主要問題:展開運算符是一個表達式嗎?

它看起來像一個表達式,因為它出現在列表字面量中,在預期表達式的地方:

像表達式一樣,您對它進行計算,它會產生一些資料。也許它是一個計算結果為 Iterable 物件的表達式?但是,等等,這說不通。這就是展開運算符 內部 的表達式所做的。如果您只想使用一個計算結果為 Iterable 的表達式,則無需在其前面加上 ...

展開運算符不會計算結果為單個 Iterable 物件,它會 解壓縮 該物件並計算結果為 Iterable 產生的 一系列 物件。然後將其重新打包回某個新物件是沒有用的。但是表達式總是計算結果為 單個 物件。

如果展開運算符是一個表達式,那麼在允許表達式的其他地方使用它意味著什麼?

這會做什麼?將整個 Iterable 作為物件儲存在 wat 中是沒有意義的。如果您想要這樣做,您可以完全省略 ...。答案是展開運算符 不是 表達式。它們是另一種語法類別。Dart 像許多語言一樣,已經有兩個大的語法組:語句和表達式。

語句會執行,但不產生任何結果值。相反,它們預期會產生一些有用的副作用。它們不能在任何需要值的地方使用,因為它不會給您提供值。這就是為什麼,例如,這是禁止的:

for 語句不會產生值,因此將其塞入變數初始化程式中是沒有意義的。確實有 將表達式和語句統一起來 並允許此類程式碼的語言。它們定義每個語句以某種方式執行, 產生一個值。但 Dart 不是這些語言之一。

表達式計算結果為單個結果值。您可以在值有用的地方使用它們。還有「表達式語句」 - 後跟分號的表達式 - 它們是包含單個表達式的語句。這很方便,因為許多表達式也恰好具有副作用,即使不需要它們的結果也很有用。

展開運算符不是其中任何一個。展開運算符可以計算結果為零個值(如果您展開空集合)、一個值或多個值。它是它自己的東西。這個類別的一個好名字是「生成器」。我的模型來自 Icon,其中 每個 表達式都可以是一個生成器。但 Dart 已經有了 生成器函數,所以我不想過度使用這個術語。

展開運算符只能出現在可以優雅地處理接收零個或多個值的地方。如果沒有徹底修改語言的執行模型並將其變成 Icon(我發現這很奇怪地吸引人,但可能不切實際……),那麼沒有太多地方符合這個限制。基本上是集合字面量,也許還有位置參數列表。(我為後者寫了一份 提案,但它相當複雜,所以我們沒有這樣做,至少現在沒有。)

這就留下了集合字面量的正文內部。基於此,我將這些稱為「元素」。元素是一段程式碼,當計算時會產生零個或多個值。然後,這些值會插入到它們出現的周圍上下文中。因此,在列表中,它們成為新列表中的一系列元素。在映射中,它們成為一系列鍵/值對。您明白了。

因此,集合字面量的正文可以包含表達式或元素。允許兩個類別有點令人困惑,但幸運的是,我們可以透過說集合只包含元素來簡化這一點。然後我們將「表達式元素」定義為包含單個表達式的元素。該元素總是產生一個結果 - 表達式的值。有點像表達式語句的元素等價物。

好的,這就是我們現在的處境。我們已經將集合更改為包含元素而不是表達式,並定義了兩種元素,展開運算符和表達式元素。要計算集合字面量,您可以遍歷元素,計算每個元素,並將所有結果物件連接起來(或在集合的情況下進行聯合)。

考慮到這個模型,我們可以瀏覽其他兩個新功能:

集合 If

編寫任何 Flutter 程式碼,您很快就會遇到這樣的情況:您想要建構的 Widget 樹會根據某些條件而變化。假設我們有:

後來,我們決定在 Android 上使用不同的搜尋按鈕。您已經可以使用條件表達式執行此操作:

這可以,但我從未覺得 C 的條件運算符很容易閱讀。但是,通常情況下,您不想根據條件 交換 Widget,您只想 省略 一個 Widget。假設您根本不想在 Android 上顯示搜尋框。今天,Flutter 使用者傾向於使用以下兩種模式之一。這是一種:

它強制您重新排列整個函數,方法是在使用子列表之前將其提取出來並以指令式的方式建構它。另一種模式如下:

這使用了一個條件表達式,它有時會產生一個 null,然後從結果列表中過濾掉該 null。向提出這個想法的人致敬,但這不是任何使用者為了完成如此簡單的任務而應該編寫的內容。簡單的問題應該有簡單的解決方案,對程式的小概念變化不應該需要大的文字變化。

以下是新的內容。對於我們想要在 Android 上使用不同按鈕的第一個範例,它看起來像這樣:

它使用熟悉的 ifelse 語法,而不是 ?:。這實際上與現有的條件表達式只差幾個標記,因此它似乎沒有發揮作用。更有趣的情況是當我們想在 Android 上 省略 按鈕時:

請注意,沒有 else 子句。這兩個範例看起來與 Ruby 等語言非常相似,在 Ruby 中,if 是一個表達式。但表達式必須始終計算結果為一個值,即使條件為 false。在 Ruby 中,在這種情況下,它隱式計算結果為 nil

但這 不是 您在這裡想要的。您不希望在子 Widget 列表中以 null 元素結束。這就是為什麼上面的條件表達式範例必須使用煩人的 where() 將其過濾掉。幸運的是,這在這裡不是問題。因為集合中的 if 不是 表達式。它是一個 元素

現在您明白為什麼我要帶您瀏覽所有關於展開運算符的東西了。元素為我們提供了基礎,讓我們可以使用 if 語法從集合中完全省略一個元素。如果條件為 true 或存在 else 情況,則 if 元素會產生單個值。如果條件為 false 且沒有 else 子句,則它根本不產生任何值。

我認為這種行為非常有用,但如果您查看程式碼並期望 if 的行為像一個簡單的表達式,也會令人困惑。

集合 For

前一個功能採用了現有的 Dart 語句語法,並將其重新用於在集合的上下文中執行一些有用的操作。還有其他值得採用的語句形式嗎?

大多數語句形式都沒有意義。在其中插入 return 語句不會做任何有用的事情,因為它只會退出周圍的函數。while 也不是很有用。為了退出 while 迴圈,主體通常包含 breakreturn 或某種副作用,例如指派。但這意味著主體包含 語句,這不是我們想要的。目標是使集合更具 表達性,而不是更具 指令性

我仔細研究了現有程式碼中的許多集合字面量,尋找我認為可以透過新語法改進的模式。到目前為止,最主要的是 if。但我看到一些地方我認為可以透過 for 改進。以下是我找到的一些程式碼的略微清理後的範例:

所有 command.add() 的東西都感覺不必要地指令式。如果我們允許在集合字面量中使用 for 迴圈,則變為:

組合元素

鑒於我們已經加入了展開運算符,for 語法似乎並沒有那麼引人注目。您不能使用展開運算符與可迭代物件上的高階方法的某種組合來完成同樣的事情嗎?是的,您可以。您會得到如下內容:

這確實有效,並且適用於某些使用案例。讓我們考慮一個稍微不同的例子。假設我們只想在存在相應的 JSON 檔案時包含一個入口點。這意味著我們沒有進行簡單的 1-1 對應。僅使用展開運算符,我們會得到如下內容:

這也有效。但將簡單的「如果檔案存在,則執行此操作」邏輯轉換為基於流的高階函數樣式變得越來越困難。總有一些 map()where()transform() 的組合可以完成這項工作,但感覺就像將俳句翻譯成逆波蘭表示法。

有一個更乾淨的解決方案,它涉及一個關鍵問題:這些新的 iffor 元素的 主體 是什麼?在我目前向您展示的範例中,它始終是一個表達式。但沒有必要僅限於此。相反,我們允許任何元素都放在那裡。換句話說,所有這三個新功能都可以自由組合。上面的程式碼可以表示為:

for 內部的一個簡單的 if,就像您在編寫指令式語句時所做的一樣。組合元素的語義非常明顯:

  • 如果條件為 true,則 if 元素會產生其 then 子句產生的所有值,否則會產生「else」子句的所有元素。如果沒有「else」,則不產生任何元素。
  • 每次執行主體時,for 元素都會產生其主體元素產生的所有值的連接。

這可以啟用一些我認為很酷的模式。您遇到的顯顯問題是想根據單個條件包含或省略 多個 值。因此,假設在我們之前的範例中,我們想跳過 Android 上的標題和搜尋框。您可以透過將展開運算符包裝在 if 中來執行此操作:

這裡需要展開運算符來解壓縮內部列表。否則,當不在 Android 上時,您將包含整個內部列表作為單個值。(我們考慮過在這種情況下根據靜態類型隱式展平,但當您考慮 List<Object> 之類的東西應該如何表現時,這就變得非常可疑了。)

您可以將展開列表字面量視為語句的元素等價物 - 它讓您可以在只預期一個元素的地方放置多個元素。(如果您熟悉 逗號運算符,那基本上就是表達式的類似形式。到處都是類比。)

在空集合字面量中使用 forif 可以讓您獲得與其他語言(如 Python)支援的特殊「列表推導式」語法不太一樣的語法:

您甚至可以巢狀 for

這會建構一個列表,其中包含給定 horvert 矩形中所有點的 笛卡爾積

此外,這些新功能可以跨集合類型組合。我一直使用列表作為範例,因為它們最常出現,但所有這些功能也適用於映射和集合。唯一的區別是,對於集合,重複項會被隱式丟棄。在映射中,基本元素不是原始表達式元素,而是鍵/值對。例如,這:

可以重寫為:

我們對所有這些功能的一個真正擔憂是,我們基本上是在為您提供新的方式來表達您今天已經可以表達的東西。這是有代價的,因為這意味著使用者需要花費腦力來 決定使用哪個功能,並且在閱讀其他人的程式碼時,他們可能會花時間質疑為什麼選擇一個選項而不是另一個選項。只需學習更多功能,語言就更大了。

我們花了很長時間 為此而苦惱。有時候,什麼都不做是最好的設計。簡單性非常有價值,而且您很少有機會讓一種語言隨著時間的推移變得更簡單。但是,在查看了大量程式碼並與一位令人愉快的 UX 研究人員合作進行了一項研究後,我們相當有信心這些功能足夠輕量級且有用,可以發揮其作用。

與任何語言更改一樣,在使用者使用之前,您永遠不知道它會如何運作。這些功能將在即將發佈的 Dart 2.3 版本中提供,我非常期待看到您如何使用它們。


讓 Dart 成為更好的 UI 語言 最初發佈在 Medium 的 Dart 上,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣佈 Dart 2.2:更快的原生程式碼,支援集合字面量

今天,我們宣佈推出 Dart 2.2 SDK 的穩定版本,這是 Dart 2 的一個增量更新,它提供了改進的 提前 (AOT) 編譯 原生程式碼的效能和一個新的集合字面量語言功能。

針對 Flutter 開發人員的 Dart 效能改進

我們繼續努力使 AOT 編譯的程式碼(例如 Flutter 應用程式)更快。在 Dart 2.1 中,我們減少了類型檢查的開銷,大大降低了 AOT 編譯程式碼和使用 JIT(即時)編譯在 VM 中執行的程式碼的類型檢查成本。

在 Dart 2.2 中,我們特別關注 AOT 編譯程式碼的效能,在微基準測試中將 AOT 效能提高了 11-16%(代價是程式碼大小增加了約 1%)。這一改進是幾個季度以來努力減少靜態調用開銷的結果。我們最佳化的 AOT 程式碼現在能夠使用 PC 相對調用(即使用 程式計數器)直接調用目標;之前我們必須對物件池進行多次查找才能確定目標地址。當程式碼包含大量建構函式和靜態方法調用時,這些最佳化特別有用,例如建立大量 Widget 的 Flutter 使用者介面程式碼。

Dart 2.2 集合字面量語言功能

Dart 的核心函式庫 (dart:core) 包含許多集合類別,用於對物件的 映射列表集合 進行建模。映射是鍵值對的集合。列表是有序的值序列,其中每個值可以透過索引存取,並且可以出現多次。集合是無序的值集合,其中每個值只能出現一次,並且可以有效地檢查集合中是否存在值。

Dart 集合通常使用編譯時常數初始化,因此 Dart 有一種方便的字面量語法來表達這種初始化。在 Dart 中,可以使用以下程式碼初始化列表:

1
var list = ['USD', 'EUR', 'JPY'];

以前,Dart 僅支援列表和映射的字面量語法,因此集合的初始化很麻煩,因為我們必須透過列表進行初始化:

1
var currencies = new Set.from(['USD', 'EUR', 'JPY']);

這段程式碼不僅不方便且效率低下;缺乏字面量支援會導致貨幣無法成為編譯時常數。透過 Dart 2.2 將字面量擴展到支援集合,我們可以使用一種方便的新語法初始化集合並使其成為常數:

1
const currencies = {'USD', 'EUR', 'JPY'};

有關 Flutter 團隊如何開始應用集合字面量的真實範例,請參閱 PR #27811。有關如何使用集合字面量的更多一般詳細資訊,請參閱 更新的 Dart 2.2 語言導覽

使用 Dart 2 通用前端 (CFE) 建立新的語言功能

Dart 語言實作(Flutter 使用的 Dart VM、dart2js 編譯器和 Dart 開發編譯器 (dartdevc))共用一個通用前端。Dart 通用前端(或 CFE)會解析 Dart 程式碼、執行類型推斷,並將 Dart 轉換為後端實作接受為輸入的低階中間語言。

集合字面量功能是一個語言結構的例子,由於 CFE,我們能夠快速開發它。解析集合字面量和執行類型推斷的程式碼在 CFE 中為所有 Dart 後端實作一次。此外,我們建立了一個僅限前端的過渡實作,後端最初可以使用它。在過渡實作中,上述非 const 版本的貨幣集合字面量在編譯期間被轉換為等效的:

1
2
3
4
var currencies = new Set<String>();
currencies.add('USD');
currencies.add('EUR');
currencies.add('JPY');

const 集合字面量的過渡實作是不同的,因為 const 集合不能以片段的形式逐步建立。相反,我們根據一個私有的不可修改集合類別來實作它,該類別包裝一個 const 映射,其中集合元素是映射的鍵:

1
const currencies = _UnmodifiableSet({'USD': true, 'EUR': true, 'JPY': true});

不可修改集合類別根據包裝的映射實作 Set 介面中的方法。

總體而言,我們最初能夠將集合字面量實作為僅限 CFE 的功能。後端最初可以使用 CFE 實作,然後獨立於功能的初始啟動開發自己的原生支援。這允許後端推遲其原生支援,直到更好地理解此功能的效能方面。

Dart 2.2 語言規範

Dart 2 是 Dart 的一次 重大升級,我們花了一段時間才將正式的 Dart 語言規範 更新以符合我們所做的所有變更。我們終於完成了這項工作,並且規範已更新至 Dart 2.2。我們還將語言規範來源移至新的 語言儲存庫,並加入了持續整合,以確保在我們為未來版本的 Dart 語言發展規範時,以 PDF 格式生成滾動草案規範。2.2 和滾動 Dart 2.x 規範都可以從 Dart 規範頁面 獲得。

獲取 Dart 2.2

Dart SDK 2.2 現在可以從 Dart 首頁下載。如果您是 Flutter 開發人員,則 Dart 2.2 已包含在內。(注意:目前的 Flutter master 和 dev channel 將報告 Dart 2.2。今天的 Flutter 1.2 穩定版本將報告 Dart 2.1.2;這與 Dart 2.2 具有相同的功能)。

目前就這些。我們希望您喜歡 Dart 2.2!


宣佈 Dart 2.2:更快的原生程式碼,支援集合字面量 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣佈 Dart 2.1:改進效能和易用性

今天,我們宣佈推出 Dart 2.1 穩定版,這是 Dart 2 的一個更新,它提供了更小的程式碼大小、更快的類型檢查、更好的類型錯誤易用性,以及新的語言特性,以提高構建使用者體驗時的生產力。

Dart 2 正式發佈

Dart 2 是 Dart 平台的一次重大升級。它完成了從早期網頁根源到主流程式語言的轉變,適用於跨行動裝置和網頁快速開發豐富的使用者體驗。Dart 作為一種語言,在提供虛擬機器 (VM) 和編譯成原生機器碼和 JavaScript 方面都有些獨特。這支援了各種平台的不同需求,無論是開發還是生產。

作為向 Dart 2 過渡的一部分,我們加入了健全的類型系統,以支援大型團隊構建複雜的應用程式;新的編譯器支援生成針對行動裝置最佳化的原生程式碼;以及完全重新設計的網頁平台工具。Dart 為 Flutter 提供動力,Flutter 是一個快速發展的工具包,用於從單一程式碼庫為 iOS 和 Android 構建美觀的原生體驗;它也是 Google 一些最大專案(例如 Google Ads)使用的語言。

自 8 月份 Dart 2 發佈 以來,我們一直致力於讓整個生態系統都使用 Dart 2。我們將 dartlang.org 網站切換到使用 Dart 2 範例,並提供用於遷移 Dart 1.x 套件的工具和文件。我們理解 Dart 2 中的重大變更涉及到現有 Dart 開發人員的遷移工作,我們非常感謝那些幫助我們為未來建立新基礎的人。我們現在的重點轉向利用這些投資來改進效能和生產力。

Dart 2.1 語言支援 int 到 double 的轉換

新的 Flutter 開發人員經常在指定 padding、設定字體大小等時遇到如下分析錯誤:

從系統的角度來看,這些錯誤是有道理的:API 需要一種類型 (double),而開發人員指定了不同類型 (int) 的值。但是,從易用性的角度來看,這似乎有點愚蠢:從 int 到 double 有一個簡單的轉換,為什麼不直接這樣做呢?Dart 2.1 現在可以推斷 int 何時可以靜默地評估為 double

Dart 2.1 語言支援 Mixins

我們還改進了 Dart 對 Mixins 的支援。如果您以前沒有接觸過 Dart Mixins,那麼值得閱讀 Romain Rastel 撰寫的這篇關於 Dart Mixins 的精彩介紹。Dart 2.1 引入了一種新的 Mixins 語法,其中包含一個新的 mixin 關鍵字,您可以使用它來定義只能用作 Mixins 的類別。我們還加入了支援,以便 Mixins 可以擴展其他類別(以前它們只能擴展 Object),並且可以在其父類別中調用方法。

擴展非 Object 類別的一個例子來自 Flutter 的動畫 API,其中 SingleTickerProviderStateMixin(一個提供用於將動畫前進一幀的 ticker 的框架類別)宣告了一個實作通用 TickerProvider 介面的 Mixin。動畫僅適用於有狀態的 Widget(因為動畫中的位置被視為狀態)。新的 Mixin 支援允許我們透過宣告只有擴展 Flutter State 類別的類別才能使用 Mixin 來表達這一點:

Dart 2.1 編譯時類型檢查

Dart 2 的健全類型系統可以在開發過程中保護您,當您違反類型指定的契約時,它會告訴您。例如,假設您正在為 Flutter 建立一個狀態類別。這個類別應該擴展框架 State 類別。State 類別應該傳遞包含其狀態的 StatefulWidget。

現在,如果您犯了一個程式設計錯誤,並且例如傳遞了 StatelessWidget 的後代而不是 StatefulWidget,類型資訊可以使工具捕獲錯誤並立即向您顯示警告:

這些編輯時類型檢查已在 Dart 2.0 中加入(由 Dart Analyzer 提供支援)。但是,還有另一個地方您可能會期望進行這些類型檢查,即在編譯時,當您進行 Flutter 發佈構建時。這些檢查在 Dart 2.0 中是不完整的,這可能會導致易用性問題,錯誤的原始程式碼可以在不產生任何錯誤的情況下進行編譯。在 Dart 2.1 中,這些檢查是完整的,並且 Analyzer 和 Dart 編譯器包含相同的檢查。

Dart 2.1 針對 Flutter 開發人員的效能改進

對於在我們的 VM 上執行的 AOT 編譯程式碼(例如 Flutter 應用程式),Dart 2 通常比 Dart 1.x 更快。但是,在一些邊緣情況下,新類型系統 加入的全面檢查會導致 20-40% 的額外開銷。在 Dart 2.1 中,我們大幅降低了類型檢查的成本,無論是對於 AOT 編譯程式碼,還是對於使用 JIT(即時)編譯在 VM 中執行的程式碼。

一個由此受益的具體案例是我們的開發人員工具(使用 VM 執行)。例如,對一個大型基準測試應用程式(基於 Flutter Gallery 的多個串聯副本)執行程式碼分析過去需要約 41 秒;現在只需要約 25 秒。

Dart 2.1 針對網頁開發人員的效能改進

我們還改進了在網頁上執行的 Dart 程式碼的程式碼大小和編譯時間。我們專注於 dart2js 的輸出大小,並且看到了良好的結果,例如,對於我們的一個範例,縮小後的輸出大小減少了 17%,編譯時間提高了 15%

核心 SDK 之外的變更

除了 Dart SDK 中的上述變更之外,我們還在核心 SDK 之外進行了一些令人興奮的變更。

Protocol Buffers(簡稱 protobuf)是一種平台中立的序列化結構化資料的機制。它們在 Google 內部廣泛使用,並且在 Google 之外也得到了廣泛的採用,例如作為 gRPC 的一部分。Dart 現在是一種官方支援的 protobuf 語言,我們在 核心 protocol buffers 網站 上加入了詳細的文件,其中包含教學參考文件。

您可能聽說過 knative,這是一個基於 Kubernetes 的平台,用於支援構建、部署和管理無伺服器工作負載。我們最近調查了在 knative 上支援 Dart 程式碼的服務,並建立了一個小範例。對於大多數 Dart 應用程式開發人員來說,這可能太低階了,但我們從平台的角度來看,覺得這非常令人興奮,並預計這將是改進 Google Cloud 中 Dart 程式碼服務支援的關鍵基石,例如為 Flutter 應用程式建立後端。

獲取 Dart 2.1

Dart SDK 2.1 可以從 Dart 首頁下載。如果您是 Flutter 開發人員,Dart 2.1 將作為即將推出的 Flutter 1.0 版本的一部分包含在內。

我們還更新了 DartPad 以執行 Dart 2.1,並加入了 int-to-doublemixins 的範例。

接下來是什麼

我們預計將發佈多個 2.x 版本,以利用 Dart 2 平台基礎。我們將根據框架合作夥伴和應用程式開發人員的需求來制定這些版本。在 2019 年,我們預計將研究以下領域:

  • Dart 語言的持續發展:對於 Dart 2.2,我們正在研究許多變更,例如對 const 表達式 的改進和對新的 Set 字面量 的支援。除了 2.2 之外,我們還在研究對 Unicode 字串的支援,並且正在研究是否可以提供更好的空值安全性。
  • 進一步改進以最佳化 Dart 作為建立使用者介面的語言:Dart 2 以其新的類型系統和可選的 new 開始了這段旅程,Dart 2.1 加入 int 到 double 值推斷,我們目前正在研究許多潛在的改進,例如支援 Widget 列表中的條件,將 物件集合 擴展到其他集合,甚至可能移除使用分號終止語句的需要
  • 更好的效能:我們將繼續減小編譯後 Dart 程式碼的大小和提高效能,包括更好地支援使用多核處理器,進一步減小程式碼大小以改善下載和啟動時間,以及可能更好地控制大小或運行時效能是否最重要。

要了解更多關於 Dart 和 Flutter 的資訊,我們誠邀您加入我們參加 Flutter Live,這是一個線上活動,將於 12 月 4 日舉行,並將在網路上進行直播。我們很興奮屆時能與您分享更多關於路線圖的資訊。

目前就這些。我們希望您喜歡 Dart 2.1!


宣佈 Dart 2.1:改進效能和易用性 最初發佈於 Medium 上的 Dart,人們在那裡透過突出顯示和回應這個故事來繼續對話。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

Future 與 Future,有什麼區別?

在 Dart 2 的諸多升級中(除了更好的靜態檢查、運行時類型安全、可選的 new/const、核心函式庫改進等等),一個值得一提的改進是 void 的正式化,使其更加實用且更不容易出錯。這在異步程式設計中尤其清晰,您可以為異步函數編寫 Future<void>,這些函數在工作完成時不返回結果。在此之前,您可能使用了 Future<Null>,所以我們經常被問到的問題是:Future<void>Future<Null> 之間有什麼區別?我應該使用哪一個,以及何時使用?

由於我的目標是提供有用的資訊,我將從 TLDR 開始。

TL;DR:99.99% 的情況下,您應該使用 void 類型。

我還建議您現在就在您的專案中啟用兩個與 void 相關的lint 規則

  • prefer_void_to_null: 幫助您改掉輸入幾乎過時的 Null 的習慣。
  • void_checks: 為您提供可能更直觀的 void 語義,即使它們對於安全程式碼並非絕對必要。

本文的其餘部分並非輕鬆閱讀。它結合了歷史、邊緣案例和類型理論。我接下來要寫的就像試圖解釋單子,但我會盡力而為,希望您能跟上。

快速 Future 小知識

為了展示閱讀本文的價值,我為您提出了一個小知識問題。

假設我們將自己限制在兩個 Future - 一個 Future<User> 和一個 Future<Null>。以下程式碼行會做什麼?

1
2
// 這段程式碼在您的編輯器中會失敗嗎?
Future<User> f1 = Future<Null>.value(null);
1
2
// 這段程式碼在運行時會失敗嗎?
Future<Null> f2 = Future<User>.value(0);

如果我們改用 await 呢?

1
2
User u = await Future<Null>.value(null);
Null n = await Future<User>.value(0);

暫停以產生戲劇性效果

答案是,除非您禁用了隱式向下轉型,否則這四行程式碼都不會在您的編輯器中顯示任何錯誤。沒錯,使用 Future<Null> 並假裝它是它不是的東西,例如 Future<User>,是 100% 合法的。Future<void> 就不是這樣!

同樣不直觀的是,第二行和第四行在運行時會失敗(以這種方式丟棄 Future 的結果是不「安全」的),而其他行則靜默地成功。

這些行為應該會讓您感到不安。但別擔心!您已經知道解決方案:使用 Future<void>。我將分析為什麼這種不直觀的行為會這樣工作。

void 現在是如何工作的

我一直在思考本文的最佳順序。這裡有很多概念需要解釋。但是,我會盡量按照從最有用的資訊到最不有用的資訊的順序進行。因此,由於 void 在 Dart 2 中比 Null 更有用,因此從 Dart 2 void 語義開始是有意義的。

Dart 中 void 背後的基本思想源於 void 值不應使用的目標。

1
2
void f() {}
print(f()); // 錯誤!

希望這就是您期望發生的!到目前為止,一切都很好。

值得注意的是,實現這個目標並且將 void 作為一個相當普通的類型公開會有點奇怪:

1
2
3
4
f(void argument) {
print(argument); // 錯誤!
// 我們可以接收參數,但我們不能使用它!
}

這就是 Dart 2 中我們新的「廣義 void」開始變得酷炫和強大的地方……儘管有時會讓人感到困惑。

我向您提出一個問題……我們可以傳遞什麼類型的值作為 f 的參數?

1
2
f(void argument) { … }
f(x); // x 是什麼?

答案是……任何東西!因為我們不允許您在函數 f 中使用 x 的值,所以我們可以自信地說 x 可以取的任何值都不會導致任何運行時錯誤。

1
2
3
4
f(1); // 與以下程式碼沒有區別
f("foo"); // 與以下程式碼沒有區別
f([1, 2, 3]); // 與以下程式碼沒有區別
// ...

如果我們根據 void 是一個不會被使用的值的思想推導出 void 的最佳語義,這就是我們得到的結果。這意味著,它是一個可以用任何東西填充的值。它就像一個真空:一個沒有輸出的輸入。

現在,在某些情況下,某些東西在運行時總是沒有錯誤,但仍然應該導致靜態錯誤。這是一個 lint 的絕佳案例!事實上,這就是 void_checks lint 背後的思想。它會尋找您將 null 以外的任何東西傳遞到 void 位置的地方,我鼓勵團隊啟用它。它並非健全性所必需的,但是將 null 以外的任何東西傳遞到 void 位置仍然可能是一個意外,lint 會為您標記它。

由於所有這些都基於基礎類型理論,因此 void 可以與 Dart 的每個部分都很好地配合使用,即使在利用類型推斷的 Future<void> 上下文中的延續也是如此:

1
2
3
4
Future<void>().then((x) {
print(x); // 錯誤!x 的類型為「void」,所以您不能列印它!
// 這就是我們想要的!
});
1
2
// 是的,這也是一個錯誤:
Foo f = await voidFuture();

此時,普通開發人員可能已經了解了有效使用 void 所需知道的一切。

然而,如果您仍在繼續閱讀,還有一些更有趣的東西。

即使啟用了 void_checks,也不能保證 void 位置包含 null。以下列覆寫為例:

1
2
3
4
5
6
7
8
class A {
void f(Object o) {}
}

class B extends A {
@override
Object f(Object o) => o;
}

我們不想讓此覆寫非法,因為它是安全的、有用的,並且與 Dart 1 相比是一個重大變更。因此,我們不得不接受 void 位置可能包含任何值。我們也不能「優化掉」A.f() 返回的值,因為在運行時它可能是一個 B!

相反,我們有一個聰明的選擇,即讓 void 成為 Object 的姊妹類型。畢竟,它可以包含任何值,並且所有值都是物件。這不是我們設計的東西,它只是現實。認識到現實,我們就可以利用它。

透過使 void 成為 Object 的同級,我們沒有任何要求 void 值完全未使用。我們盡力讓 void 保持自身,但為了向後兼容性,我們特意在一些地方放鬆了這些限制:

1
2
dynamic f() => voidFn(); // 這是合法的
voidFn() as dynamic; // 這是合法的

這些是對於 Object 合法的特殊情況,為了使 Dart 2 更順利地推出,我們也讓它對 void 合法。

使 void 成為 Object 的同級也意味著 void 可以用作類型參數,而不會使編譯後的輸出膨脹(對於 C++ 使用者來說,沒有「模板特化」)。這種減少有助於保持 Web 和 Flutter 應用程式的小巧,但代价是允許以下操作:

1
<void>[1, 2, 3].toString(); // 這是合法的,並列印 [1, 2, 3]

最後,將參數類型指定為 void 可能看起來沒有用,尤其是因為它是 Object 的一種形式。然而,void 值可以傳遞到 void 位置

1
2
f(void x) { … }
f(voidFn()); // 這是合法的

這對於排序很有用,例如在模擬返回 void 的方法時,Mockito 會使用它。(將上面的程式碼中的 f 替換為 when 以獲得更接近的近似值。)

總而言之:

  • void 類型是 Object 的同級。
  • 幾乎總是,void 物件不能被使用。
  • 標記為 void 的東西實際上可以是任何東西。
  • 任何東西都可以「丟棄」到標記為 void 的位置,而 lint void_checks 限制了這種行為。
  • void 值可以傳遞到其他 void 位置。

底部類型

在我開始討論 Null 之前,我們必須先討論「底部」類型。

這是一個類型理論中自然發生的類型,它有一個簡短的學術定義和一些實際應用。

如果您因為它太奇怪了而停止閱讀本節,那就是我最初 TLDR 的有力證據。除非您希望您的程式碼同樣奇怪,否則您可能需要 void。現在讓我們探索這個奇怪的兔子洞,看看它有多深,好嗎?

底部類型是所有類型的子類型。用更簡單的面向對象術語來說,這意味著它是一個人。還是一輛車。還是一種動物。還有來自每個程式的所有其他類型。

如果這聽起來很荒謬,那是因為它確實如此。我喜歡將它視為一個「佔位符」類型。但是「荒謬的」或「虛構的」類型也是一個合理的稱呼。然而,它被稱為底部類型,因為它是類型層次結構的底部,在電腦科學中,它是顛倒的。¯_(ツ)_/¯。正式地,它可以用符號 ⊥ 表示,您可能也認識到它是「假」的符號。

如果您試圖想像一個既是人又是又是動物的值,您將無法想到任何東西。令人驚訝的是,這就是 ⊥ 的實際用途的來源!

如果我編寫一個永不返回的函數會怎樣?有兩種簡單的方法可以做到這一點:

1
2
3
loopForever() {
while(true); // 第一種方法
}
1
2
3
alwaysThrow() {
throw Exception(); // 第二種方法
}

這兩個函數的最佳返回類型是什麼?

這取決於情況。因為函數永不返回,所以返回類型並不重要。您可以使用任何類型 - 即使是荒謬的底部類型,它可以在各種語言中以各種方式使用。

C++ 將其稱為 noreturn。Rust 有 !,Scala 有 Nothing。但我最喜歡的例子來自 Haskell,它有一個非常常用的 undefined 函數,它會中止程式。它返回,您猜對了,底部類型。

如果我們假設 Dart 中有一個 undefined 函數,就像 Haskell 中的那樣,它在被調用時只拋出一個異常並返回 ⊥,那麼它看起來像這樣:

1
Foo foo = cond ? val : undefined();

在示例用法行中,當 cond 為真時,程式可以安全地運行並儲存 val。而當 cond 為假時,程式將在 undefined() 期間拋出異常。將 undefined() 的結果「儲存」到 foo 中是安全的,無論 foo 的類型如何,因為該儲存永遠不會真正發生!

undefined() 這裡沒有返回任何東西。但這裡的教訓不是我們可以讓 foo 為空……而是底部類型像一個空的承諾一樣是空的。它比空還空。它永遠不會發生。

我必須小心地說明的一點是,您可以在實踐中從這些函數中返回 void,並且根據用法,通常應該返回 void。通常,像 return loopForever() 這樣的程式碼更有可能是一個錯誤,而不是一個有用的模式。然而,您可以自行選擇。

底部類型也適用於唯讀空列表。在 Dart 中,List 是協變的,因此允許將 List<int> 用作 List<Object>。如果您試圖將 String 放入該 List<Object> 中,運行時檢查會為您捕獲該錯誤並拋出錯誤。

這意味著,如果我們建立一個 ⊥ 列表,我們就無法在其中放入任何東西,但我們可以將其視為任何東西的列表:

1
2
3
4
List<int> intList = <⊥>[];
for (int i in intList) {
print(i * 2); // 有效,因為這永遠不會發生
}

當您查看所謂的底部類型的「逆變」位置時,還會有更多有趣的案例。

假設我們定義了一個具有 ⊥ 參數的函數:

1
void f(⊥ x) {}

這幾乎與 undefined() 示例相反。我們不是聲明了一個永不返回的函數,而是聲明了一個無法被調用的函數!沒有實際值可以賦值給該參數 x。您不能傳入 Person,因為它也不是 Car,並且您不能傳入 Car,因為它也不是 Person。您唯一可以傳入的就是荒謬的類型本身:

1
f(undefined());

但是正如我們之前所討論的,undefined() 永不返回,所以在這種情況下,f() 仍然永遠不會被實際調用!

將參數類型指定為 ⊥ 可能看起來沒有用,但它具有深奧的價值,因為所有可以被調用的函數都是不能被調用的函數的子類型。(想一想:一個可以被調用的函數不一定要被調用。如果一個函數沒有被調用,它就不會產生運行時錯誤。)

如果您還在繼續閱讀,請深呼吸,並拍拍自己的背。

具體來說,將任何 Function(X) 轉換為 Function(⊥) 是安全的,對於任何 X 都是如此。這比使用 Dart 的包羅萬象的 Function 類型更好,因為它更具體。

例如,您可以將任何一元函數儲存在一個欄位中,並動態調用它,以繞過靜態錯誤,如果您犯了錯誤,則用運行時錯誤代替它們:

1
2
Function(⊥) f;
f = (int x) => x + 1;
1
2
// 123 作為 f 的參數的有效性在運行時檢查
(f as dynamic)(123);

這是一個在緊要關頭幫助您的小技巧。

現在我們可以討論 Null 了。

Dart 2 中的 Null

概念上的「底部」類型(所有類型的子類型)存在於 Dart 中,但這並不是全部。這樣的值也存在於 Dart 2 中!在 Dart 中,我們稱它為 Null,而該值就是 - 您猜對了 - null。

由於我可以使任何東西都為 null(/Null),所以在 Dart 中荒謬的類型並不那麼荒謬。這使得它有點複雜。

注意:值 null 當然不是 Car,也不是 Person。我們確實收到了對 Dart 中非空類型的請求。因此,如果我們確實對 Dart 進行了這樣的更改,我們將需要一個新的底部類型,可能命名為 Nothing 之類的名稱,那時它將是一個更真實的底部類型。

Null 不僅具有與底部類型相同的所有荒謬用法,而且還有一個逃生出口。如果您真的需要從一個不能返回的函數中返回,或者調用一個不能被調用的函數,我們會給您一個出路!您可以將 null 傳遞進去!老實說,如果您從整體上看,我們不這樣做會有點不公平。

但是,對於一個原本簡單的聲明,它確實給出了很多警告:

1
Null nothing() => null;

Null 是 foo() 最特定的類型,所以它是一個合乎邏輯的選擇,也是一個良好的起點。如果您在其上調用不存在的方法,分析器也會善意地警告您:

1
nothing().x; // 錯誤!Null 上沒有成員 x

但这可能会造成安全的假象。

1
2
nothing().toString(); // 沒有錯誤:Null 定義了 toString()
Foo foo = nothing(); // 沒有錯誤:foo 將變為 null
1
2
3
// 對於 f(x) 中的所有參數類型都可接受,
// 如果 f(x) 需要非 null 值,則會出現運行時錯誤
f(nothing());

如果您的目標是讓 nothing() 成為

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

Dart 套件網站的探索功能提升

七月份時,我們談論了 Dart 套件網站 (http://pub.dartlang.org) 的變更,其中包括更好地支援分析套件的潛在問題。從那時起,我們一直專注於進一步改進在套件網站上探索內容的功能。

Dart 2不相容性

絕大多數最新的熱門套件都已遷移以支援 Dart 2,因此它們已準備好用於FlutterAngularDart 5 應用程式。然而,有些套件(大多數是未維護的舊套件)仍然不支援 Dart 2。為了確保您可以輕鬆地發現這些套件,我們現在在總覽和套件詳細資訊頁面中加入了 Dart 2 不相容標籤。

與 Dart 2 不相容的套件會清楚地標示為「Dart 2 不相容」

此外,我們預計在未來版本的套件網站中會歸檔/停止使用這些不相容的套件,因此如果您是不相容套件的作者,請考慮盡快遷移!

支援核心函式庫

Dart 附帶了一組豐富的核心(標準)函式庫。開發人員告訴我們,很難記住特定的工具函式是在核心函式庫中還是在套件中。為了幫助您找到所需的 API,pub.dartlang.org 上的搜尋現在不僅包含已發佈的套件,還包含核心函式庫。來自核心函式庫的搜尋結果在評分圈中帶有 sdk,以及註釋 Dart 核心函式庫。如果您點擊該項目,您將直接進入函式庫 API 文件。

SDK 核心函式庫中的搜尋結果會包含在結果中

套件更新 Feed

Pub 已經提供 Atom Feed 一段時間了;為了讓它更容易被發現,我們在網站頁尾加入了一個直接連結。

評分變更

我們對健康和維護的評分模型進行了一些簡化和調整。有關目前模型的概述,請參閱 pana README(pana 是提供評分的工具)。

未來,我們計畫透過直接在套件分析頁面上顯示每個報告的問題降低了整體評分的程度來提高透明度。

歡迎提供回饋

發現問題了嗎?有什麼好主意或建議嗎?請透過在我們的追蹤器中開立 issue 來告訴我們。另請注意,即使是套件網站本身也是在 pub-dartlang-dart 儲存庫 中作為開源程式碼製作的,非常歡迎貢獻和想法!


Dart 套件網站的探索功能提升 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

Dart 2:void 的遺產

Dart 2 中類似 void 類型宇宙的半精確描述

我在 StackOverflow、Gitter 甚至 Google 內部支援頻道上看到的最常被問到的問題之一是 Dart 2 中以下內建類型之間的差異:ObjectdynamicvoidNull

長話短說,Null(或其他語言中的 Bottom,即「無」)不應該在大部分真實使用者程式碼中使用,我懷疑在不久的將來我們會看到更多文章和 lint 來溫和地阻止使用。

其餘三個類型則不太清楚,因為在 Dart 2 中,任何東西在執行時都可以是 dynamicObjectvoid,僅根據 靜態 類型簽章而有所不同。因此,讓我們看看幾個 何時 應該使用哪種類型簽章的實際範例。

Object

Object 是 Dart 類別階層的根類別,Dart 中的每個其他類別都是 Object 的子類別——包括像 intdoublebool 這樣的「原始」類型。它保證了一些東西:一個 hashCode 屬性,一個 == 運算子,一個 toString 方法

實際上,我使用 Object 作為窮人的聯合類型——期望使用者在使用某個東西之前使用 is 運算子來確定它的真實類型。**我不使用 dynamic**,因為正如下一節所述,它會停用重要的靜態分析,並且更容易讓您進入無效狀態。

1
Object readProperty(String name) { ... }
1
2
3
4
5
6
7
8
void main() {
var age = readProperty('name');
if (age is int) {
print('I am $age years old');
} else if (age is String) {
print(age);
}
}

另一個選項是使用 Object 來宣告您 不關心 資料結構的內部類型是什麼,例如 List<Object> 可能表示「任何東西的列表」。例如,在編寫一個組合 List 中每個元素的 hashCode 的函式時,這就派上用場了:

1
int hashList(List<Object> elements) { ... }

Object 的一個很好的特性(與 dynamic 相比)是,如果您嘗試在其上調用一個不存在的方法,您將立即獲得分析和編譯器回饋。例如,以下程式碼會產生一個 靜態錯誤

1
2
3
4
void main() {
Object a = 5;
a.aMethodThatDoesNotExist();
}

然而,在實際應用中,Object 是相當(並且有意地)有限的。我希望 Dart 將獲得對方法重載的支援,這將允許我在真實程式碼中顯著減少 Object 類型的使用。

dynamic

我個人在 Dart 2 中 從不 使用 dynamic 類型。在我看來,它是 Object 和一個特殊指令的聯合,該指令告訴工具和編譯器 停用靜態分析檢查。也就是說,以下程式碼是合法的,並且只會在執行時出現錯誤(而不是靜態地!):

1
2
3
4
void main() {
dynamic x = 5;
x.aMethodThatDoesNotExist();
}

在 Dart 1 中,dynamic 無處不在,任何其他靜態類型都是為了 IDE 和靜態分析支援——但編譯器(和執行時)將所有東西都視為 dynamic。儘管如此,在 Dart 2 中仍然有一些不幸的「陷阱」可能會 意外地 建立一個動態類型的變數:

1
computeAge() => 5; // 返回類型是 dynamic
1
2
3
4
void main() {
var name; // 靜態類型是 dynamic
var animals = []; // 靜態和執行時類型是 List<dynamic>
}

更糟糕的是,dynamic 調用會丟失 Dart 2 中至關重要的類型資訊:

1
2
3
class User {
String name;
}
1
2
3
4
5
6
7
void main() {
var users = []; // 隱式地是 List<dynamic>,還記得嗎?
users.add(new User()..name = 'Matan');

// 執行時錯誤:List<dynamic> 不是 Iterable<String>
Iterable<String> names = users.map((u) => u.name);
}

發生此錯誤的原因是因為這裡的實際調用是:

1
users.map((dynamic u) => u.name);

…它沒有足夠的靜態類型資訊來產生 Iterable<String>。透過將 users 修正為正確的類型(並避免動態調用),一切正常:

1
2
3
4
5
6
7
void main() {
// 我們也可以寫成 `var users = <User>[
var users = [new User()..name = 'Matan'];

// OK!
Iterable<String> names = users.map((u) => u.name);
}

void

最後,我們有 void,這是 Dart 2 中最新的類型。在 Dart 1 中,void 只能用作函式的返回類型(例如 void main()),但在 Dart 2 中,它已被 泛化,並且可以在其他地方使用,例如 Future<void>

void 類型在語義上類似於 Object(它可以是任何東西),但有一些額外的限制——void 類型不能用於任何東西(即使是 ==hashCode),並且將某個東西賦值給 void 類型是無效的:

1
2
3
4
5
void foo() {}

void main() {
var bar = foo(); // 無效
}

實際應用中,我使用 void 來表示「任何東西,我不關心元素」,或者更常見的是表示「省略」,例如在 Future<void>Stream<void> 中:

1
2
/// 清除快取。
Future<void> purgeCache() { ... }

在上面的程式碼片段中,我不希望使用者嘗試使用提供的 Future 的返回值,因為它不相關。我見過使用 Future<Null> 來達到此目的的範例,這實際上是在 Future<void> 成為可能 之前 的一種解決方法。

例如,這在靜態上是正常的,但在執行時在 Dart 2 中是無效的:

1
2
3
4
5
6
7
8
9
import 'dart:async';

Future<String> _doAThing() async => 'Test';
Future<Null> doAThing() async => _doAThing();

void main() async {
// Future<String> 不是 FutureOr<Null> 類型的子類型
await doAThing();
}

…而使用 Future<void> 作為 doAThing() 是有效且正確的。

另一個例子可能是不帶任何事件資料的 Stream

1
2
/// 當使用者登出系統時觸發事件。
Stream<void> get onLogOut { ... }

另一個更實際的用途是實作一個具有您不會使用的泛型類型引數的類別。例如,實作流行的 訪問者模式,當 C(上下文)類型引數未使用時,我們可以透過傳遞 void 來忽略它:

1
2
3
4
5
6
7
8
abstract class Visitor<N, C> {
N visitNode(N node, [C context]);
}

class IdentityVisitor<N> extends Visitor<N, void> {
@override
N visitNode(N node, [_]) => node;
}

我希望這篇簡短的文章可以幫助您圍繞使用 Objectdynamicvoid 做出 API 決策。如果您有任何其他問題或想法,請留言!


Dart 2:void 的遺產 最初發佈於 Medium 上的 Dart,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣佈 Dart 2 穩定版和 Dart Web 平台

今天,我們宣佈 Dart 2 穩定版正式發佈,其中包括 Dart Web 平台的重寫,它提供了生產力、效能和可擴展性的獨特組合。

Flutter 開發者已經享受 Dart 2 的許多優點,因為 Flutter 幾個月來一直在捆綁 Dart 2 SDK 的預發佈版本。然而,由於框架和語言之間的緊密耦合,Dart 2 的穩定版是 Flutter 穩定版發佈的另一個重要里程碑。如果您還沒有安裝 Flutter,請由此開始

隨著 Dart 2 的發佈,Web 開發者現在可以利用相同的語言、函式庫和工具以及許多 Web 專用的增強功能。Web 開發者應從開始使用頁面開始,以獲取有關安裝工具和建立第一個應用程式的說明。

Dart:越來越受歡迎

Dart 2 標誌著 Dart 作為一種主流程式語言的重生,專注於為行動和 Web 應用程式實現快速開發和出色的使用者體驗。我們希望讓構建客戶端應用程式的開發人員提高生產力,使用一種語言、框架和組件來減少樣板程式碼,讓他們專注於業務邏輯,並使用能夠及早識別錯誤、實現強大除錯功能並提供小型、快速執行時程式碼的工具。

在過去的一年中,Dart 經歷了巨大的成長。我們自己的分析表明,外部使用量成長了十倍。在上個季度,根據拉取請求的衡量標準,Dart 是 GitHub 上成長最快的語言之一StackOverflow 問題的成長也生動地展示了 Dart 的發展勢頭:

在內部,Dart 是 Google 用於 Web 應用程式開發的少數幾種語言之一,擁有來自數十個不同專案的數百萬行程式碼,包括 Google Ads、Google Shopping 和我們自己的內部基礎架構團隊。

事實上,您可能已經在使用 Dart 而沒有意識到:流行的 Sass 樣式表預處理器 最近用 Dart 重寫,使其更快、更可攜帶且更容易編寫。Sass 現在作為 Homebrew 和 Chocolatey 上的獨立可執行檔發佈,並在 npm 上編譯為純 JavaScript。Dart 可以輕鬆地在任何地方安裝應用程式,而無需外部相依性,並且可以融入使用 Dart 和不使用 Dart 的人的工作流程中。

Dart 2 專注於三個領域:強化和收緊語言、開發我們對 Web 和行動框架的支援,以及將支援 Google 使用 Dart 的一些工具和組件帶到外部世界。本文的其餘部分將探討這些主題。

Dart 2:客戶端優化的語言

正如我們在二月 強調的Dart 類型系統健全的。這意味著在使用者執行您的應用程式之前(在分析和編譯期間)就會發現一大類問題。當我們將 Dart 擴展到 Google 內部極其龐大的應用程式時,類型系統在開發週期的早期捕獲了更多錯誤,從而提高了生產程式碼的品質。

使用 Dart 2,您無法進入表達式求值結果與表達式的靜態類型不符的狀態。這意味著您可以將程式碼庫擴展到數百萬行,處理大型重構專案,並自信地部署程式碼。

別擔心:健全性並不意味著大量的樣板程式碼。類型系統包括進階推斷,即使是泛型類型參數。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final _field = [3.14, 6.28];

void main() {
// 沒有提供類型參數,但推斷為 <int>
print([1, 2, 3].runtimeType); // List<int>

// 如果類型不匹配,則使用它們的共同基類型
// int (1) 和 double (3.14) 都是 `num`
print([1, 3.14].runtimeType); // List<num>

// 推斷超越了變數。
// 您也可以省略欄位上的類型。
print(_field.runtimeType); // List<double>
}

要了解更多關於 Dart 2 類型系統的資訊,請查看此頁面

如先前的部落格文章所述,Dart 2 還減少了幾個關鍵領域的語言儀式:例如,new 關鍵字現在是可選的,而 const 關鍵字在常數環境中是可選的。

最後,我們在幕後做了很多工作來統一各種支援工具,使用一個共同的前端,現在支援我們的編譯器和執行時。這確保了我們使用者的現在的一致性,並承諾在 Dart 持續發展的過程中提高新功能的速度和品質。

Web 上的 Dart:豐富而強大的框架

Web 應用程式從一開始就是 Dart 使命的核心。事實上,Google 的大多數 Dart 開發都是針對 Web 應用程式。其中最大的一個是 Google Ads,它為數十億美元的網路經濟提供動力。在將他們的程式碼遷移到 Dart 的過程中,他們將 UI 程式碼庫的大小減少了 40%,同時提高了開發人員的生產力和應用程式品質。

雖然核心 Dart SDK 提供了存取現代瀏覽器 API 的函式庫,但我們也支援一個強大的受 Angular 啟發的框架,用於構建複雜的 Web 應用程式。作為 Dart 2 的一部分發佈的 AngularDart 5 利用了 Dart 的健全類型系統和新的構建系統(如下所述)在開發過程中提供快速增量構建,並在您準備部署時提供更小的已編譯 JavaScript。

此版本的一個主要重點領域是改進在頁面加載時處理的程式碼量,顯著減少了網頁的「互動時間」。與 AngularDart 4 相比,許多應用程式的程式碼大小減少了一半以上。

為了展示 Dart Web 平台的改進,我們構建了一個基於 Dart 的 HackerNews 網站客戶端實作,作為一個漸進式 Web 應用程式;這與其他範例實作一起發佈在流行的 HNPWA 網站上。使用構建 Dart Web 應用程式的最佳實務,我們能夠提供功能齊全的體驗,在現代設備上在一秒鐘內即可完全互動,在中等行動設備上在緩慢的 3G 網路上在五秒鐘內即可完全互動。即使與針對較小型應用程式進行優化的輕量級 Web 框架相比,這也具有競爭力。

hnpwa.dartlang.org

我們的框架投資延伸到我們的核心組件,我們也更新了這些組件。您現在可以存取 100 個新的類別,包括許多日期、時間和選單 Material 組件。您可以在組件庫中瀏覽我們的所有組件。

Material 日期選擇器 - Web 應用程式可用的豐富組件之一

Dart 2 的彈性工具

使用 Dart 2,Dart 成為一流的編譯為 JavaScript 的語言,具有 Web 開發人員期望的開發週期和出色的執行時效能特徵。這是同時提供大多數以 JS 為目標的語言不提供的優點:健全的類型系統和出色的原生行動應用程式支援。

使用 Dart 2,我們的 Web 工具採用 100% JavaScript 開發模型,並使用兩個互補的 JavaScript 編譯器。開發編譯器 dartdevc 提供快速增量編譯,同時產生易於閱讀和除錯的 JavaScript。這讓我們的生產編譯器 dart2js 可以專注於為行動裝置的漸進式 Web 應用程式和桌面的複雜企業體驗產生高度優化的 JavaScript。這兩種編譯器都利用 Dart 的健全類型系統來優化它們的輸出。

我們的 Web 工具基於新的構建系統構建,該系統旨在快速、可除錯且可擴展。現在,諸如產生序列化程式碼、將 Sass 編譯為 CSS 以及將 Dart 編譯為 JavaScript 之類的任務都發生在一個工具鏈中,該工具鏈支援在您變更應用程式部分內容時進行快速增量更新。構建系統也被設計為支援 Web 以外的用途。例如:Flutter 開發人員正在使用它來產生 JSON 序列化程式碼

使用 Dart 2,我們還擁有一套擴展的開發人員工具。除了支援 Android Studio 和 JetBrains 套件工具(包括 IntelliJ IDEAWebStorm)之外,我們還支援 Visual Studio CodeDart Code 擴展。我們也有一個很棒的實驗暫存區:DartPad,它已針對 Dart 2 進行了完全更新。

最後,Dart SDK 還附帶了一套其他有用的工具:與我們的套件網站一起使用的套件管理器、靜態分析器、您可以從命令列或作為套件使用的 linter,以及用於Web 文件和符合 Dart 樣式指南程式碼格式化的工具。

了解更多

查看開始使用 FlutterDart Web 工具集的說明。

發行說明提供了自 Dart 1 以來進行的許多其他較小改進的詳細技術說明,其中一些是對舊 Dart 程式碼的重大變更。如果您遇到 Google 搜尋無法解決的問題,我們建議您從 StackOverflow開始。訂閱 Dart 公告郵件列表並在 Twitter 上關注我們以獲取更新。我們也希望在我們的社群中看到您,例如 Gitter聊天室和 r/dartlang subreddit

謝謝

Dart 已成為一種針對 Web 和行動開發進行優化的通用語言。我們已經開發 Dart 2 多年,在此期間,它觸及了我們生態系統的各個方面,並且需要遷移數百萬行程式碼和數百個套件。

我們感謝 DartFlutter社群以及數百名幫助我們完成這段旅程的 Google 工程師。沒有你們,我們不可能做到這一點!


宣佈 Dart 2 穩定版和 Dart Web 平台 最初發佈於 Medium 上的 Dart,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

準備迎接 Dart 2,並讓您的套件在 Pub 網站上看起來更棒!

我們發佈了 Dart 套件庫 Pub 的新版本!這次我們專注於為 Dart 2 穩定版發佈做準備。

將您的套件遷移到 Dart 2

我們預計 Dart 2 版本將在不久的將來升級到穩定版!因此,至關重要的是您要遷移您的程式碼——尤其是您已發佈的任何套件——以使其與 Dart 2 相容。這主要有三個方面:

  • 確保您的程式碼通過 Dart 2 分析(詳情)。我們建議您也要注意提示——例如,為棄用做準備。
  • 運行程式碼測試,以確保您的程式碼通過 Dart 2 運行程式碼檢查(詳情)。
  • 將套件的 SDK 約束上限更新為 <3.0.0(詳情)。

我們強烈建議所有套件作者完成這項工作,我們也將在接下來的幾天內努力完成我們擁有的套件。

Pub 網站上改進的分析介面

為了幫助您進行遷移,我們更新了 Pub 網站的分析標籤頁,使程式碼分析結果更易於閱讀。首先,在分析頁面的頂部,我們加入了進度條,讓您可以快速查看套件的狀態:

Pub 網站上顯示的套件評分

進度條下方是詳細的分析結果,按嚴重程度分組:錯誤(紅色圖示)、警告(黃色圖示)和建議問題(藍色圖示),如下圖所示:

Pub 網站上顯示的分析問題

更新的分析評分

我們還對分析評分進行了一些調整。如果符合以下任何條件,我們現在會降低套件的評分:

  • 套件未通過 Dart 2 分析。
  • SDK 約束上限不小於 <3.0.0。
  • API 參考生成(dartdoc)失敗。

對於我們的下一個 Pub 網站版本,我們預計將進一步完善評分,並加入一些關於評分計算方式的文件。

搜尋套件的 API 介面

搜尋是 Pub 網站上最受歡迎的功能——這並不奇怪。在過去的幾個季度中,我們在這一領域進行了許多改進,重點是加入平台過濾器和更好的排名。新版本擴展了搜尋功能,支援搜尋套件的 API 介面。除了目前對套件描述和 README.md 內容的搜尋索引外,搜尋現在還涵蓋了套件的所有公共 API 成員,以及這些成員的 API 文件!當您想要找到描述和 README.md 檔案中未提及的內容時,這非常棒。

例如,假設您正在尋找一個可以使用 ISO 4217 貨幣格式格式化字串的套件。在描述和 README.md 檔案中搜尋會給您一個結果,但包含 API 文件的搜尋會給您幾個結果

1
2
3
4
5
6
7
8
9
10
11
12
13
4217 的 10 個結果

intl
包含處理國際化/本地化訊息、日期和數字格式化和解析、雙向文字和其他國際化問題的程式碼。
v 0.15.2 • 更新時間:2017 年 10 月 19 日 FLUTTER WEB OTHER
API 結果:
intl/NumberFormat-class.html

flutter_billing
一個 flutter plugin,用於與 iOS 和 Android 上的計費系統進行通訊。
v 0.1.2 • 更新時間:2018 年 2 月 10 日 FLUTTER
API 結果:
flutter_billing/BillingProduct-class.html

總結…

Dart 2 穩定頻道版本即將發佈,我們已經對 Pub 網站進行了一系列與 Dart 2、排名和搜尋相關的改進。

如果您已經發佈了套件,現在 是時候更新它們以適應 Dart 2 了:

  1. 確保您的套件通過 Dart 2 分析。
  2. 在 pubspec.yaml 中將 SDK 約束上限更新為 <3.0.0。
  3. 重新發佈您更新的套件。

然後前往您在 pub.dartlang.org 上的套件分析頁面,看看您還可以做些什麼來提高您的排名!


準備迎接 Dart 2,並讓您的套件在 Pub 網站上看起來更棒! 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/