Dojo 進階

官網 https://dojo.io

序言 - 構建企業級 web 應用程序

在熱衷敏捷交付的時代,鼓勵將小功能點持續地交付給用戶。軟件行業開始青睞這種方式,因爲它最大限度地降低風險,並最大限度地提高用戶的參與度和滿意度。

即使採用現代的交付方式,一些風險仍然不可避免。複雜性就是這樣一種風險,對於成熟的應用程序而言,複雜性更成爲一個重要的關注點。無論應用程序遵循什麼樣的系統架構,隨着時間的推移,許多小功能聚集出一個龐大且令人畏懼的代碼庫,需要幾個團隊監督。

應用程序上線的時間越久,實現一個設計簡潔的新功能的機會就越少。相反,更多的是在現有功能的基礎上調整、修復 bug 或擴展。一個成功的應用程序——以及所包含的功能——大部分時間都花在維護上。

維護複雜的應用程序需要經過嚴格的訓練。團隊很容易陷入泥潭,將時間花在抱怨代碼和同事上面,而不是向用戶交付價值。要降低這種風險涉及很多方面,包括標準化、模式化、技術選型和工具等領域。

管理複雜性

在軟件交付的生命週期中,錯誤發現的越早越好。在開發階段修復一個錯誤,比在交付環節修復錯誤,或者已給用戶帶來負面影響的上線階段修復錯誤要快的多,成本也低得多。

強類型

早期捕獲錯誤的好方法是在應用程序的開發階段支持強類型。如果應用程序的代碼中顯式指定了類型信息,就可以避免數據類型不匹配而導致的邏輯錯誤。編譯器和靜態類型檢查程序可以驗證類型信息,並當類型不匹配時讓構建失敗。只有修復了這些錯誤,軟件才能從個人的工作空間進入到交付管道的後續環節。

Dojo 構建在 TypeScript 之上,提供了顯式的類型和靜態編譯時的類型檢查。使用 Dojo 構建的應用程序可以用到 TypeScript(而不是普通的 JavaScript)帶來的優勢。

當使用 Dojo CLI 構建應用程序時,應用程序的構建過程會默認包含 TypeScript 的編譯階段。開發人員從一開始就能編寫出類型安全的應用程序。

模塊化——單一職責原則

理想情況下,一個組件應該足夠小,以便它只實現單一職責。一個組件越簡單、封裝程度越高,則大量程序員長期理解和維護時就越容易。擁有龐大代碼庫的大型應用程序就是由大量的更小、更容易理解的組件組合而成的。

試圖降低複雜度時,在單個組件中隔離職責有以下好處:

  • 限制範圍。假設組件維護一套一致的 API,則能在不影響外部用戶使用的情況下更改內部實現。相反,組件的詳細信息保存在定義模塊的內部,這意味着其定義不會與其他組件的定義衝突,所以就不會與其他組件的命名約定重疊。
  • 簡化測試需求,因爲單元測試只需關注單一職責,而不是多個條件組合成的越來越多的應用程序邏輯。
  • 組件可以在多處重用,避免多處重複實現。修復錯誤時,只需專注於單個組件,而不是散落在各處的單獨實例。

對於 web 應用程序而言,隔離還能爲終端用戶帶來額外的好處。應用程序可以劃分爲多個層,用戶在給定的時間點只加載他們感興趣的層。這減少了資源大小和網絡傳輸需求,從而縮短了加載時間。

Dojo 應用程序的組件

Index 網頁

HTML 頁面是每個應用程序的基本內容,Dojo 應用程序也不例外。傳統上,單個 index.html 文件既是應用程序的入口點,也是將應用程序的整體結構存入 DOM 的根容器。

Dojo 應用程序通常會注入到單個 DOM 元素中,默認情況下是注入到 document.body 中。這使得 Dojo 應用程序可以與頁面中的其他內容共存——靜態資源、傳統的應用程序、甚至是另外一個 Dojo 應用程序。

部件

Dojo 中的部件與 DOM 元素類似,是 Dojo 應用程序中封裝的核心概念。正如傳統網站是通過 DOM 元素逐層構建的一樣,Dojo 應用程序則是通過部件逐層構建的。

部件可以描述一切,從單個界面元素(如 label 或 textbox)到更復雜的容器(如 form 表單、頁面或者整個應用程序)。

類似地,並非 DOM 中的所有元素都對用戶可見,Dojo 部件不僅提供用戶界面,也可以實現應用程序的所有幕後需求。

詳見創建 Dojo 部件參考文檔,瞭解如何在應用程序中創建部件。

TypeScript 模塊

Dojo 部件可以是一個渲染函數工廠或者 TypeScript 類,通常包含在單個 TypeScript 模塊中。該模塊封裝了組成部件的大部分內容,包括它的行爲以及虛擬 DOM 的語義化表示。

部件通過屬性接口向外部消費者提供 API。這個接口既可包含狀態字段列表,在渲染時注入到部件中;也可以包含函數,當事件發生時,部件需要通知應用程序的其他部分時調用,比如部件狀態的變更。

CSS 模塊

部件的外觀樣式是交由 CSS 設置的,與常規的 HMTL 元素樣式類似。CSS 模塊用於封裝單個部件的樣式,避免與其他部件的 CSS 類名衝突。

部件導入 CSS 模塊跟導入其他 TypeScript 模塊一樣,並允許通過對象屬性引用 CSS 類名,這些屬性會在開發人員的 IDE 中自動提示。在定義部件的語義元素結構時,可以使用這些屬性名指定樣式類。部件中的 CSS 類名和最終的樣式類名不一致,而這可以在構建階段識別出來。

雖然部件的 CSS 模塊可以完全封裝自身的樣式,但通常也需要一些靈活性。部件可以在應用程序的不同配置下使用,每個配置都有自己獨特的外觀需求。Dojo 提供了覆蓋特定樣式的能力以滿足這個需求。

爲了支持應用程序層面外觀的一致性,可以通過主題進一步控制部件的樣式。

詳見 Dojo 樣式和主題參考文檔,瞭解如何爲單個部件設置樣式。

狀態管理

企業應用程序通常需要持久化狀態,並允許用戶以各種方式查看和操作這些數據。當需要同時在多處訪問和編輯同一數據,且要保持數據的一致性時,狀態管理可以成爲大型應用程序中最複雜的領域之一。

狀態通常存在位於 web 應用程序組件外部的數據存儲或數據庫中,這意味着一些狀態管理複雜性需要在應用程序之外解決。然而,對於數據在應用程序與其用戶之間流動的情況,有幾個範例能夠大大的降低管理複雜狀態的風險。

響應式的數據修改

以命令式方式編寫的應用程序會描述應更改哪些數據,應該如何更改,以及指定必須在何時何地更改。如果通過某種形式的計算或賦值在邏輯上連接多塊數據,則這些連接在一段時間後只能表示成離散的點。久而久之,除了這些點之外,可能會以違反預期邏輯連接的方式修改任何數據值。

相反,以響應式方式編寫的應用程序設法提升數據之間的邏輯連接,並放棄對明確地指定何時何地修改數據的控制,以使邏輯數據連接始終保持一致。

具有多個服務層的複雜應用程序可能會在多處描述相同的數據,因爲它們散落在應用程序的各處——這方面的一種常見模式是使用數據傳輸對象。一段數據的描述位置越多,則維護應用程序狀態完整性的複雜度呈指數級增長。

任何應用程序只要 UI 需要動態展示(包括 web 應用程序),都會遇到維護邏輯數據連接一致性的問題。這些應用程序中的數據通常至少有兩種表示方式。

舉例說明問題

假設有一個待辦事項應用程序,它存儲了一組任務,當向用戶顯示時,每個任務都有以下兩種數據表示方式:

  • 任務的確切描述(它的“真實來源”,例如它在數據存儲中的值)
  • 任務描述的副本,通過 UI 元素(如 label 或 textbox)呈現給用戶。

如果用戶只能查看任務,則有幾個問題與如何修改任務的描述以讓用戶可見有關。

如果在底層的數據存儲中更改了任務,則需要通過 UI 向上傳播新的描述信息,這樣用戶就不會查看過時的數據。如果任務顯示在 UI 的多個位置,則所有實例都需要更新,確保用戶不會在不同位置看到的數據不一致。

如果用戶還可以修改任務(比如更改描述信息),則還需要解決其他問題。

任務描述現在有兩處真正的來源:數據存儲中的舊值,以及用戶在 textbox 中輸入的新值。

然後,需要將修改請求傳回給底層的數據存儲,以便用新值替換舊值。修改完成後,需要將新的任務描述返回給用戶,讓用戶看到更改後的正確值。嘗試修改任務描述時會發生的任何錯誤也需要在數據交換時考慮。

Dojo 的狀態管理

對於最基本的狀態管理需求,部件可以使用本地變量管理自身的狀態。雖然這種方法有助於隔離和封裝,但它只適用於非常簡單的用例,如只在應用程序中出現一次的部件,或者與應用程序處理的所有其他狀態都斷開了連接的部件。

隨着在部件間共享狀態的需求增加,Dojo 支持響應式的控制反轉。將狀態提升到父容器部件中,然後使用子部件的 properties 接口注入到子部件中。如果需要,這種狀態提升可以橫穿整個部件層級,將狀態集中在應用程序根部件中,然後將部分狀態注入到相關的子分支中。

對於更復雜的需求,或者對於較深的部件層級且不希望在不相關的中間層傳遞狀態,則外部的數據存儲可能是最好的方法。集中的數據存儲能夠幫助應用程序處理大量的狀態,允許複雜的狀態編輯操作,或者在多處請求相同的狀態子集。

Dojo 提供了一個 Store 組件,它支持多種高級的狀態管理需求,例如:

  • 內置支持異步調用,例如調用遠程服務進行數據管理。
  • 狀態操作按確定的順序執行。
  • 記錄狀態操作歷史,允許回滾或撤銷操作。
  • 中間件用於包裝數據操作流程,可添加橫切點,如用於授權或記日誌。
  • 內置支持基於 LocalStorage 的數據存儲,有助於實現 PWA。
  • 支持樂觀的數據更新,失敗時會自動回滾。

用戶體驗

Web 應用程序本質上是通過用戶界面提供體驗的,應用程序的作者需要考慮各種因素,以向用戶展示最好的界面。一致的可視化外觀和可訪問性通常是最顯眼的因素,但也需要關注效率和性能,無論是應用程序的邏輯,還是交付的內容,都有助於提升 web 應用程序的用戶體驗。

主題

應用程序提供最佳用戶體驗的一種方式是向最終用戶提供一致的外觀。這可能與在類似的元素中使用一致的字體一樣簡單,但通常會擴展到使用相同的色調顯示應用程序,甚至實現一整套設計語言,如 Material Design。

Dojo 的樣式管道使用 CSS 模塊將樣式規則封裝到特定的部件中,避免在大型代碼庫中交叉污染。但是,樣式並不是完全隔離的——集中的 CSS 變量能夠定義公共的主題屬性並在應用程序的所有部件間共享。也可以爲 Dojo 部件套件提供自定義主題。

詳見 Dojo 樣式和主題參考文檔,瞭解如何創建應用程序主題。

UI 部件套件

通過部件套件,Dojo 提供了一些現成的 UI 組件。開發人員可以立即使用這些部件製作許多常見的頁面,如 combobox、button、list、tab、text input 和 calendar 等部件。

Dojo 的部件支持國際化、可訪問性主題,讓開發人員在無需自定義 UI 組件的情況下,能夠靈活的交付應用程序專有的用戶體驗。

導航路由

雖然有些應用程序爲用戶提供了一個主視圖,其中可以處理大部分工作,但很多應用程序中用戶需要訪問更多的區塊。幫助頁面、設置面板或者分步驟工作流這些例子中,應用程序可能有多個界面,用戶可以在任何時間訪問這些界面。

應用程序的每塊內容都需要唯一標識符,這樣用戶就可以訪問它們。這些標識符也必須要支持爲鏈接設置書籤和分享鏈接,以便跳轉到應用程序特定區塊。用戶也需要在不同區塊間導航,以便可以訪問應用程序提供的所有功能。導航可以前進到下一步、後退到上一步或者根據用戶的選擇在多個選項間跳轉。

使用靜態文件的傳統網站包含可單獨識別的內容,因爲站點中的每個靜態文件都能單獨訪問。HTML 文件可使用錨點元素,通過點擊鏈接在不同文件間導航,而不必手動修改瀏覽器地址欄中的 URI。

顧名思義,單頁面 web 應用程序只有一個主文件,用戶通過該文件訪問整個應用程序。但是,這些單頁面應用可以使用 URI(連同 URI 已有的優點)來標識每一個小節。

路由組件爲跨層級的路由提供了導航選項,並會將相應的已標識的路由分發到相應的應用程序區塊。路由還將處理任何錯誤條件,例如導航到不存在的路由。

Dojo 路由

Dojo 的路由系統允許將 URL 的子路徑註冊爲路由,以鏈接到某個特定類型部件上,這個特定類型的部件稱爲 Outlet。當用戶導航到特定的路由時,將會渲染註冊到該路由上的 Outlet 部件。

當用戶導航到 Outlet 時,就會“渲染” Outlet,但 Outlet 很少直接處理應用程序的渲染。Outlet 主要是處理導航的封裝器(傳入查詢參數或者處理錯誤的回調),而將渲染功能委託給應用程序中的其他部件。

類似於在傳統 HTML 頁面中使用的錨點,應用程序可以使用與 Outlet 關聯的 Link 部件向用戶提供導航選項。

當使用路由時,Dojo 的構建系統能爲應用程序中的所有頂級路由自動生成單獨的包。然後可以根據需要將每個包獨立的交付給用戶。

詳見 Dojo 路由參考指南,瞭解如何在自己的應用程序中實現路由。

效率和性能

高效的渲染

動態網站內容(即包含 JavaScript)成爲 web 的一部分已經有很多年了。長期以來,站點就可以包含一些腳本來操作 DOM,進行添加、更新或刪除內容。但是,Web 的起源(至今仍然是它的一大關鍵特徵)是以靜態頁面爲基礎的。隨着時間的推移,瀏覽器的 DOM 實現得到了優化,以便儘可能高效地、快速地向最終用戶渲染靜態內容。

近年來,隨着 web 應用程序越來越複雜,瀏覽器已通過 DOM 性能優化做出了迴應,針對動態內容作了優化。然而,爲了渲染用戶界面,web 應用程序仍然需要與一套幾十年不變的命令式 API 交互。圍繞響應式數據傳播而設計的現代 web 應用程序需要一種更高效的方式,將用戶界面轉換爲網頁的 DOM。

Dojo 將 DOM 從應用程序中抽象出來,推薦使用響應式狀態流來最小化應用程序的樣板文件,同時提高了渲染性能。部件會在渲染函數中輸出虛擬節點,這些渲染函數使用虛擬 DOM 描述部件的結構層級。然後,框架以儘可能高效的方式處理 VDOM 的渲染,只會影響實際需要修改的 DOM 元素。

需要從 DOM 中獲取具體信息來實現其需求的應用程序,Dojo 通過中間件系統提供了另一種 DOM 抽象層。Dojo 中間件以一致的方式解決了這些問題,並仍然支持橫跨應用程序的響應式數據流。

應用程序的交付——分層和包

隨着 web 應用程序規模的增長,當一個任務只需要訪問一部分資源時,卻必須加載應用程序的所有資源,這樣效率就會越來越低。每一個應用程序資源都有一個與大小相關的成本:內存空間需求和網絡上的數據傳輸;所有這些都會影響到用戶開始工作之前需要的等待時間。讓應用程序只在需要的時候加載所需的內容,從而將此成本保持在最低水平,這符合用戶的最大利益。

獲取應用程序的資源時,在 HTTP 資源協商方面會產生額外的開銷。客戶端需要請求數據,然後客戶端必須等待服務器發送完資源的最後一個字節。更嚴重的情況下,開銷還包括 DNS 解析、TCP 連接重建和 TLS 密碼/證書協商。

瀏覽器可以有效地減少這一開銷,但是瀏覽器不能完全消除這一開銷——應用程序也有責任來減少資源傳輸的開銷。與資源的大小相比,獲取一個應用程序資源的開銷是相對不變的。獲取1KB 文件的開銷與獲取100KB 文件的開銷類似。

因此可以通過兩種方式降低開銷:減少資源總數和增加單個資源的大小。web 應用程序可以通過分層和將相關的資源打包來實現這兩種方式。

單個層中應該包含應用程序中特定功能相關的資源集。當用戶訪問該功能時,層中的所有資源可能同時加載。然後一個層包含的所有內容都可以打包到一個文件中,以便更高效地傳送給用戶。

自動分層

當使用 Dojo 的路由系統時,應用程序可以從自動分層和打包中獲益。應用程序中的每個頂級路由都成爲一個單獨的層,Dojo 的構建系統會自動打包每層內容。這樣就可以對層分離,以及打包資源,而不需要配置額外的工具鏈。這種自動化方案有一處折衷,即在每個包中都內聯和複製了跨多個層的公共依賴項。

聲明分層

複雜的應用程序可能需要對層或包的定義做更細粒度的控制。例如,如果應用程序有一組橫跨多個路由的公共依賴項,不要在每個包中內聯或複製這些依賴,則需要將公共依賴提取到自己的包中,然後在第一次引用時延遲加載。

Dojo 的構建管道允許在應用程序的 .dojorc 構建配置文件中指定資源,然後能自動將橫跨多個包的模塊依賴項轉換爲延遲加載的引用。

可訪問性與國際化

Web 本質上是全球性的,爲其編寫的應用程序需要支持所有用戶。文本需要按用戶選擇的語言和腳本顯示,並且需要根據用戶的區域設置對日期、時間、數字和貨幣等值進行相應的格式化。

Dojo 允許輕鬆使用消息包將文本消息從應用程序邏輯中分離出來,然後根據需要選擇使用 Unicode CLDR 數據的相關部分支持更高級的值格式化。

開發 web 時,需要應用程序對用戶足夠包容,不論用戶是否需要可訪問性。W3C 的可訪問性提案已經幫助標準化了許多這方面的需求,包括對可訪問的富 Internet 應用程序做的額外工作。

使用 Dojo 的部件套件開發的應用程序已提供了現成的 WAI-ARIA 屬性。雖然 Dojo 在這一點上提供了幫助,但它只也只能做這麼多——應用程序作者有額外的責任來驗證他們的應用程序提供的可訪問性級別。建議在應用程序的交付生命週期中包含顯式的可訪問性測試步驟。

詳見 Dojo 國際化參考文檔,瞭解如何爲全球用戶開發 Dojo 應用程序。

可適配的外觀

當前社會,Internet 的重要性與日俱增,應用程序被要求能適應用戶訪問 web 的各種方式。較小尺寸的移動體驗已經超過了桌面,但較大的外觀仍然可以滿足複雜的應用程序需求。Dojo 提供了多種解決方案,幫助開發人員創建適應用戶訪問需求的應用程序。

當需要預渲染內容時(如開發靜態站點時),Dojo 應用程序可以利用構建時渲染(BTR),應用程序結構的一部分或全部都是在構建時計算的,而不是在用戶瀏覽器中運行時計算的。Dojo 提供了一個靈活的基於塊 BTR 的解決方案,當構建應用程序時能運行 Node.js 腳本,支持讀取文件來獲取內容等功能。Dojo 的 BTR 解決方案也支持漸進式融合,以在預渲染內容之上支持動態行爲。

漸進式 web 應用程序(PWA)有助於提供與本地設備 App 接近的體驗,同時依然能從 web 支持的可移植性和易交付等功能中受益。Dojo 通過簡單的構建配置就能幫助創建 PWA,開發人員可以在應用程序中添加離線使用、後臺數據同步和推送通知等。

Dojo 允許開發人員通過中間件系統,在所有的交付目標上以一致的方式使用幾個即將可用的 web API。Intersection observer API 用於更好的控制渲染,僅渲染用戶可見的部件,例如支持無線滾動列表。Resize observer API 能夠讓應用程序動態響應視窗大小的變化,允許界面在桌面和移動視窗的所有分辨率間逐步適應。

應用程序的開發生命週期

Dojo 爲開發 web 應用程序提供了一個端到端的管道。應用程序的作者可以使用 dojo create app CLI 命令快速創建 Dojo 應用程序。然後可以使用 dojo build app 命令在開發模式和生產模式下構建應用程序。使用本地 HTTP 服務運行應用程序,並監視對項目文件的修改,構建工具爲快速開發和迭代提供支持。使用這種機制,開發人員可以在運行的應用程序中更改代碼並能立即看到結果。

這些命令是模塊化 Dojo CLI 工具鏈的一部分,該工具鏈支持開發生命週期中的各種使用。通過應用程序根目錄下的 .dojorc 配置文件,可以配置應用程序的構建管道。

詳見 Dojo 構建參考文檔,瞭解如何使用 Dojo 構建各種應用程序。

測試的策略

編譯器和靜態類型檢查程序無法捕獲出所有的錯誤。編寫的功能在語法和邏輯上是有效的,但要麼在運行時出現無法預見的問題,要麼不是按預期的要求執行功能。爲了降低這種風險,需要進行額外的測試。

當使用 Dojo CLI 構建應用程序時,默認會內置一個 Intern 測試庫的測試運行器。這樣開發人員人員在編寫應用程序功能的同時就可以立即編寫測試代碼。

Intern 爲很多測試場景提供瞭解決方案,但可能不足以滿足項目的所有測試需求。Dojo 也提供了一個簡單的測試工具,允許應用程序測試代碼在 VDOM 的抽象層級驗證框架和部件。這個工具可以用在很多測試運行器中,如 Intern、Jest 或應用程序測試策略所需的任何其他程序。

詳見 Dojo 測試參考文檔,瞭解如何高效測試 Dojo 應用程序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章