AngularDartの、Angular Componentsの使い方
フルスクラッチでUIを構築していくのは現実的ではない。現代のUIに対する要求水準の高さに応えるために決定的に重要な要素として、UIライブラリの質がある。
Angular TypeScript版にAngular Materialライブラリが提供されているように、AngularDartにはpackage:angular_components
が提供されている。このパッケージは、マテリアルデザインのUIを構築するためのさまざまなUI部品がAngularのComponentとしてまとめられ、またUIテーマやスタイリングのためのSCSSが同梱されている。AngularDartでUIを構築するならば、素直にマテリアルデザインを採用するべきだ。
Angular TypeScript版のAngular Materialのサイトと比較して、AngularDartのpackage:angular_components
のサイトは地味なので、誤解されることもあるかもしれない。そこで、ここではpackage:angular_components
の使い方を解説していく。
特徴
使用方法
セットアップ
セットアップ方法については、他のdart packageと同じくREADMEを参照する。 https://pub.dartlang.org/packages/angular_components
SCSSのビルド用にpackage:sass_builder
が必要。
表示
たとえば、MaterialButtonの場合は、
import 'package:angular_components/material_button/material_button.dart';
とインポートして、
@Component
アノテーションのdirectives
名前付き引数にリストとしてMaterialButtonComponent
を指定する。
import 'package:angular/angular.dart'; import 'package:angular_components/material_button/material_button.dart'; @Component( selector: 'buttons', templateUrl: 'buttons.html', directives: [MaterialButtonComponent], ) class ButtonsExampleComponent {}
そして、Templateにカスタムタグを書けば表示される。
<material-button>Default</material-button>
ほとんどのタグにはmaterial-
prefixがつけられている。
いくつかのComponentでは、directives
への登録に加えてproviders
への登録が必要。
使い方が分からなければ、各componentの使用例を参照する。 例えば、MaterialButtonの場合は、 https://dart-lang.github.io/angular_components/#/material_buttonと、そこからリンクされているソースコード https://github.com/dart-lang/angular_components/blob/master/examples/material_button_example/lib/material_button_example.dartを読んで理解する。
レイアウト
アプリのレイアウトをスクラッチで書くのは地味に面倒でバグが発生しやすい。そのため、https://dart-lang.github.io/angular_components/#/app_layoutでマテリアルデザインのレイアウトを構築する方法について解説されている。Flutterのようなscaffold widgetだったり<app-layout></app-layout>
というコンポーネントがあるわけではない。複数のComponentやscss stylingを組み合わせる形。
ささいなUI部品はComponentでなくSCSSだけで表現
FlutterではすべてがWidgetであったりやTS版AngularのAngularMaterialがささいなUI部品でもComponentで表現しているのとは異なり、package:angular_components
では、ComponentにせずSCSSで表現するものもある。たとえば、MaterialCardやDividerといったもの。
MaterialCardについては、以下のようにmdc-card
のscssを指定して表現している。
https://dart-lang.github.io/angular_components/#/material_card https://github.com/dart-lang/angular_components/blob/master/examples/material_card_example/lib/material_card_example.html
Dividerについては、ギャラリーにセクションは設けられていないが、そのようなささいなものはpackage:angular_componentsが提供するColor関係の変数を用いて、
@import 'package:angular_components/css/material/material'; .divider { display: flex; width: 100%; border-bottom: 1px solid $mat-border-light; }
このように定義するだけでよい。
<div class="divider">
残念ながらFlutterやAngularMaterialほどのコードの統一感はないが、おそらく実行性能を重視した結果だと思う。
スタイリング
マテリアルデザインのためのスタイリング用のSCSS変数やMixinが用意されている。基本的に、以下をimportして変数を使用する。
@import 'package:angular_components/css/material/material';
*注) sass-builder
はdart-sass
に依存している。dart-sass
は、Dartのpackage urlをサポートしているため、上記のpackage:angular_components/
の指定は有効。
これにより、
// How to use me: // // This set of scss files should never output any css unless sass classes are // extended in the importing file. This allows you to import _material without // bloating the css files for your components. All variables and components // within the material stylesheet are prefixed with "mat-" in order to give a // clear distinction between them and the styles in components that are using // these material styles. // // Example: // // small-example.scss // @import "package:angular_components/css/material/material"; // // .my-app { // background-color: $mat-blue-700; // } @import 'color_palette_material'; @import 'color_material'; @import 'core_material'; @import 'elevation_material'; @import 'scrollbar_material'; @import 'transition_material'; @import 'typography_material';
が有効となる。これらでマテリアルデザインガイドラインで定義されたスタイリングは一通り定義されているはずなので、ここから必要となる変数を利用していく。以下、例で説明する。
テーマカラー
例えば、アプリのテーマカラーを指定するには、
$primary-color: map-get($mat-teal-map, $teal-number); $secondary-color: $mat-light-green-400;
といった具合に書き、各コンポーネントのSCSSで提供されているmixinなどで指定していく。
Elevation
すべてSCSSで定義されており、使用方法もelevation_material
のソースコードのコメントに書かれている。
/// Applies the Material Shadow styles to the selected element. /// /// Use the attributes below to control the shadow. /// /// - `animated` -- Whether to animate the shadow transition. /// - `elevation` -- Z-elevation of shadow. Valid Values: 1,2,3,4,5 /// /// Example: /// /// .shadow-box { /// @include material-shadow(); /// } /// /// <div class="shadow-box" animated elevation="3">...</div>
その他の要素
フォントサイズ、リンクスタイル、マージンなど、一通りのスタイルが定義されているので、マテリアルデザインガイドラインに従うためには、marginをアドホックに指定したりアプリ独自に変数を定義するのではなく、上記で説明したように@import "package:angular_components/css/material/material";
を用いて定義された変数を探して適用していくことが基本となる。
各UIコンポーネントへのテーマの適用などのカスタマイズ
MaterialButton
などの各UIコンポーネントには、テーマの適用などのカスタマイズ方法として、SCSSのmixinなどを用意されている。たとえば、MaterialButtonならば、material_button/_mixins.scss
など。
例:
$primary-color: map-get($mat-teal-map, $teal-number); $secondary-color: $mat-light-green-400; @mixin button-mixin { $selector: 'material-button'; @include button-color($selector, $primary-color); } @mixin input-mixin { @include material-input-theme($primary-color); }
サードパーティのマテリアルデザインPackage
FlutterのWidgetの充実度と比べると、package:angular_components
でサポートしているUI部品の種類は相対的に少ない。不足している部品はスクラッチで作ってしまうのも良いが、サードパーティのものを利用することも検討する。例えば、package:skawa_material_components
。
https://pub.dartlang.org/packages/skawa_material_components
以下の部品が提供されている。
<skawa-data-table>
<skawa-grid>
<skawa-snackbar>
まとめ
Dartはソースコードの可読性がとても高いしDoc Commentも充実しているので、PackageのソースコードとDoc Commentを読む習慣があれば上記の情報はすぐに把握できる。しかし、AngularDartに興味をもった段階でその関連ソースコードを読む、というのもハードルが高いので、AngularDartへの理解の促進と誤解を解くための一助として、この解説を書いた。
不明点や疑問点は、以下のチャンネルなどで質問を投げてくれたら解答したい。
公式ガイド
2018年のDart言語の振り返り
2019年初に2018年のDartを振り返ってみる。僭越ながらDart advent calendar 25日目の記事だが、大幅に超過して新年になってしまった。申し訳ない。そして大した内容ではない。個人の回顧です。
Dart 2のリリースと、Dart 1の回顧
Dart 1を回顧するに、以下の特徴をもった言語だったと思う。
- VMを活かした動的言語であること
- VM前提の動的言語の表現力、進化を妨げないための、選択換装可能な型システム (言語が型システムに依存するのではなく、型システムが言語に依存する)
- エディタ、デバッガ(等々)が統合された開発体験などの、Smalltalkを目標とした環境の提供(が目標)
- ライブラリや開発環境などを整備し、言語のカタログスペックでなく実際のDeveloper Experienceが優れていること
- 世の中の圧倒的多数を占めるCとJavaのプログラマーに学習コストとストレスなく使ってもらうため、C系(Java系)の構文であること
- 他言語で良いとされている機能を単にカタログスペックを埋めるように採用するようなことはせず、言語仕様の無駄な肥大化を抑えること
- JavaScriptに無理なくコンパイルできること
Dart 1時代のコアメンバーの経歴や発言、Snapshotの追加や独自統合開発環境(DartEditorやCDE)の提供の試みなどを観察するに、Dart 1によって、Javaの皮を被ったSmalltalkをWebにもたらそうという意思が強く伝わってきていた。そのような野心的な構想に、どうなるのかと興味を惹かれ続けてきたが、その心づもりの言語は結局さまざまな環境要因により、当時のWebコミュニティでは人気を得ることは叶わなかった。これは本当に残念なことだった。
Dartが、Webに加えて他のプラットフォームへの適用の可能性を模索してきた中で結実したもののひとつがFlutterだが、その過程において、iOSなどのプラットフォームの制約事項によりDart言語がVMを前提にできなくなり、AoTコンパイル後のパフォーマンスとスペースを少しでも上げるために、型システムへの方針を変更してAoTコンパイル時に積極的に型情報を利用するものに刷新したものがDart 2であるという理解をしている。
それに伴い、型システムでなくランタイムからのフィードバックに依存する動的言語としての側面はなりをひそめ、AoTコンパイルとコンパイル時メタプログラミングの技法が重視されるようになった。dart:mirror
は実質的にVM環境でないと使用できないというややもどかしい状況になっている。ただし、Flutterでも開発時や、CLIやサーバーサイドはVMで動作する。
2018年にDart 2がリリースされるまで、宣伝を控えて年単位で大改装が続けられていたが、Dart 2を決意して無事リリースされた現在は、マイナーな機能改善に着手できる段階に達したのだろう。リンクは張らないが、言語チームはいろいろな改善を検討していることがGitHubから観察できる、と書いていたら、うまく日本語でまとめられていたので、リンクを張っておく。
しばらくは、Flutter中心のフィードバックを経て、ユースケースベースで言語機能の改善が続けられていくのだろう。
Dart 1では、主にScalaやKotlinあたりが優れた言語であると信じるプログラマーから、それらの(というか、Java)言語に表面上似ていても独自のビジョンをもって優先順位を決めていたこの言語に対して、批判がたびたび聞こえてきていたし、現在も、Flutterの人気爆発によりDartが注目されたため、それが再び散見されるようになった。
Dart言語は、評価が定まっていない仕様を軽率に採用してあとから取り除くこともできず、結果として言語仕様やコアなエコシステムが乱雑になる事態を警戒してきたことが観察できる。たとえ、Kotlin、Swift、ScalaやJavaScriptなどの他の言語で良いとされて導入されているアイデアであっても、言語仕様や標準ライブラリへの追加には、極めて慎重で注意深い態度を示してきた。
たとえば、言語マニアからまれに批判される、パターンマッチングが無いことなどについても、長い議論の末に、極めて意図的に、あえて非採用、またはペンディング扱いになっていることがGitHub Issueやカンファレンス動画などからうかがえる。
そして、Dart 1にはそのようなマイナーな改善よりもはるかに優先順位が高い事項があったが、悲しいことに、上記のようにその独自の哲学とビジョンはあまり理解されることはなく、推進力を得ることができなかった。
Dart 2時点では、Dart 1で主張していた動的言語としての側面はなりをひそめてしまったので、Java, Scala, Swift, Kotlinといった言語とのスペック比較がやや正当なものになっていくのではないか。
Flutter
2015年(だったか?)のSkyの発表からそれがFlutterになり、観察、学習しつつもまだアルファ版だからなあと静観していたら、昨年あれよあれよと人気が盛り上がっていき、12月に1.0に到達した。
https://ntaoo.hatenablog.com/entry/2018/12/05/091924
Flutter専門のコミュニティが立ち上げられたり、@_mono さんによる、Flutterの解説を専門とした素晴らしいブログなど、日本でもなかなかの盛り上がりを見せている。
FlutterでDartの魅力を知った方が 初心者にこそオススメしたい言語Dart といった記事を書かれたり、先入観なくDart言語とその開発環境の魅力が評価されていく現象が広がっていってたいへん嬉しく思う。
アプリをFlutter Dartで書けても、ネイティブAPIを叩くプラグインはKotlinかJavaで書くしかないので(AndroidでネイティブAPIを叩くときにDartが利用できるという選択肢は当面ないんじゃないか)両者はうまく棲み分けすることで安定しそうだ。(FlutterでUIを書くFuchsiaも控えていることだし。FuchsiaとAndroidの関係はどうなるのだろう?)
Dartは2.0で良くも悪くも手堅い言語になったと思う。これからは、拡張関数などのマイナーな改善が続けられていく段階に到達したようなので、Kotlinファンの不満もなくなっていくだろう。
Dart for Web
JavaScriptの話題が多彩なのでどうしても見逃されがちだが、Web向けの機能もDart 2に伴って地道に改善が続けられているので、JavaScriptに疲れた人には強くお勧めする。フレームワークについては、当面はAngularDartが第一の選択肢になる。
https://ntaoo.hatenablog.com/entry/2018/12/15/173713
2018年12月にWeb版Flutterとして、Hummingbirdの構想が発表された。たとえFlutter Webが未来だとしても、AngularDartは現在もうある非常に信頼できる技術なので、Flutter Webが安定するまでは第一の選択肢としてありがたく利用させていただくことになる。AngularDartとFlutterでModelのコードを共有しておき、Hummingbirdが安定したらゆっくりと置き換えていけばいいんじゃないかと思う。その期間は、数年はあるのではないだろうか。
サーバーサイドDart
サーバーサイドでも、すでにJavaで巨大なサービスを運用して苦闘している膨大な数のJavaエンジニアにとって、学習コストを抑えて両者を併用して書きつつ、場合によってはスムーズに移行できるという特徴は大きな価値を持つだろう。もっとも、まだライブラリ不足なのは否めないが...
https://aqueduct.io/やJaguarなど、コミュニティベースのフレームワークも年齢を重ねてきている。
Dartが、Webも含めたクライアントサイドのシングルコードベースでの大統一開発環境になろうという構想が現実味を帯びてきたので、この調子で今度はGCPの公式サポートが充実してほしいし、FirebaseやgRPCのDart公式サポートなどその兆しは十分ある。
2018年は、Dartはクライアントサイドにフォーカスすると公式に表明されたが、2019年にはサーバーサイドを含めたユニバーサルな言語となる構想が語られてもおかしくないところまで来ていると思う。
個人としては、Firebase、そしてクライアントもサーバーも普通にDartで書いていこうと思う。ライブラリやプラットフォームのサポートが不足している場合はPythonやGoのお世話になると思うが。GAEとか。
ということで
春にDartのミートアップを予定していて、同志や入門者などDartに興味がある方々と情報交換したいなと思う。 https://dartisans-jp.connpass.com/event/109371/
さまざまな言語を触ってきたが、Dartは、言語仕様が思慮深く選定されつづけ、公式ドキュメントがしっかりしているので迷わず(英語だけど)、ScalaやJavaScriptのような先行言語に見られるような、歴史的な経緯による少なくない量のBad Partsによる混沌もなく、JavaScriptのように環境構築やコアとなるライブラリ選定などにもつまづくことがない。Dart 2になっても、Dart 1時代からの、扱いやすくパフォーマンスが高く信頼できる実用言語という特徴は維持している。したがって、本質的なプログラミングに集中できる。
実務的には、自分が技術選定できる場合は、Webでもネイティブでも環境を問わず、当分はDartが第一の選択肢になるかなという感じがする。
2019年は、エンジニアリング方面では普通にDartでアプリやパッケージを書いたりVMやコンパイラへの理解を深めていくことに集中するとともに、趣味ではSmalltalkを始めいろいろな環境を触ってさまざまな発想を得たい。このブログも実務的な内容を増やしていこうと思う。
AngularDartの魅力
Flutter経由でDartの魅力に気づいた人が増えてきているので、ここでAngularDartにもすこしでも興味を持ってもらって、使い始める人が増えると嬉しいと思い、この記事を書いた。
網羅的な解説をする余裕がないので、覚え書きの質で書きちらす。また、具体的な機能の特徴をコードを交えて解説するものではない。それに関しては、https://webdev.dartlang.org/を参照されたい。
なお、よく知られたTypeScript版のAngularとの比較が多くなるため、ここでは便宜的にそれをAngularTSと呼称して、AngularDartと区別する。
Hummingbird (Flutter Web)が発表されたけど
Hummingbird (Flutter Web)が発表されたが、まだオープンソースにもなっておらず評価するには早すぎるので、これからはAngularDartでなくHummingbirdだと決めつけてしまうのは年単位で気が早い。
短期的には、Webで要求されるユースケースのすべてを代替できるかについては懐疑的。将来もしHummingbirdが成功するならば、時期を見定めて数ヶ月から数年かけてゆっくりと置き換えていけばいいのではないか。
それまでは、BLoCパターンなどでModelをDartで共有して、WebをAngularDartで、ネイティブをFlutterで書くというパターンが有効。プラットフォームに依存したコード以外の、Model、ライブラリと開発基盤は再利用できるのが強み。
AngularDartの略歴
一昔前にAngularTSからフォークした。それまではAngularTSをDartにコンパイルして提供されていたが、よりDartらしくコードを改善し、パフォーマンスと生産性を高めていく目的。Google AdsのWebクライアントで採用されているのが定番の宣伝文句。その他、Youtubeの一部など、Google内部では活発に使われているらしい。
ちなみにさらに歴史を遡ると、AngularJS (Angular 1)のメジャーバージョンアップの構想ためのアイデアの実装としての側面がAngularDart(初代)にはあり、それがAngularJSにバックポートされつつAngular 2の開発が進められ、そのAngular 2ではTS版もDart版もひとつのコードベースで書かれていたが、なんやかんやあって結局AngularはTypeScript版が推されていき、Dart版はフォークして独自のフレームワークになった。
そして、数ヶ月前のDartのversion2.0のリリースによって、Dartの基盤を利用したAngularDartもversion5.0で成熟したとみなせるようになったので、こうして安心して紹介できる。
AngularTSが、Webのドラフト段階のAPIや将来の構想を先取りした巨大なポリフィルとして冒険するエキサイティングな側面があるのに対し、AngularDartは、巨大なミッションクリティカルなアプリで採用されていることもあり、フレームワークの安定性と生産性とパフォーマンスを非常に重視していると感じられる。
AngularTSがWebコミュニティを積極的に巻き込んでGitHub主体でオープンソースで運営している一方、AngularDartはGoogleの内部リポジトリで揉んで決定されたコードがGitHubに同期されてパブリックになる。ただしGitHub Issueなどで意見を募ったりバグ報告やプルリクエストは受け付けてそれがGoogle内部リポジトリに逆に取り込まれたりといったことがまれにある。
エコシステムやコミュニティの広さはAngularTSのほうがはるかに上である。
なぜAngularDartか
Dartで書ける。つまり、Dartのエコシステムに乗ることで、TypeScript (JavaScript) の欠陥や機能不足、乱雑で混沌としたエコシステムやビルド環境に悩まされずにすむ。JavaScriptをハックする必要がない。
- ビルドエンジニアを用意したり、Webpackと格闘したりする必要がない。基本的に、
package:webdev
に用意されているコマンドを叩けばよしなにやってくれる。 - クロスブラウザ対応のためにJSのAPIのポリフィルを導入したりする必要がない。Dartの豊富なライブラリを利用して開発する。デプロイ時はES5にコンパイルしてくれる。
- JavaScriptのライブラリを利用したくなる場合は、TypeScriptと同様にJSと通信するコードを用意する。
package:js
やdart:js
で、d.ts
のようなものを書く感覚。ただし、Dartのエコシステムがあるので、そのようなコードを書いてJSのライブラリを利用したくなることはあまりない。
また、Flutterでモバイルアプリを書くならば、Web版はAngularDartで開発してModelを共有しておけば、Flutter WebやDesktopが成功して安定した際はスムーズに移行できるだろう。
主にAngularTSと比較してのAngularDartの特徴
網羅的な比較をする余裕はないので、覚え書き。
AngularDartはAngularTSの知識の大部分を流用できるので、TS版を知っているならば学習コストが少ないし、逆にDart版を知ればTS版も苦労せずに応用できる。Zone
、ChangeDetection
、大部分のテンプレート構文、Dependency Injection
といった、中核となる概念を共有している。
AngularTSは、フルスタックフレームワークとして、JavaScriptの混沌としたエコシステムからの技術選定をせずにすみ、統一した開発環境を提供していることが大きな強みだ。AngularDartでは、Dart言語の基盤がそのような役目を担っており、AngularDart自体はその基盤の上でのWebのUI層を司る位置づけになっている。普段の開発でJavaScriptを意識することはほとんどない。
TypeScriptが、良くも悪くもJavaScriptに型システムを追加しただけの方言にすぎず、JavaScriptの作法やエコシステムへの依存度が大きいことに比べ、Dartは完全に別の言語であり、シンプルで堅牢な言語仕様、ビルド基盤、充実した標準ライブラリ、独自のエコシステムを構築している。Flutterやサーバーサイドなど、Web以外の適用範囲が非常に広い。ただし、そのエコシステム自体はまだ発展途上なのが現実。
テンプレート構文
開発チームはAngularTSからの知識の再利用ができる点については気を払っており、たとえば*ngFor
もTS版と同じノリで*ngFor="let e of list"
と書いたりする。Dartなのに。フォーク前から存在する構文に破壊的変更が入るのをなるべく控える方針のようだ。
ただ、TS版と異なりDart版はテンプレート構文の種類が絞り込まれている。TS版での構文の増加に追随しなかったとも言える。これは良し悪しがあって、テンプレート構文が便利になればなるほど、テンプレートに本来はModelに書くべきロジックが入り込む誘惑が強くなるし、テンプレートのコンパイラの複雑さやIDEサポートの困難が増したり、学習コストが増えたりコードレビューが面倒になったりする。一方、テンプレート構文が便利になるとたしかに簡単さを感じるので、Webコンポーネント的なコンポーネントでhtml(テンプレート)とスクリプト部分が分離している設計では、ある意味仕方のない進化なのかとも思う。
複雑なテンプレート構文と付き合うプログラマーのためにIDEサポートが充実しているのもTS版の特徴で、普通に補完がきいたりリファクタリングサポートがちゃんと動くのもよい。WebStormかVSCodeを使っているとその恩恵を強く感じる。Dart版もテンプレートを解析してサポートしてくれるが、TS版ほど充実しているわけではなく、ここははっきりとTS版に劣るところだ。しかし、そもそもテンプレートの解析が必要なほどのロジックを持ち込むなという良い制約になっているという解釈もできなくはない。 ここは対象ユーザー層の量と質の違いがはっきりと現れているのではないか。
私的には、テンプレートには最低限現状の種類のものがあればよく、増えてもメンテナンスが面倒になるだけだと思う。TS版で同僚がさまざまなテンプレート構文を駆使してテンプレートの複雑度が増していくのをあまり抑止できなかったのが残念ポイントだった。Pipeも一見簡単便利なのだが、本来はModelに書くべきものがViewに露出してしまっていることがほとんどなので、使用を禁止したいくらいだ。
AngularDartは、機能を増やすよりも、パフォーマンスと安定性、生成するJSのコードサイズの削減を重視して、あまり使われていない機能を整理、削除することが多い。GitHubのAngularDartリポジトリのCHANGELOGをみたらよく分かる。
AngularCLIは?
AngularTSのCLIは、JavaScriptの面倒な部分をだいぶ見ないですむようにしてくれて、生産性の向上に大きく貢献している。AngularDartではその役割はpackage:webdev
が担っており、AngularDartに限らないWeb開発のサポート機能が提供されている。package:webdev
の各コマンドを叩けばそれでよく、それ以上のことをする必要がもしあるならば、Dartのビルドシステムを学習してプラグインを書く必要がある。
dart:html
AngularTSのようなマルチプラットフォーム対応のための抽象化層は、フォークのあとに廃止された。現在はdart:html
をそのまま利用する。パフォーマンスの向上とコンパイル後コードサイズの削減のため。
dart:html
はDOM APIの一部のおかしな挙動(Node, Elementとか)を修正しているので快適。また、XSSを防ぐためのセキュリティ機能を提供しているので安全である。AngularTSにも似た機能はあったはずだが、AngularDartではフレームワークではなくその基盤のdart:html
のAPIを使う。
SSR
抽象化層を廃した代償として、SSR機能がない。そのため、SSRが必須だと判断されたプロジェクトでは選択肢から外れる。 ただし、2019年の環境で、SSRがそのプロジェクトで本当に必要なのかはよく考えたほうがよい。SSRを維持するためのさまざまなコストを支払えるか、PWA+CDNで問題ないのではなど。
https://speakerdeck.com/kazuyaseki/state-of-seo-for-spa-2018
個人的には、SSRは過渡期の技術とみなしていて、もはや積極的に採用することはない。ほぼアンチパターンだとみなしている。
PWA
Find the PWA here: https://t.co/M5Lx8zTOpm
— Iiro Krankka (@koorankka) November 7, 2018
I had my doubts about combining @AngularDart and the already existing Redux logic on the web, but it turns out the performance is quite good, as long as you fine tune it a little. At least Lighthouse seems to like it. pic.twitter.com/YYPIunfeOt
AngularDart製のアプリが、Lighthouseのスコアでほぼすべて満点を取れている。
PWAは、Angularフレームワークに同梱はされず、Dartらしくdart:html
や独自パッケージでサポートしている。
StreamとRx
RxJS
の知識を再利用したい、またはBehaviorSubject
を利用したいならば、RxDart
を採用する。RxDart
は標準ライブラリのStream
を継承して書かれている。
私的な好みでは、Rx
の大量のオペレーター群を見るのにうんざりしているので、BehaviorSubject
以外は、Dartの標準ライブラリのStream
とpackage:async
, package:stream_transform
を組み合わせて使っている、チーム開発ではRxDart
を使うのが無難かもしれない。
AngularDartはAngularTSとは異なり、Rx
もStream
も必須ではない。
HttpClient
AngularTSではHttpClient
はフレームワークの一部扱いだが、AngularDartではpackage:http
を使う。Flutterでも利用できるパッケージ。
Module (NgModule)
AngularTSでは、NgModuleはアーキテクチャ上重大な役目を果たすフルスタックフレームワークの要ともいえるものだが、AngularDartでは、単にProviderを合成してまとめておく薄い機能にすぎず、使わなくとも問題ない。特に困っておらず、逆にTS版ではNgModuleに悩まされることが多かった(個人の経験談)。
Test
package:test
とpackage:mockito
を使う。成熟している。Flutterでもサーバーサイドでも、どのような環境でも利用できる。
AngularTSでは、型システムを生かしていない設計のJavaScriptテストフレームワークのjasmine
を使うか、マイナーだがmockito
のTS版を使うかという選択肢がある上に、Angularの外に目を向けるとさまざまなテストフレームワークがそれぞれそれなりに使われている混沌とした状況だが、Dartではパッケージの選定に迷うことはなく、さらにWebでもネイティブアプリでも普通に同じパッケージを利用できる。
スタイルガイド
AngularTSでは、独自のスタイルガイドが細かく規定されてLintが提供され、AngularCLIもそれを前提に作られている。 AngularDartでは、Dartのpackage conventionとスタイルガイドに従う。AngularTSのスタイルガイドはAngularDartでは通用しない。
AngularTSのスタイルガイドは、フルスタックフレームワークとして、コミュニティ内でスタイルに迷いがでないようになる利点は確かにあるが、そのスタイル自体はあまり良いものではないと個人的には思う。
package:angular_components
package:angular_components
でかなり楽にMaterial Design対応できる。
ほぼ一通りのUIコンポーネントは揃っているが、Flutterほど充実して最新の仕様に追随しているわけではない。
付属のSCSSにMaterial Designのスタイルが一通り定義されているので、その変数を利用してスタイリングしていく。独自にmarginやcolorなどを定義していく必要はない。UIコンポーネントのcolor themeの変更についても、UIコンポーネントごとにSCSSのmixinが提供されているので、それを利用する。
たとえば、'package:angular_components/css/material/material'
や、
'package:angular_components/app_layout/layout'
をimportする。
個別のUIコンポーネントのスタイルのカスタマイズ用SCSSは、
たとえば'package:angular_components/material_button/mixins';
にある。
ちょっと困ったところ
まれに落とし穴に落ちてしまう。
- UnmodifiableListViewを*ngForに渡すとフリーズする。
- async pipeにstream transformerをかましたstreamを渡すと、無限ループする。streamを適当にキャッシュして、ChangeDetectionのたびにいちいちstream transformerをかまさないように注意する必要がある。もしくはasync pipeを使わない。
需要があれば詳細と回避方法を書く。
入門のための学習リソース
- https://webdev.dartlang.org/をひととおりこなす
- https://github.com/dart-lang/angular/tree/master/docを読んでおく
- https://dartisans.jp/のangularチャンネルで質問する
- 私に質問くだされば答えられることは答えます。メンションでもDMでもOK
- Teratailに質問を投げるのもいいのでは(私はまだ解答したことがないけどたまにウォッチしている)
フィードバック
後半になるほど息切れした。抜け、漏れがたくさんあるかもしれない。
フィードバックをいただけると喜んで回答します。
Resultライブラリを使うべきか
あまり知られていないと思うが、実は、package:async
にResultライブラリが存在する。
https://pub.dartlang.org/documentation/async/latest/async/Result-class.html
Result
Resultのソースコードに端的に表現されている。やっていることはこれだけ。
/// Creates a `Result` with the result of calling [computation]. /// /// This generates either a [ValueResult] with the value returned by /// calling `computation`, or an [ErrorResult] with an error thrown by /// the call. factory Result(T computation()) { try { return new ValueResult<T>(computation()); } catch (e, s) { return new ErrorResult(e, s); } }
計算結果、または計算過程でのエラーをキャッチしてResultオブジェクトでくるむ。
final result = Result(() => someAction());
あとは、通常の制御構文を使用する。
if (result.isValue) { final computationResultValue = result.asValue.value; // proceed. } else { result.asError.handle((error) { // error handling. }); }
ExceptionやErrorのハンドリングを網羅しているかどうかを静的に検査できるわけではない。 また、try catchにおけるアンチパターンと同様に、計算を雑にResultでくるんで回復不能なエラーまで握りつぶしてしまわないように注意。
非同期処理をキャッチする
Result.capture
でFutureをcatchできる。
final result = await Result.capture(someFutureAction());
Result.captureAll
でFutureのIterableを、Result.captureStream
でStream
をcaptureできる。
標準のtry - catch - finallyとの使い分け
ほとんどの場合は、標準のtry - catch - finallyを使えばよいだろう。
- Dartの
try
は優秀で、async await
を組み合わせれば同期処理と非同期処理が混在していてもcatchしてくれる。 - プログラマーは
try
での処理に慣れているため、学習コストがない。 Result
は標準ライブラリに含まれていない。
Result
を使えば、以下のメリットがあるだろう。
- コードレビュー時に、
try
よりもResult
のほうがエラーハンドリングの不備が見逃されづらくなる。 - エラーハンドリングのタイミングを遅延させるほうが便利な、特殊な状況がまれにある。
よって、少なくとも、パッケージを作成するときは、パッケージ利用者にResultの使用を強制しないようにデザインするとよい。つまり、ライブラリの公開インターフェースにResultを使用しない。
プライベートなアプリ開発においては、Result
を使用する基準は、開発チームが議論して決めたら良い。
背景に関してのメモ
Dart言語は、評価が定まっていない仕様を軽率に採用してあとから取り除くこともできず、結果として言語仕様やコアなエコシステムが乱雑になる事態を警戒してきたことが観察できる。たとえ、Kotlin、Swift、ScalaやJavaScriptなどの他の言語で良いとされて導入されているアイデアであっても、言語仕様や標準ライブラリへの追加には、極めて慎重で注意深い態度を示してきた。
言語マニアからまれに批判される、パターンマッチングが無いことなどについても、長い議論の末に、極めて意図的に、あえて非採用、またはペンディング扱いになっていることがGitHub Issueやカンファレンス動画などからうかがえる。
そして、Dart 1にはそのような言語仕様や標準ライブラリのマイナーな改善よりもはるかに優先順位が高い事項がたくさんあったが、悲しいことに、その独自の哲学とビジョンはあまり理解されることはなく、推進力を得ることができなかった。
方針変更したDart 2では大きな仕事が一段落したようで、比較的マイナーな事項に関する進化を進めているので、気になる人はdartlang/languageリポジトリをwatchしたらいいんじゃないかと思う。
Flutter 1.0 安定版が発表された (Flutter Live キーノート 概要レポート)
本日Londonで行われているFlutter LiveのキーノートがYoutubeでライブ配信されていたので、キーノートの概要を手短にレポートする。
ちなみに、Flutterのプロダクトマネージャーがキーノートと同内容を公式ブログに寄稿しているので、英語を苦にしなければ原文を読めば内容は詳細に網羅されている。
Flutter 1.0: Google’s Portable UI Toolkit
Flutterとは
Flutterは、美しいUIのアプリをシングルコードベースでクロスプラットフォーム開発できるUIツールキット。古めの端末でも高パフォーマンスで動作する。Stateful hot reloadingの恩恵により、状態を維持したままコードの変更が即時に開発中のアプリに反映される。(SmalltalkのLive programming環境に少しだけ近づいてきた)。ネイティブ環境に依存する機能はプラグインを開発して呼び出す。それ以外はUIも含めてすべてDart言語で書く。したがって、UIのマークアップ用の外部DSLは存在しない。
すでにGoogle内部で、Google AdsのiOS版とAndroid版がFlutterで開発されていることは有名だろう。(ちなみにWeb版はAngularDartで開発されている。)Googleの外でも、特に中国系の企業が積極的にFlutterを使用していて、世界一多くの開発者とユーザーがいるそうだ。
本日のカンファレンスのキーノートで、ついに1.0のリリースがアナウンスされた。めでたい。
サードパーティのエコシステム
サードパーティのエコシステムについて、3つのサービスが紹介された。
Flutter 1.0の新機能
- iOSピクセルパーフェクト対応
- 既存のiOS/AndroidアプリへのFlutter追加機能
- AndroidとiOSのネイティブビューの埋込機能(AndroidViewとUiKitView)
- Dart 2.1対応
モバイルネイティブアプリから真のクロスプラットフォームアプリ開発環境へ
Flutterでデスクトップアプリ
Windows, macOS, Linux, ChromeOSのネイティブアプリ対応が公式に宣伝された。(現在はまだ安定版ではなくearly preview段階)
https://github.com/google/flutter-desktop-embedding
FlutterでWebアプリ (Hummingbird)
なんと、WebアプリもFlutterでシングルコードベースで書いてしまおうという野心的なプロジェクトが発表された。 これについてはすでにレポートを書いたので参照されたい。
Flutterの開発体験デモ
開発中のアプリの状態を維持したまま、リスタートなしにリアルタイムでコードの変更がアプリに反映されている様がうまくデモされているので、開発者は必見のデモになっている。その他、サードパーティのサービスのデモや、Firebase MLKitを使用した、カメラによるアバターとの表情や動作の同期のデモなど。
所感
Dartが、Webも含めたクライアントサイドのシングルコードベースでの大統一開発環境になろうという構想が現実味を帯びてきた。
この調子でGCPの公式サポートが充実してほしいし、FirebaseやgRPCのDart公式サポートなどその兆しは十分ある。2018年は、Dartはクライアントサイドにフォーカスすると公式に表明されたが、2019年にはサーバーサイドを含めたユニバーサルな言語となる構想が語られてもおかしくないところまで来ていると思う。
Hummingbird: FlutterでWebアプリを作る構想
本日のFlutter Liveのキーノートでは、Flutter 1.0 GA化を始め、たくさんのエキサイティングな発表がされたが、その中でも一番の驚きは、このキーノートの最後の、FlutterでWebアプリを開発可能にするという、Hummingbirdの発表だろう。
GAの次の構想として、AndroidとiOSに加えて、Windows, macOS, ChromeOS, ラズベリーパイなどのネイティブ環境での統一したUIツールキットとなる構想が発表されたことは予想の範囲内だが、Webアプリに関してはWebブラウザーという独特の環境で制約が強いため、Webアプリまでを対象にする構想がこの日に明らかにされたことは全く予想外だった。
これによりFlutterは、モバイルアプリ用途を超えた、モバイルOSでもデスクトップOSでもWebブラウザーでもシングルコードベースで動作する、完全にポータブルなUIツールキットと再定義された。
情報源
英語を苦にしないなら、原文を読むほうが早い。原文を読もう。
Google’s Flutter toolkit goes beyond mobile with Project Hummingbird
Hummingbird: Building Flutter for the Web
Hummingbirdはまだオープンソースにもなっていないので、手元で試すことはできない。したがって、この記事では、主に2つ目の記事である、GoogleエンジニアによるHummingbirdの解説について、その要点を以下に挙げる。
要点
- FlutterのDartコードをWeb用にコンパイルする必要がある。
- 特定のプラットフォームに依存したコード以外のサブセットがコンパイル対象。
- コンパイル先のWebテクノロジーのサブセットを選定する。
- シングルコードベースでWebでも動作させたい。
- そのため、ネイティブアプリではC++で提供しているFlutterエンジンを、Web用にHTMLやCSSのサブセット、CanvasといったWebテクノロジーを利用するものに書き直している。
- すでに、すべてのWidgetおよびWidget FrameworkをJavaScriptに問題なくコンパイル可能。
- HTML+CSS+Canvasのアプローチに加え、未来に向けてCSS Paint APIの利用も試みている。
- FlutterからDartライブラリを呼び出すことはもちろん全く問題ない。FlutterからJavaScriptライブラリを呼び出すには、
package:js
とdart:js
を利用する。 - Flutterの安定性とパフォーマンスを重視するため、内部ではCSSのわずかなサブセットのみを使用する。CSSを書くことを避け、ネイティブアプリと同じFlutterコードベースでWebでも動作させる。
- 既存のWebアプリにFlutterの埋め込むソリューションは調査中。iframeとshadow DOMを考えている。
- Custom Elements, Angular Components, React ComponentsなどをFlutterに埋め込むソリューションについても、調査中。これらの非FlutterコンポーネントがFlutterのパフォーマンスと正確性に問題を発生させる可能性があるため、調査を継続している。
- Webの制約により、ネイティブアプリのネイティブの機能を利用したコードも含めてすべてをシングルコードベースにすることは不可能(なので、Web用のプラグイン開発が必要)
現在のステータス
所感
Flutter for Webについて発表がされ、ネイティブで動作していたデモアプリが今度はWebブラウザーでヌルヌルと動作していることに衝撃を受けたものの、直後に第一感として設計上の疑問がふつふつと湧いてきて、Webの独特の制約や要求事項にどうやって応えるつもりなのか、どういう割り切りをしたのか、どこまで移植可能なのか興味が尽きなかったので、さしあたってこの記事にて上記の通り要点のみ翻訳した。
詳細は訳していないので、気になる人は原文を読んだり機械翻訳を使って理解したらいいと思う。
デスクトップOSでは、ソーシャルアイデンティティなどを大多数のユーザーはWebブラウザーに保持しているだろうから、デスクトップでもネイティブアプリだけでなくWeb PWAも選択肢として考慮する必要があるのは明らかだろう。インストーラブルPWAも進んでいるので、デスクトップネイティブアプリかPWAのどちらを優先するかは、開発するアプリの特性をよく考えて検討したら良いし、うまくいけばほとんどのコードをシングルコードベースにできるので、アプリの開発コストがさらに劇的に下がりそうだ。
また、Webアプリを開発していて個人的にもっとも厄介だと感じている点は、CSSの調整コスト、アニメーション、モバイル用のジェスチャーサポートなので、これをFlutter WidgetとEngineで完全に隠蔽して触らずにすむようにしてくれるだけて、最高のDeveloper Experienceになりそうだ。
WebassemblyでなくJavaScriptにコンパイルする点については、まだWebassemblyはまったく成熟していないし、すでに成熟したJavaScriptコンパイラーがあるので、まったく妥当だろうと思う。
AngularDart
Flutter Webが未来だとしても、AngularDartは現在もうある非常に信頼できる技術なので、Flutter Webが安定するまでは第一の選択肢としてありがたく利用させていただくことになる。AngularDartとFlutterでModelのコードを共有しておき、Hummingbirdが安定したらゆっくりと置き換えていけばいいんじゃないかと思う。その期間は、数年はあるのではないだろうか。
Google I/O 2019で続報がありそうなので、ぜひ現地に行きたくなった。行こう。
Flutterでデスクトップアプリを開発
公式ではなくコミュニティベースのソリューションだが、興味深い記事だったので紹介。
https://medium.com/flutter-community/flutter-on-desktop-a-real-competitor-to-electron-4f049ea6b061
以下、上記記事の要点。
- Androidエミュレーターを立ち上げないので、開発速度は更に高速。
- デスクトップアプリなので、当然ウインドウは可変。まるでWebブラウザのような感覚のレスポンシブデザイン。もちろんウインドウの枠をドラッグで自由に変更できる。
- Hot reloadingとdebuggingも従来どおり可能。
- RAMの使用量に圧倒的な差。エミュレーターは1GB使うのに対し、こちらは100MB。
- Androidエミュレーターでもデスクトップのウインドウでも動くChatデモの動画あり。
- ホバーやカーソルの種類の変化など、マウス前提のデスクトップアプリに必要なWidgetを用意。
- ほとんどの既存のWidgetはすでにデスクトップでも利用可能はユニバーサルなもの。
TargetPlatform
でページのデザインを分岐。レスポンシブデザインのbreak pointが少なくともモバイルとデスクトップ用で用意されているようなもの。CursorWidget
などのデスクトップ用Widgetは、モバイルでは単に無視される。- デスクトップ用レイアウトとモバイル用レイアウトの切り替えは、
PageLayoutWidget
で行う。 - Pluginは、デスクトップもサポートしているものならばそのまま使える。
さっそく試そうと思ったが、コードは近日中 (soon) に公開されるとのことなので、期待して待ちたい。
Fuchsia OSですでにFlutterが動いているはずだし、デスクトップアプリ開発も公式に対応を研究していたはずなので、アプリはすべてデスクトップも含めてFlutterで書いてしまって、WebはSPA PWAでなくペライチのランディングページで済ますという方向性の開発も予想される。
Webブラウザで動かすFlutter for WebもGooglerの個人プロジェクトベースで模索されていて、WebのUIなどの要求事項は独特なのでなかなかWidgetの再利用は難しいはずだが、すでにあるAngularDartという信頼できるソリューションに加えて、もしFlutterも選択肢に加わると、かなり贅沢なクライアントアプリ開発環境になるだろう。感慨深い。