
最後更新:2026 年 5 月 28 日
Quire(53,992 行、1,620 KB 的 Dart 程式碼)於 2014 年以 Dart 打造,用戶端與伺服器端皆採用 Dart,並以 DQuery 和 Bootjack 取代 AngularDART 與 Polymer.dart,以便更細緻地掌控 DOM。這些核心決策(全端強型別、用戶端與伺服器共用資料模型、伺服器端事件迴圈,以及輸出 JS 時的 tree shaking)到了 2026 年依然站得住腳。
Quire 不是第一個用 Dart 寫成的網頁應用程式,也不會是最後一個,但它可能是第一個在用戶端與伺服器端都如此倚重 Dart 的應用程式。

這是一款骨子裡採用樹狀結構的有趣任務管理工具。整個專案有 53992 行、1620KB 的 Dart 程式碼,並搭配社群中幾個開源函式庫打造而成。
在開始這個專案之前,我們是一群被稱為 Rikulo 團隊的 Dart 愛好者,已經釋出好幾個 Dart 函式庫,包括 UI 框架、UI 函式庫、網頁伺服器、訊息伺服器、資料庫用戶端等。
2011 年 Dart 剛問世時,我們就為它的未來潛力感到興奮,隨即在一些小型專案中開始使用 Dart。後來,我們進一步打造出一個主要以 Dart 組成的完整應用程式。在此誠摯分享一些心得,希望能為你開發 Dart 提供一點參考。
選擇 Dart 的理由很多,其中對我們最重要的有:
選擇 Dart 也有缺點:
有強型別支援,用 Dart 寫程式比用 JavaScript 可靠得多。Dart 也比 Java 簡潔,有時(例如使用函式運算式時)甚至比原生 JavaScript 更簡潔。整體而言,用 Dart 工作相當順手,只有幾點例外:
在 Dart VM 進入 Chrome 之前,你只能靠編譯成 JavaScript 來交付產品。許多語言都能編譯成 JavaScript,但 Dart 有幾項額外的優勢。
Dart 在伺服器端從來不是 Dart 社群的熱門話題,但我們認為 Dart 是伺服器端程式語言的有力人選,原因如下:
相關閱讀:Quire 通過 ISO 認證,保護你的資料,多年後,Quire 在正式的資料保護認證上,同樣重視伺服器端的穩健性與安全性。
Quire 匯入了約 30 個函式庫,其中 10 個來自社群,其餘由 Dart 團隊釋出。如果你是 Dart 專家,大概會猜裡面有 AngularDART 和 Polymer.dart,但其實兩者都沒有。
我們沒有使用 AngularDART,原因如下:
我們也沒有使用 Polymer.dart,原因如下:
我們用戶端技術堆疊的基石是 DQuery 和 Bootjack,兩者都是由 Rikulo 團隊釋出的開源專案:
我們循著這條路線打造應用程式的技術堆疊,好善用過去在 JavaScript 世界累積的知識與技能。
Stream 是我們的網頁伺服器,以純 Dart 撰寫,具備路由、過濾器、伺服器端 MVC 等功能。Stream 與事件迴圈模型搭配得很順暢。撰寫請求處理常式,只是串接一連串非阻塞的常式而已。
比起傳統的多執行緒模型,這樣不但更有生產力,用起來也更愉快。我們也透過 nginx 擴展網頁服務並處理 HTTPS,再把請求委派給 Stream。有了這樣的架構,我們可以個別啟動與停止 Dart VM,並在不中斷使用者操作的情況下升級伺服器。
用 Dart 工作是一段愉快的經驗,我們也期待 Dart 社群有更多令人興奮的發展。最後,如果你好奇用 Dart 能做出什麼,歡迎來玩玩我們的應用程式 Quire。它是免費的!
延伸閱讀:Quire 後來如何重新構想並重建為 Mark II。

自本文撰寫以來,Dart 語言與 Quire 程式碼庫都有了長足的演進。Dart 2.0(健全型別系統)在 2018 年問世,Dart 3.0(null safety、records、patterns)在 2023 年推出,而 Flutter 改變了跨平台 UI 的版圖(Quire 自己的行動版團隊也改用了它)。但上述的底層架構決策,經得起時間考驗的程度超乎你的想像。
全端強型別依然值得。 強型別系統在建置階段攔截的執行期錯誤,和當年是同一批錯誤。JavaScript 透過 TypeScript 朝型別化邁進了一部分,但完整的端到端型別化技術堆疊依然少見。Simon 提到的那類問題(無數的小錯誤),仍是無型別技術堆疊會帶上正式環境的那類問題。
用戶端與伺服器共用資料模型,依然能省下工夫。 資料模型複製問題(一份在 JS、一份在 Python,悄悄地逐漸走樣),2026 年和 2014 年並無二致。解法(端到端使用同一種語言)也一樣。TypeScript-on-Node 團隊打造類似 Quire 的架構,正是出於這個原因。
網頁伺服器採用事件迴圈而非多執行緒,依然勝出。 Node、Deno、Bun,以及 Dart 自家的 Stream 模式,看法一致。這個論點每隔幾年就被重新辯論一次,結論始終相同。
Tree shaking 變得更重要,而不是更不重要。 2014 年,Dart 的 tree-shaking 編譯器是一大差異化優勢。到了 2026 年,它已是任何以 JS 為目標的建置流程的基本配備。Webpack、Rollup、esbuild 和 Vite 都支援。這個教訓依然成立,只是工具追上來了。
2014 年對 ShadowDOM 的疑慮,大多已經化解。 Polymer.dart 已被棄用,Web Components 也已成熟。Simon 當初避開 Polymer 的理由(樣式注入、與框架協同運作),最終都由平台本身解決了。這個通則(正式應用程式的核心,不要採用尚在最前線的框架)到了 2026 年,對於當下那個「令人興奮」的框架依然適用。
相關閱讀:How It's Made: A Flutter-Based Mobile Timeline,了解 Quire 的行動版團隊如何運用 Flutter 打造時間軸檢視模式。
為一個要運作多年的應用程式挑選語言技術堆疊,是少數事後代價高昂、難以回頭的工程決策之一。團隊選錯時,常出現以下四種模式。
1. 預設選擇社群最大的語言。 JavaScript 的社群最大,但「最大」不等於「最適合你的問題」。型別系統強、工具完善但社群較小的語言,表現可能勝過保障薄弱的龐大生態系。社群規模很重要,但不是唯一重要的事。
2. 選擇用戶端與伺服器使用不同語言的技術堆疊。 每一份複製出來的資料模型,都是等著爆發的錯誤。伺服器用 Ruby、用戶端用 JS 的團隊,得永遠承擔這份成本。兩端共用同一種語言(Dart、TypeScript、Kotlin)的團隊,則避開了整整一類錯誤。
3. 在技術穩定之前就採用最前線的技術。 2014 年,Quire 團隊避開 AngularDART 和 Polymer.dart,因為兩者的架構都還有懸而未決的問題。AngularDART 後來大幅轉向,Polymer 則被棄用。這條通則(先讓框架在大規模正式環境中證明自己,再把應用程式核心押注在它身上)到了 2026 年,對於當下那個「令人興奮」的框架依然適用。
4. 為了短期速度而跳過型別系統。 無型別語言在第一週感覺比較快。到了第六個月,省下的時間會以除錯時間加倍奉還。同時待過無型別與有型別大型技術堆疊的工程團隊,幾乎不會自願回頭。
回頭看,Quire 團隊 2014 年押注 Dart 的決定相當合理,並不是因為 Dart 成為主流(它並沒有),而是因為背後的準則(型別化、用戶端與伺服器共用、事件迴圈、tree shaking)在之後十年成了業界共識。
禁得起時間考驗的,是底層的工程原則,而不是特定的工具選擇。在不確定中選擇語言時,這個規律通常都成立:要為十年後仍能站得住腳的原則做選擇,而不是為現在量得出來的人氣。
Dart,用戶端與伺服器端皆然。整個應用程式約有 54,000 行 Dart 程式碼,匯入約 30 個函式庫。
強型別、類別式繼承模型,以及用戶端與伺服器端使用同一種語言。Dart 也在 JavaScript 尚未普遍支援之前,就先提供了一些現代功能。
團隊想要比 AngularDART 資料繫結所能提供的更精細的 DOM 控制,而且 Quire 的架構模式與 Angular 的邏輯不符。至於 Polymer.dart,當時 ShadowDOM 與 Bootstrap 這類選擇器互相衝突,因此沒有採用。Polymer 後來被棄用,印證了這個決定。
由 Rikulo 團隊(Quire 的工程團隊)釋出的開源函式庫。DQuery 是 jQuery 的部分 Dart 移植版,著重於事件委派。Bootjack 是 Bootstrap 3 的完整移植版,CSS 與 API 幾乎完全相同。
Quire 的網頁伺服器,以純 Dart 撰寫,採用事件迴圈模型,內建路由、過濾器與伺服器端 MVC。Quire 將 Stream 架在 nginx 之後,由 nginx 負責擴展與 HTTPS。