AngularJS についての所感
Posted:Updated:
AgularJS に対する気持ち
所感といいつつ、主に自分がつらさとして感じていることを書く。所感シリーズとしてはjQueryについての所感 も併せて読みたい。
この学習曲線の中でいうと、たぶん今の自分はVery Cool! の頂点から降りている最中くらいだと思う。そして、マサカリをふりかぶった諸兄にひとつだけ言いたいのは、共感脳を養った方がモテるということだ。
チキンハート的弁解: 以下はAngularJSに関するつらさを述べることに専念するために、美点を挙げていないだけであってAngularJSを全方位的に貶めたり、何かと比べて明確にクソだというような意図はない。
画像はAngularJS: The Best Parts · Anand Mani Sankar からの引用。X軸にあるwww.bennadel.com は AngularJS 大好きさん。
辛1. $scope が使えるようで使えない
$scope のおかげで AngularJS が初見でやさしく便利なライブラリに見えている。Controller や Directive、さらにHTMLからも参照できるデータ空間が、自由自在にデータを共有できるかのような「錯覚」をもたらすことこそ$scope の功罪だろう。
angular.module('app', []).controller('ParentCtrl', function($scope) { $scope.items = [1, 2, 3]; $scope.foo = 'bar'; console.log($scope.foo); // 'bar' }).controller('ChildCtrl', function($scope, $location) { console.log($scope.foo); // 'bar' }).directive('loopItem', function() { return { restrict: 'A', link: function(scope, element, attr) { console.log(scope.foo); // 'bar' } };});どこまでいってもfoo = 'bar' だ。このグローバルっぷりは「window 汚染すんな」が「$scope 汚染すんな」という問題にすり替わってしまっただけのような印象すらある。
ここで$scope というオブジェクトが共有されてるかと思いきやそれは誤りである。あくまで子が親のスコープを継承しているだけである。
{{baz}}{{baz}}
angular.module('app', []).controller('ParentCtrl', function($scope) { $scope.baz = 'qux';}).controller('ChildCtrl', function($scope, $location) { $scope.baz = '✌(՞ټ՞✌)';});これの結果は次のようになる。
qux✌(՞ټ՞✌)
同じオブジェクトを扱っているように思えるが、子のスコープに加えられた変更は親に伝播しない。ここでうっかり$parent を使い始めようとするとアンチパターン一直線になる。
Controller は常に親を継承して新しいスコープを生成する。Directive はscope の設定で Controller のスコープをそのまま使うか、継承して新しいスコープを生成するか、独立した新しいスコープを生成するか選べる。
一見して簡単そうに見える$scope を使いこなすためには、これらの法則を理解した使い分けが必須だ。つらい。
辛2. 飽和する DI と Provider
万能かと思われた$scope と距離を置き始めるのは、AgularJS を使い始めてからさほど時間のかかることではない。Directive は内外のスコープを保護するために Isolated Scope にするだろう。attribute を通したバインディングだってする。
そうすると$scope で雑な共有ができなくなりデータストアや処理の共通化に困るようになる。それを解決するのが Dependency Injection(依存性注入)だ。
DIは強力だ。window も$scope も使わずにProvider を注入して色々なオブジェクトを共有できる。DI を徹底するために$window や$location、$document (なんでjQueryライクオブジェクトなんだ!)もある。
myApp.controller('AcmeCtrl', function($scope, $location, someService, someResource) {DIを使いこなしている感じがして良い。
myApp.controller('AcmeCtrl', function($scope, $location, someService, someResource, dogeService, dogeResource) {うっ...
myApp.controller('AcmeCtrl', function($scope, $location, someService, someResource, dogeService, dogeResource, SOME_CONSTANTS, $window, $compile, popupService) {アアッ。
(極端な例だけど)つらい。
辛3. $scope アクセスの記法が一貫性ない & expression 強力すぎ
binding と interpolate の違いということになるのだろうが、{{ }} の有無のせいでHTMLから$scope の世界にアクセスするときのルールが増えてしまっている。
分かってはいる。分かってはいるんだ。しかし、やはり気になってしまう。
{{item.text}}{{ }} 繋がりで八つ当たりをすると、テンプレートで利用できる expression が高度すぎるのもどうかと思うことがある。HTMLの中にロジックを散らかすのは好きではない。
ng-show などのディレクティブに渡せるのもパラメータではなく expression と言える。ng-show="(!foo && bar) || baz" のような呪文を、紳士協定で禁じなければならないのは面白くない。 つらい。
辛4. digest loop の後ろをとるための $timeout と敗北感
たとえばある配列を<li> としてng-repeat したとして、そのリストのheight を取得するには AngularJS の dirty check が終わったあと、明示的には訪れないいつかを待たなければならない。そのとき手っ取り早いのは次のような方法だろう。
$scope.$watch('items', function() { $timeout(function() { var height = element.height(); });});なんでやねん
構造上、これが仕方無いのも分かるが、いざこういうコードを書かなくてはいけなくなったケースの敗北感たるや凄まじい。つらい。
でもこれ vdom の patch を rAF でアップデートするときも起こりうるし、dom更新後にイベント発行して once するしかないかな
辛5. スマートUI(賢すぎるUI)化する Controller
いわゆる Fat Controller 問題と同種の話が容易に発生する。本当は、Isolated Scope な Directive が疎に協調しあっていれば、少なくとも複数の関心が一箇所に集中することは避けられるはずだ。
であるにも関わらず、Directive がコンポーネント化を奨めてくれているのにも関わらず、Controller は昔みたような冗長なjQuery的なコードの繁殖を促しているという点が、非常にもったいない。どっちやねん。てか Directive にも Controller とかあるしオイオイ。
寡兵に鉄砲を持たせて突撃したいのに、すぐに腔発するような鉄砲を渡されたようなものだ。つらい。
番外1. AngularJS に思いを馳せるのが疲れる
個人の好き嫌いなので番外とする。
チーム内に理性を強制するコストが高いのと、色々ぶっ込みすぎたが故の一貫性の無さが自分にとって耐えがたかったと思われる。何でもできるように見えて、何かと引き換えに何かを失っているように映る。随所において、一貫性や正しさが損なわれてしまっていて、完全でないような印象を受ける。
ググらないと分からないとか、$compile やらrequire: '^ngModel' やら$digest/$apply のような知らないと使いこなせない機能の深淵さは、まぁ必要な学習コストだと思う。必要なコストは払わなければならない。Directive のrestrict の記述がクソなのだって、センスが微妙なだけで実害はなかった。
しかし、つらい。
番外2. 業務的な観点から見たとき
これも運用次第なので番外。
- jQuery書けるよ人材では、Seriveなどを使ったオブジェクトの分掌設計ができずに、スマートUI化が進むので開発効率が悪い。
- プログラム分かるけどフロントの経験が浅い人材では、AngularJSに慣れても潰しが効かないので教育効率が悪い。
1はコードレビューのコストで解決できるが、2はどうしようもない。端的に言えば「jQuery書ける != JavaScript書ける」と同種の問題が顕在化する恐れがある。
全体感としても、便利な機能に対してコードの自由度が高すぎるので、少人数で人員の回転が速いプロジェクトにおいては安全な文化ができあがるよりも早く汚染が進みがちだ。ましてや常に JavaScript スペシャリストだけがアサインされるとは限らない。規約の遵守etcにマネコスをかけられる環境でないと正直つらい。個人の感想です
逆にどこで活かせるかといえば、少数精鋭でAngularJSを使うことの合意が取れている環境か、素早く簡単なプロダクトを作りたいときだろうか。殴り書きによる短期的な生産性は捨てがたいし、そういうのには向いている。
2.0 に期待しよう
ちなみに AngularJS 2.0 では次のような変更があるそうなので、1.x とは別モノと捉えて期待を寄せている。詳しくは見れていないが、ちょうどこの記事でイチャモンをつけたポイントが改善されているような気がする。
自分が今のAngularJSに対して感じている、ある種の「正しくなさ」がメジャーアップデートによって解消されることを望んでいる。それまでは別のプロダクトに寄り道しつつ待ちたい。
- Angular 1.x の controllers と templates を包含する統一されたコンポーネントモデルにより、概念 (concepts) と定型 (boilerplate) を減らして再利用性を高める。
- scope の概念を見直してシンプルでわかりやすくし、コンポーネント間の責任分担を改善。(日本語訳)ng-europe, Angular 1.3, and beyond - AngularJS Ninja
AngularJS がコンポーネント的な考えやデータバインディングという仕組みの認知浸透に与えた功績は小さくない。なんだかんだで、目の前にあるプロダクトはAngularJSなのでしばらく頑張ります(。•̀ᴗ-)✧
追記
この記事が、あまりにも似たようなことを述べている感じだった。おれもこんなオシャレに書きたかった!
Author

ahomuAyumu Sato
KINTOテクノロジーズ株式会社
Related
Latest
Archives
- 2025年1月エントリー数2件
- 2024年8月エントリー数1件
- 2024年7月エントリー数2件
- 2024年6月エントリー数1件
- 2024年3月エントリー数1件
- 2024年2月エントリー数2件
- 2024年1月エントリー数2件
- 2023年11月エントリー数3件
- 2023年9月エントリー数2件
- 2023年7月エントリー数1件
- 2022年1月エントリー数1件
- 2021年8月エントリー数2件
- 2021年6月エントリー数3件
- 2020年9月エントリー数1件
- 2020年7月エントリー数1件
- 2020年4月エントリー数2件
- 2020年3月エントリー数1件
- 2018年12月エントリー数1件
- 2018年9月エントリー数2件
- 2018年5月エントリー数5件
- 2018年4月エントリー数1件
- 2018年3月エントリー数1件
- 2018年2月エントリー数4件
- 2017年11月エントリー数2件
- 2017年7月エントリー数1件
- 2017年6月エントリー数4件
- 2017年5月エントリー数4件
- 2016年12月エントリー数4件
- 2016年11月エントリー数5件
- 2016年9月エントリー数5件
- 2016年8月エントリー数2件
- 2016年7月エントリー数7件
- 2016年6月エントリー数4件
- 2016年5月エントリー数5件
- 2016年3月エントリー数1件
- 2016年2月エントリー数4件
- 2016年1月エントリー数1件
- 2015年12月エントリー数4件
- 2015年10月エントリー数1件
- 2015年9月エントリー数1件
- 2015年7月エントリー数2件
- 2015年6月エントリー数8件
- 2015年5月エントリー数9件
- 2015年4月エントリー数2件
- 2015年3月エントリー数2件
- 2015年2月エントリー数9件
- 2015年1月エントリー数2件
- 2014年12月エントリー数9件
- 2014年11月エントリー数8件
- 2014年10月エントリー数7件
- 2014年9月エントリー数4件
- 2014年8月エントリー数7件
- 2014年7月エントリー数4件
- 2014年6月エントリー数6件
- 2014年5月エントリー数2件
- 2014年4月エントリー数2件
- 2014年2月エントリー数6件
- 2014年1月エントリー数6件
- 2013年12月エントリー数7件
- 2013年10月エントリー数5件
- 2013年9月エントリー数3件
- 2013年8月エントリー数2件
- 2013年7月エントリー数1件
- 2013年6月エントリー数2件
- 2013年4月エントリー数3件
- 2013年3月エントリー数2件
- 2013年2月エントリー数3件
- 2013年1月エントリー数5件
- 2012年12月エントリー数13件
- 2012年11月エントリー数7件
- 2012年10月エントリー数4件
- 2012年9月エントリー数7件
- 2012年8月エントリー数5件
- 2012年7月エントリー数7件
- 2012年6月エントリー数2件
- 2012年5月エントリー数6件
- 2012年4月エントリー数5件
- 2012年3月エントリー数18件
- 2012年2月エントリー数10件
- 2012年1月エントリー数4件
- 2011年12月エントリー数7件
- 2011年11月エントリー数4件
- 2011年10月エントリー数13件
- 2011年9月エントリー数3件
- 2011年8月エントリー数4件
- 2011年7月エントリー数10件
- 2011年6月エントリー数13件
- 2011年5月エントリー数14件
- 2011年4月エントリー数10件
- 2011年3月エントリー数12件
- 2011年2月エントリー数14件
- 2011年1月エントリー数11件
- 2010年12月エントリー数6件
- 2010年11月エントリー数19件
- 2010年10月エントリー数15件
- 2010年9月エントリー数16件
- 2010年8月エントリー数13件
- 2010年7月エントリー数7件
- 2010年6月エントリー数2件
- 2010年5月エントリー数11件
Tags
- #a-blogcmsエントリー数39件
- #AdventCalendarエントリー数13件
- #Analyticsエントリー数5件
- #Androidエントリー数8件
- #apacheエントリー数8件
- #APIエントリー数10件
- #Appエントリー数9件
- #Backboneエントリー数7件
- #CentOSエントリー数8件
- #CMSエントリー数6件
- #CoffeeScriptエントリー数5件
- #CSSエントリー数14件
- #CSS3エントリー数9件
- #CSSNiteエントリー数5件
- #ES6エントリー数5件
- #Firefoxエントリー数8件
- #Gitエントリー数6件
- #Googleエントリー数12件
- #Gruntエントリー数9件
- #HTMLエントリー数5件
- #HTML5エントリー数14件
- #HTTPエントリー数6件
- #InternetExplorerエントリー数5件
- #iOSエントリー数9件
- #iPadエントリー数11件
- #iPhoneエントリー数11件
- #JavaScriptエントリー数94件
- #jQueryエントリー数35件
- #Macエントリー数18件
- #Mobileエントリー数18件
- #nginxエントリー数6件
- #nodeJSエントリー数7件
- #npmエントリー数6件
- #phpエントリー数39件
- #PhpStormエントリー数7件
- #Polymerエントリー数8件
- #Reactエントリー数6件
- #Rubyエントリー数7件
- #Twitterエントリー数12件
- #WCANエントリー数12件
- #WEB+DB_PRESSエントリー数6件
- #WebComponentsエントリー数9件
- #Windows7エントリー数7件
- #Wordpressエントリー数6件
- #おいしいエントリー数33件
- #おしらせエントリー数5件
- #さくらのVPSエントリー数15件
- #アクセシビリティエントリー数9件
- #イベントエントリー数23件
- #パフォーマンスエントリー数20件
- #フロントエンドエントリー数5件
- #モジュールエントリー数7件
- #モブログエントリー数5件
- #レシピエントリー数11件
- #就職活動エントリー数5件
- #屋久島エントリー数8件
- #旅行エントリー数46件
- #温泉エントリー数9件
- #読書感想文エントリー数13件
- #酒エントリー数6件
- #鹿児島エントリー数5件
