トップ «前の日記(2018-06-08) 最新 次の日記(2018-06-15)» 編集

2018-06-11 [長年日記]

_ Vue.jsチョット書いたのでふりかえり

ちまちま各論のメモは起こしてあるけど、ざっくり全体のメモがなかったので、自分が現時点で意識していることをまとめてみた。

Vue.jsのメリット

Single File Componentが標準で扱える

Single File Component がすべての問題を解決するとは思っていないのだけれど、まず自分は

  • jQuery 的な DOM 要素とそのイベント中心の書き方
  • Unobstrusive JavaScript

の組み合わせは「操作」が「要素」と密結合しているにも関わらず、コードとしては非常に距離が遠く凝集度が低すぎると考えている。HTML と JavaScript の紐付けは class や ID で行うが、ページをまたいで影響させるのは難易度が高すぎる。またコードがイベント中心かつ操作中心になりすぎてしまい、抽象度が低すぎる。本当に考えるべき状態やロジックまで意識を到達させるのはとても難しい。要するにものすごくちぐはぐなのだ。

この問題を Single File Component は比較的うまく解決していると思う。機能として意味のある HTML 断片をコンポーネントとして切り出し、その隣に実際の機能を記述することができる。また Scoped CSS を書けるのもよい。

これが Vue.js では追加設定なしに書ける。

ある程度オールインワンなので準備が楽

AngularJS (v1) のように世界が閉じすぎておらず、React や Mithril のように単機能すぎない。一応 Babel などのビルドプロセスがなくても動かすことができる。Angular (2+) のように TypeScript を新たに勉強する必要もない。

この準備が複雑すぎないことはとても重要と考えている。少なくとも現在の環境では

  • デザイナーなど必ずしも JavaScript に明るいわけではない人にも扱える
  • Node.js ヘビーな JavaScript の環境構築に詳しくない人でも扱える

ことを重視している。

環境構築は目的ではなく手段。手段の習得がヘビーすぎるのは完全に本末転倒と言ってよい。準備が楽であればあるほど新しく参加するメンバーの初期コストが下がる。それは特に小さく固定的でないチームにとって重要である。

必要な用語が少ない

正確な理解とより良いコードを書くためには、当然のことながら各機能の意味や役割を表現するために用語を使いこなす必要はあるのだが、さしあたって動くコードを書くためにあまり多くの用語を必要としないのはよいことである。

(※ ただし、準備が楽で必要な用語が少ないからと言って必ずしも学習コストが小さいとは言い切れないと思っているので、このような表現になっている。)

ある程度流行っている

個人的には、JavaScript を利用したシンプルな ViewModel フレームワークとしては Mithril が好きなのだけれど、いかんせん CSS フレームワーク、UI フレームワークと組み合わせるとか、単純にノウハウなどの流通において React, Vue.js, Angular に大きく水を開けられてしまっているのは事実である。

こうなると他のツールと同じことを再現しようとした時にはややコストの大きな環境になってしまいやすい。Mithril 自体の準備はそんなに大変じゃないけど、デザイナーが手を加えていくプロセスにおいてゼロベースのコーディングの量が増えてしまいやすかったり、何か意図通りに動かず困った事態に陥っても、とにかく自分で試行錯誤する以外の解決方法が見つからない可能性もある。

これがライブラリのアップデートに煩わされず、細かいデザイン要件のない業務向けなら CSS の優先度を下げて妥協するといった判断もできるが、Consumer 向けの Web を扱おうと思うとさすがにそういうわけにはいかないだろう。

Vue.js なら十分に流行っているのでそのような心配はほとんどないと言ってよい。安心してググれるし、情報交換が可能である。

PWAやネイティブアプリへの道もある

これらはさすがに標準対応じゃないけど、道があるという事実だけメモしておく。

現在のVueとの付き合い方

  • ノーフレームワーク(一部Babelだけアリ)
  • Rails + Webpacker
  • Nuxt(SSR 付き)

をやってみた*1 が、この中でバランス的には Rails + Webpacker で小さなアプリから始めるのがよさそうだと感じている。要するにサーバ側との統合作業も Webpack などの JavaScript 周りのごちゃごちゃとした設定作業も必要にならないもの、である。

逆にサーバ側と絶対に密結合にならないようにするために Nuxt で全部縛っちゃうというのもギプスとしてはアリかなという気がしている。少なくともこれが使いこなせればサーバ側の選択肢は広がる*2。ただし Nuxt は SSR がすごい悩む。最初は SPA モードのみにするのもアリだとは思う。*3

とりあえずサラから Webpack を書くのはやらないと決めている。このこだわりは正解だったと思っている。*4

Vueの扱いで気をつけるべきこと

Vue Componentに過度の期待をしない

Vue Component で扱えること、Vue Component に書けることは限られている。基本的には

  • 変更を監視するデータ
  • (ユーザの入力に対する)イベントハンドラ
  • VM Life Cycle Hook

くらいしかない。あとは変更を監視するのがデータそのものではなくメモ化機能付きの function か、extends 元があるか、くらいだ。

データバインディングはとても便利だ。変更の操作を記述する必要がない。しかしそれ以上は面倒を見てくれない。結局のところ人間の「考える」余地は大きく、設計がダメならコードが必要以上の複雑さを抱えてしまうのは普通のプログラミングと同じである。現実世界の課題は Vue Component というか React が集中しようとした ViewModel なコンポーネントで扱う範囲より広く、より複雑な形で存在している。

Vue Componentの外のコードを活用する

これはアセットバンドラがないとダメなので準備が楽という話と矛盾してしまうが、Vue Component の外にコードが置けることは重要である。

上にも挙げたが、Vue Component の中で function の書ける場所は基本的には computed か methods しかない。ごく単純に言い換えるとデータバインディング用かイベントハンドラ用である。

また Vue Component 同士で function を融通する方法は extends か mixin、要するに mixin である。雑に言うとフラットに function をぶちまけるだけで名前空間的にはぶつかりまくるし、Component から独立した function を融通する方法はないということである。

これはもう function をいい具合に管理することは Component のスコープから外したと考えるしかない。JavaScript が持っている機能は JavaScript の方で解決しましょう、普通に export / import しましょうということだ。Vue Component 以外を export / import すれば普通に class も利用できるし、Component から独立した共通の function を名前の重複を気にせず import することもできる。

VirtualDOMで扱えない世界や一般的なモデリングなどの手法に精通する

結局のところ現実世界の課題を分析し、適切なコードを書くには

  • HTML 5 時代の新しい要素である video や audio, canvas
  • Vue Component の外の Pure JS な世界
  • OOP や DDD などのパラダイム

に精通する必要があると言ってよい。

ViewModel コンポーネントが解決するのはあくまでごく一部なのだ。

Lifecycle Hookは用法、用量を守って

Lifecycle Hook は便利なのでついついいろんなことをさせてしまうが、Lifecycle Hook の中のコードが長くなると

  • function に分割されていないのでコードの意図を読み取りにくい
  • タイミングを制御しきれない場合がある
  • テストコードを書く際の stub out の単位がでかすぎる

といった問題が出てくる。これは歴史の中に学ことができる。昔なつかしい「コンストラクタ頑張りすぎ問題」だ。

Lifecycle Hook の中のコードを整理するためにも Vue Component の外にコードを分割して置くのが恐らくかなり重要なテクニックになると言ってよいと思う。

*1 素振りとしては、素から、vue-cli から、Parcel からも試したが、実プロダクトとしては上の 3 つになっている。

*2 単に API の役割を果たしてくれればコードベースを共有する必要もまったくない。

*3 自分はなんか逃げたくなかったのでやらなかったけど

*4 結局テストコードのために書くことになるんだけど