Dart 針對網頁應用的網站在 https://webdev.dartlang.org/ 上,目前主推是基於 Angular Dart 框架的開發。
也是有其他框架或是更基礎的作法,不過指引的網頁放在很邊緣的地方,簡單來說就是: Angular Dart, Polymer Dart, Low-level HTML 這些選項。
在 Get Started 大概就是介紹一些工具:
- DartPad 可以直接在瀏覽器中試驗 Dart 程式碼。
- 下載與安裝 Dart SDK 到本機端。
- 下載與安裝 IDE (整合開發環境) 到本機端,官網是推 JetBrains 的 WebStorm, 不過我用 CE 版的 IntelliJ IDEA 加上 Dart Plug-In 也算是還過的去。
- 介紹怎麼用 IDE 開測試網頁,背後其實就是
pub serve
這樣。自己直接下指令好像會比較順,有碰到幾次透過 IDE 啟動但跑得不太順。
文件層次結構有點怪,總之在 Angular Dart > Guide > Overview 下,說明了整個文件的結構 XD 大概就是:
目前還不確定先看 Guide 好還是先跑 Tutorial 好,簡單看過感覺好像都可以。
在 Architecture Overview 介紹了整個概念、架構、主要的物件類別。大概是下面這些組成:
- Template
- 使用 Angular 擴充過的 HTML 撰寫樣板。
- Component
- 撰寫 Component 類別來使用 Template。
- Service
- 應用程式的邏輯放在 Service 類別裡。
- Module
- Service 與 Component 封裝在 Module 裡面。
程式 (網頁) 打開是一個稱為 bootstrap the root module 的過程,會把 Angular 載入,然後使用者就可以跟介面互動,互動的事件會驅動 Angular 讓介面做出對應的變化。
AngularDart 的 module 跟 Dart 的 module 或 package 是差不多的概念,總之是一個編譯的單元。
除了最基本的 root module 之外,比較大的程式會分出數個 feature module 來將程式模組化。於最單純的情境下,在 root module 裡面就只有一個 component,稱為 root component,通常會命名為 AppComponent。
Component 可以控制一小塊稱為 view 的畫面,控制邏輯定義在一個類別裡面。在 Component 中會儲存有部份的應用程式的數據,主要是要支援 view 的顯示,比如說: 在 view 中顯示了人員列表,那麼 component 中可能就會有 List<Person> 物件實體。在 Component 中,常會關聯有一個 Service 物件的實體,用來提供數據資料,這部份直接看文件裡的範例可能會比較清楚。另外 component 有生命週期,可以藉由實作對應的介面方法來對生命週期中的事件做出反應: Lifecycle Hooks。
Template 基本上就是加上 Angular 樣板語法 (Template Syntax) 的 HTML,除了標準的 HTML tag 與 Angular Template Syntax 之外,也可以使用自定義的 Angular 元件標籤。
Metadata 用來描述 Dart 類別是什麼樣的 Angular 物件類別,或者說是把用 Dart 類別實作的邏輯接入 AngularDart 框架的方法,基本上就是用 Dart 的 @annotation 達成。比如說: 透過將 @Component 加到一個類別上,就可以告訴 Angular 框架這個類別是一個 Angular Component 類別。在 @Component 上,還會描述諸如 template 路徑 (templateUrl)、找尋對應標籤用的 CSS selector (selector) 等資訊。除了 @Component 之外,還有 @Injectable, @Input, @Output 等 annotation 可以使用。
透過 @Component 就可以把 template 跟 component 邏輯繫結在一起,所以架構圖上在 template 與 component 之間,用不一樣的圖例畫了 metadata 在中間。
Data binding (資料繫結) 可以讓資料在 Component 與 DOM 之間被推送或是拉取的動作由框架代勞,而不用自己手動寫一堆程式碼來做更新 UI 或是從 UI 取值這些讓人煩躁的事情。要使用 data binding 基本上就是加註到 template 上,描述資料怎麼從 component 拉取出來或推送進去。有 4 種 data binding 的語法:
- {{hero.name}}
-
這個語法稱為 Interpolation,功能是把資料值從 component 取出,轉換成文字後嵌入 (interpolate) 到 DOM 裡面。
<li>{{hero.name}}</li>
這是一個從 component 到 DOM 的單向 binding,主要是處理文字的資料。
- [hero]
-
這個語法稱為 Property binding,功能是把資料值從 component 取出,賦值到所嵌入的 child component 的屬性上。
像下面這個例子,就是從目前的 component (在此例是 HeroListComponent) 中,取出 selectedHero 屬性的值,賦值 (assign) 到被嵌入的 child component (在此例是 HeroDetailComponent) 中的 hero 屬性中。
<hero-detail [hero]="selectedHero"></hero-detail>
文件上的圖是說這是一個從 component 到 DOM 的單向 binding,不過因為資料會先被另一個 component (child component) 接收到,再視接收的 component 的處理邏輯反應到 DOM 上,感覺上也可以說是 component 到 component 的單向 binding。
這個語法可以用來傳遞物件實體,這點跟 interpolation 語法不太一樣。
- (click)
-
這個語法稱為 Event binding,功能是在使用者事件發生的時候呼叫指定的方法。
<li (click)="selectHero(hero)"></li>
這是一個從 DOM 到 component 的單向 binding。
- [(ngModel)]
-
這個語法稱為 Two-way binding,功能是雙向的在 DOM 或 component 以及 component 之間雙向的更新資料值。
<input [(ngModel)]="hero.name">
語法 [(x)]="p" 會假設使用了這個語法的 child component 上有屬性 x 以及會在事件發生時產生新的屬性值的事件方法 xChange,在初始化的時候會拿關聯的屬性 p 來設定 x 的值,並會在 xChange 產出新的值的時候更新關聯的屬性 p 的內容。屬性 p 的變動初步測試起來也可以反應到 x 上,不過之前在 Polymer Dart 上被搞到快不行,之後可能還是要留意。
一般的 HTML element 並不支援 x-xChange 的協定,因此有 ngModel 這個 directive 來包裝傳統的 HTML element,讓他們可以使用 two-way binding。
在 Angular 中是依據 Directive 的指引從 template 的 DOM 結構產生 (render) 出最後的 DOM 結構,一個 Directive 就是一個有 @Directive annotation 的類別。
技術上來說 Component 也是一個 Directive,不過因為 Component 算是整個 Angular 運作的中心,因此在架構上會拉出來說明。
從行為上來看,Directive 有分成 structural directive 跟 attribute directive 兩種,差別就是轉換的前後 DOM 結構是不是一樣。
Structural directive 在標記上要有一個星號 (*) 在標記的名稱前面,像是 *ngIf
這樣。實際上是會先產生一個 <template></template> 標籤,把被標記的標籤放進去,然後再把 <template> 丟進 directive 的 class 中處理。
在一個標籤中不可以有兩個以上的 structural directive,因為 structural directive 會變更 DOM 結構,變更動作的先後會對結果有影響,因此透過禁止有兩個以上的 structural directive 來迴避這個順序的問題。
常見的 structural directive 有: ngIf, ngFor, ngSwitch。
Attribute directive 主要是用來變更一個元素或節點的外觀或行為。因為設定上看起來很像 DOM 節點的 attribute,所以稱為 attribute directive。
常見的 attribute directive 有: ngModel, ngStyle, ngClass。
Service 基本上就是一個封裝好作業邏輯的類別實體,裡面就是實作應用程式邏輯、資料下載、資料驗證、訊息交換、紀錄等等,在 Component 裡面就是單純的呼叫 Service 提供的方法,並操作 view 做出回應。
對 Angular 來說,Service 並不是特別的組件,Angular 本身不會去看到或是識別出 Service 組件。如果要把邏輯通通實作在 Component 中也不是不行,但是這樣從程式的結構來看就是比較不理想。
AngularDart 實作了 Dependency Injection 機制來協助管理 Service 實體,讓 Service 的物件實體的產生跟 Component 的邏輯抽離。
使用 Angular 的 Dependency Injection (DI) 框架,大致上就是:
- 要被當成 Service 組件注入的類別要加註 @Injectable() (注意: 是產生一個 Injectable 物件實體來做 annotation)
- 在層次上開始使用到 Service 的 @Component annotation 上,給定 providers 參數來描述所要導入的 dependency 物件。
- 要用到 Service 組件的 Component 中,在建構子中指定所要使用的 Service 組件的型別。
依照 Hierarchical Dependency Injectors 的說法,在 AngularDart 框架裡頭會維護一個跟 DOM tree 結構大致上一致的 Injector tree 來進行 dependency resolve 的作業。在建立 Component 物件實體時,Injector 會沿著 Injector tree 往上去找尋適當的 Service 物件實體,當找不到的時候,就會依照 @Component 上 providers 參數所設定的 Provider 組態來建立 Service 物件實體。
No comments:
Post a Comment