相談内容

  • 既存の管理ツールを新しく作り直すために新しいJSフレームワーク/言語使いたいのですが、何を選んだらよいでしょうか?
    • ここで選んだものは今後新しく作る時にも使用していく予定のため、学習コストよりメンテナンスしやすいものを選びたいと考えています。
    • 利用者は社内外で特定の権限を持った人のみであるため、サーバサイドレンダリングはしない予定です。
  • 言語は型があるものを利用したいのですが、TypeScriptとFlowのどちらがよろしいでしょうか?
  • 時間に余裕があれば、テストフレームワークやビルドツールについてもお聞きしたいです。

現在のページ/チーム

  • jQueryなどで書かれている部分が多いですが、変更を加えることが難しくメンテナンスコストが高いです。
  • サーバサイドをやってる人が片手間で書くJavaScriptといった状況です。
  • 今回新規で数ページを追加する必要があるため、何を利用すれば良いかを検討しています。

〜正確な要件の確認、何を必要としているのかを既存のツールを見ながら確認、どれぐらいの頻度で更新されるものかの確認、主に更新する部分/人は誰なのかを確認〜


回答

具体的にどれを選ぶべきかは環境によって異なるので、この記事からは省いています。

フロントエンドのスタック

フロントエンドの技術的なスタックには幾つかのカテゴリがあり、
その中で何を使うかという話になっていくことが多いです。
もちろんAngularやEmberのようにカテゴリをまたいだものを持つ本物のフレームワークも存在します。

フロントエンドとカテゴリの例

次のカテゴリは一例ですが、アプリケーションによって必要なカテゴリも異なります。そのためここで出しているものはただの例だと考えてください。
例えば、管理ツールのような一般ユーザーが使うものではなければ、パフォーマンスという機能についてのカテゴリは不要になる場合があります。(もちろんそれもあると良いですが、必要なカテゴリはアプリケーションによって異なるというハナシです)

フロントエンドのカテゴリ

できるだけカテゴリの中で何を使うか、またそのカテゴリは必要ないかを決定していくことが大事です。なぜなら、複数のカテゴリを同時に扱えるツール/ライブラリを不用意に選ぶと長期的なメンテナンスが難しくなる傾向があります。

複数のカテゴリを同時に扱えるツールのメンテナンスが難しくなる理由としては、破壊的な変更が多くなることと、そのツール作者側のメンテナンスコストの問題で放置されることです。複数のカテゴリを持つツールは、複数の進化に応じた掛け算の対応を求められるため、破壊的な変更を含む可能性が高まります。また、ツール/ライブラリ作者にも体力がないとメンテンスできなくなるためです。

特にwebpackのようなツールは同時に複数のカテゴリを扱います。できるだけシンプルに保つか、効率のために受け入れるかはケースバイケースです。その際には競合となるツールがどこまで扱えているかみて、過度にカテゴリをはみ出ていないかをチェックしてみてください。

Task Runner

Task Runnerはツールを叩くためのインターフェースです。
単純にコマンドラインツールを叩くだけならnpm-run-scriptで直接コマンドを叩くだけで問題ないです。

複数のコマンドやツールなどを上手く組み合わせた結果が必要になる場合は、gulpやMakeなどを使います。
例えば、ファイルを監視しておいて変更があったらビルドして、ローカルサーバを更新して...のようなタスクの組み合わせる領域ではgulpなどのツールが便利です。
このTask Runnerという領域で複雑な実装を書くとメンテナンスコストが高まるので、できるだけシンプルに維持することを考えるのが重要です。

どのTask Runnerを使った場合も、Node.jsが必要となるプロジェクトではpackage.jsonがあり、script fieldに実行できるタスクが書かれているのが基本となります。

そのため、最終的に npm run build などのコマンドで処理を開始できるように書く必要があります。
実行する側は、npm run xxxで何ができるということだけが決まっていれば、その実行の中身はいつでも書き換えることができます。中身はいつでも捨てられるようにすることが長期的なメンテナンスコストを小さくします。

Bundler

JavaScriptのコードはモジュールに分けて開発することが一般的です。
ブラウザにおいて、1つずつ<script>タグでモジュールをロードするのは、依存関係、ラウンドトリップ、コネクションの問題が発生します。
そのため、多くの場合ブラウザ向けのコードはモジュールをまとめた(bundleした)ファイルを作成して配信します(ES module + HTTP/2である程度は解消できますが、それもメリット・デメリットがあります)。逆を言えばモジュールの仕組みが備わっているNode.jsアプリケーションにはbundleは必要ありません。

その際にモジュールの依存関係などを解決しながらファイルをまとめる"bundler"と呼ばれるツールとしてwebpackBrowserifyがあります。

現代の複雑化したスタックにおいてはwebpackが使わますが、webpackはすべてのカテゴリの中で最も複雑です。なぜなら、複数のカテゴリを扱うこともでき、すべてのカテゴリとも連携することができるからです。

そのため、最初に手を出すときは、フレームワークが提供しているstarter kitやcliを使って、小さな設定から進めることが推奨されます。

Vueならvue-cliを使ってシンプルに動かすところから始めるのが良いです。

npm i -g vue-cli
vue-cli init webpack-simple

Reactならcreate-react-appですが、こちらは結構フルスタックなので注意が必要です。(とてもよくできてはいますが、READMEにかかれているように様々なツールが最初から含まれています)

Angularならangular-cliを利用します。

まず重要なことはwebpackでbundleしたファイルを吐き出せるようになることです。
ファイルが吐き出せれば後はHTMLから読み込めば動きはじめます。
開発サーバとの連携やHot Reloading、最適化などはその後に考えるべきことで、
動くまでのサイクルをとても長くするので問題となる可能性があります。

まずは、JavaScriptのフレームワークが用意しているシンプルなスターターキットから開始するのが良いと思います。

補足: Rollupはライブラリのためのbundlerという側面があるので、アプリケーション向けのbundlerならwebpackなどを利用した方が良いです。

JavaScript Language

JavaScriptの仕様は毎年更新されており、新しい機能や構文(syntax)が毎年増えています。
一方、ブラウザで動かすJavaScriptの場合、IEなど古いブラウザではその新しい構文を使えない問題があります。

そのため、別言語(AltJSと呼ばれる)で書いたソースコードをJavaScriptに変換するトランスパイラというツールを使うのが一般的です。ES2015+の構文を変換するだけの用途ではBabel、型が扱えるAltJsとしてはTypeScriptとFlowが使われます。

  • JavaScript
    • 何もしないで扱う
  • ES2015+ (JavaScript -> JavaScript)
  • TypeScript
  • Flow
    • 変換自体はBabel、チェックはOCaml製の型チェックツールを使います

CSS

CSSもJavaScriptと同じ理由でトランスパイラを使うケースがあります。
postcssSassがよく使われています。

CSSはJavaScriptと違って仕様に足りない機能が多いため、独自仕様の拡張が発達しています。
SassはCSSとは異なるSassという別言語です。
PostCSSはプラグインでCSSを拡張する形となるため、追加される機能は自分で決定できます。

PostCSSのcssnextはCSSの新しい仕様の先行実装を取り入れていますが、
安定した仕様ではないものも含まれているので仕様から消えてしまう拡張なども含まれています。
Autoprefixerは入れたほうが良いですが、後は好みの問題となってしまいやすいです。

JavaScript View

React、Vue、Angularを選択肢とすることが多いですが、選択肢が最も多いカテゴリです。
また、ライブラリ/フレームワークは型付き言語(AltJs)との相性もあります。

  • React
    • TypeScript、Flowどちらも使われている
  • Vue
    • TypeScriptが一部で使われてる
    • 一部型推論が効かない部分がある
  • Angular
    • 公式にTypeScriptサポート

Angularはフレームワークなので、その他とは別の比較軸となることが多いです。

Vue or React

次のように言われることがありますが、どちらも基本的には似たことはできます。

  • Vue
    • 1つのコンポーネントが大きいまま書いていける
  • React
    • コンポーネント分割が推奨される

これは技術的な違いではなく、考え方の違いなので上記のような制約があるわけではありません。
Vueにおいても小さくコンポーネントは分割できるし、Reactにおいても大きなコンポーネントを作ることはできます。
Reactがコンポーネントを小さく作ることを推奨しているだけで、技術的な制約が存在してるわけではないです。
色んなものを無視して単純に言えば、Vueはコードを書きやすく作られていて、Reactはコードを読みやすく作られています。

Vueはとりあえずで始めることが簡単なので、学習曲線はゆるやか。
トップダウン的なアプローチが取りやすい。しっかり設計していくと最終的にReactと似たような形になる。
公式のドキュメントや公式のルーターライブラリなど必要なものが公式にまとまっています。

Reactはある程度ちゃんと設計しないといけなかったり、小さなコンポーネントから作り始めることが多いです。ボトムアップ的なアプローチがとりやすい。広く使われているため、ドキュメント、ツール、ライブラリ自体がVueに比べると充実している一方、量的に迷いやすいです。公式のcreate-react-appによって初期に必要なツールが多い問題はある程度解消されてきています。

JavaScript ステート管理

VueやAngularを選んだ場合は公式の提案する方法に則ればいいのではずです。
一方、Reactはステート管理を行うライブラリが様々なので、この部分の検討が他のViewのライブラリを選ぶより難しいです。

Reactの場合はReduxがよく一緒に使われているようです。
ただReduxを選んだ場合もどのmiddlewareを使うかという別の問題が発生します。

ここでの良い戦略は、求める要件を小さくしたプロトタイプを実際に色々な選択肢で作成してみることです。
実際にサンプルを作ることで、使い勝手を確かめたり作成したコードを元に議論することができます。

アプリケーションの設計に関わる部分は机上の空論や議論だけでは上手く進まないことがあります。小さなプロトタイプを作り、実際に存在するものをベースにすることで議論が進みやすくなります。
過度に簡略化したプロトタイプにするとイメージが先行してしまいますが、要件において重要な点を軽く含めたものをベースに考えると良いです。

たとえば、アンケートフォームのようなものを考えるならば、幾つかの入力項目があり、簡単なバリデーションができ、その結果を擬似的に送信することができるといった内容を考えてみるといいかもしれません。

フレームワーク選定はフレームワークを決めるプロセスでしかないので、実際に作成するアプリケーションが何かを見失わないようにしてください。
一度選んだフレームワークにも寿命は存在するので、継続的にアクティブなのか?作成しているアプリケーションに適切なのか?を確認してください。

ユニットテスト

ユニットテストフレームワークはMocha、Jest、AVA、Jasmineがよく使われています。
JestとAVAはブラウザで動かすのが難しいので注意が必要です。
(jsdomを使ってブラウザをシミュレートして使うことが多い)

安定のMochaかJestを選択すれば大体のケースでは良いはずです。
ライブラリではMochaが多く、アプリケーションではMochaかJestを選ぶ事が多いです。

Jestは多機能でCLIはよくできていますが、カテゴリをまたぐ機能も多く含んでいます。
JestはMock APIなども含んでいます。できれば別のモックライブラリを使い本体のモックAPIを避けたほうが、万が一の際に移行しやすくなります。

JestはMochaとは違い、設定としてトランスパイルの仕組みをサポートしています。TypeScriptのコードでテストする場合はts-jestがよくできているため、JestとTypeScriptを使う場合こちらを利用するのが簡単です。

Mochaで機能が不十分な場合はJestを検討してみると良いですが、Jestは多機能かつ暗黙的な機能も含んでいるためできるだけシンプルに使うことを推奨します。

パフォーマンスについては、作成するテストケースによって異なるので大量のテストケースを作る方針でなければあまり気にしなくても良いはずです。

Test Runner(オプショナル)

基本的にはテストフレームワークと一緒に使われています。
大体のケースではブラウザで自動的にユニットテストを動かすために利用されることが多いです。
Karmaの人気は高いですが、新しいものがあまりでてきてない分野でもあります。
(jsdomが正確になった/ReactのようなDOM APIに依存しなくてもテストできるライブラリが増えたので、それで満足した人が増えた印象)

ブラウザ特有の機能をテストしたい場合はTest Runnerを検討した方がメンテナンス性はよくなります。
Canvas、Media要素など。

UI テスト(オプショナル)

UIが崩れていないかを確認するテスト。
storybookはReactとVueをサポートしている。
コンポーネント単位でのモック的なプレビューが可能になるので、デザインシステムの一部として必要な場合は検討する。
たとえば、デザイナーは実際の開発環境ではなくStorybookでのモックだけで開発できるという分業のしやすさを導入したい場合など。

reg-suitはスナップショットテストのスクリーンショット版です。

E2Eテスト(オプショナル)

E2Eテストは定番となるものがありません。
無理にJavaScriptのレイヤーでやる必要はないので、好きな言語のフレームワークを使うことも検討する。

まとめ

  • アプリケーションの要件によって向いてるフレームワークは異なる
  • 気になるJavaScriptのフレームワークで要件を小さくしたプロトタイプを作って試し、それを元に議論する
  • その他のツールはフレームワークが決まってからでも遅くはない
  • 実際に要件を小さくしたプロトタイプを作ることで、bundlerや言語といったものも触るので合わせて検討できる
  • フレームワーク選定はフレームワークを決めるためのフレームワークにすぎない
  • アプリケーションとフレームワークの寿命は異なるので、実際にフレームワークが決まった後も継続的に確認する
  • 各種カテゴリを大きく突き抜けたものではなければ、そのカテゴリ内で入れ替える事ができアプリケーションの寿命を伸ばしやすくなる

参考


〜実際のアプリケーションに基づいた設計やプロジェクト構造の議論に続く〜

実際に何を使うはその環境(ヒト/モノ/コト/トキ)によって異なるので、この記事では触れていません。