昨今、ReactやVue.jsのような素晴らしいWebフロントエンドフレームワークのおかげで、モダンWebアプリを効率的に作ることができるようになりました。しかし2022年現在多くのフレームワークがあり、Webアプリ開発を始めたばかりの人にとってはどれから始めればいいのか選ぶのに迷ってしまうのではないでしょうか?
そのような方のために、この記事では、数あるフレームワークのうち、どれから始めればいいのか判断材料を提案したいと思います。判断材料としてフレームワークの歴史、特徴、現状を解説します。そして、それらを踏まえた上で、どのフレームワークから始めるべきか考察を説明します。
非常に長い記事になりますが、もし興味がありましたらご覧ください。
この記事の対象者
この記事はWebアプリ開発の勉強を初めたばかりで、ReactやVue.jsなどのフレームワークのどれを選ぼうか迷っている方を対象とします。
またHTML, CSS, JavaScriptの中級者レベルであることを前提としたサンプルコードを記載しています。具体的にはMDNのJavaScript チュートリアル中級編を理解することができるレベルを想定しています。
用語の定義
Webフロントエンドフレームワークの定義
この記事では「Webフロントエンドフレームワーク」のことを「モダンWebアプリを作るためにJavaScript開発を支援する仕組み」と定義します。
なお、この後に登場するReactやSvelteは一般的な「フレームワーク」の定義から少しずれますが、この記事では同様の説明対象としています。
モダンWebアプリ
この記事では「モダンWebアプリ」のことを「ユーザ操作に合わせてWebページの内容が高速に変化する特徴を持つWebアプリ」と定義します。
例として多くのサイトで実装されている「ハンバーガーボタン」を取り上げます。ハンバーガーボタンとは、水平線が三つ垂直に並んだ形状をしており、これを押すことでメニューが表示されるUIコンポーネントのことです。
このハンバーガーボタンに対し、モダンWebアプリである場合と、モダンWebアプリではない場合で比較すると以下の様になります。
パターン | 概要 | 速度 | アニメ | 更新範囲 |
---|---|---|---|---|
モダン | メニューのみをアニメーション表示 | 速い | あり | 部分的 |
非 モダン | ページ全体を再読込後、メニュー表示 | 遅い | なし | 全体的 |
モダンWebアプリのハンバーガーボタンの実装例はこちらです。ボタンを押すとページ全体が再読み込みされることなく、左からメニュー領域が現れます。
モダンWebアプリの長所の例は以下の通りです。
- ユーザ操作後の待ち時間が短い
- 次に操作すべき場所が分かりやすい
今日ではユーザ体験を向上するために、モダンWebアプリの実装を目的としたフレームワークの重要性が増しています。
※一般的には「モダンWebアプリ」という言葉に対して、ここで説明した内容以上の事が期待されていると思いますが、当記事では説明を簡略化するため、あえて限定的な定義にしています。
解説対象のフレームワーク
当記事では、数あるWebフロントエンドフレームワークの中から特に注目されている三つのフレームワークを対象とします。
対象プロダクト | 読み方 |
---|---|
React | リアクト |
Vue.js | ビュージェイエス |
Svelte | スヴェルト |
※ Vue.jsはVue 2からVue 3にかけて大きく変化していますが、当記事がこれからフレームワークを選ぶ方向けである以上、より新しいバージョンを説明する方が望ましいと思いますのでVue 3を対象としています。
また、解説対象ではありませんが、フレームワークの歴史を説明する上で重要な以下のプロダクトも簡単に紹介します。
紹介プロダクト | 読み方 |
---|---|
Adobe Flex | アドビフレックス |
jQuery | ジェイクエリー |
AngularJS | アンギュラージェイエス |
TypeScript | タイプスクリプト |
Redux | リダックス |
Vuex | ビューエックス |
Next.js | ネクストジェイエス |
Nuxt.js | ナクストジェイエス |
SvelteKit | スヴェルトキット |
なお、他にもSolidJS(ソリッドジェイエス)やAlpine.js(アルパインジェイエス) 等の素晴らしいフレームワークがありますが、
これ以上対象に含めると記事が非常に長くなってしまうため、今回は外させていただきました。
フレームワークの歴史
Web フロントエンドフレームワークの現状がどうなっているか理解するために、これまでどのような歴史を辿ってきたか、簡単に紹介します。
なお歴史は様々な見方をすることができますが、今回はプログラミングパラダイムの変遷に注目しています。パラダイムとは「考え方」「やり方」のことです。
Adobe Flex (2004)
Adobe Flexは2004 年に登場したモダンWebアプリを作るための開発環境です。
Adobe FlexはモダンWebアプリを開発するという点ではjQuery以降のフレームワークと共通の目的を持ちますが、以下の表のとおり実行環境が大きく異なります。
フレームワーク | 実行環境 |
---|---|
Adobe Flex | Adobe Flash |
jQuery 以降 | JavaScript |
Adobe Flexで作られるアプリは当時の水準で見ると先進的な外観をしており、高速に画面遷移を行うことができました。当時のHTML, CSS, JavaScriptを使用した平均的なWebアプリと比較して高いユーザ体験を実現しており、Rich Internet Application (RIA)と呼ばれていました。
以下の画像はFlexの公式サイトに掲載されている、Flex製アプリのスクリーンショットです。このアプリは ArenaCore Pty Ltd が開発した Flex 用ツールキットのデモアプリです。
※この画像は https://flex.apache.org/community-showcase.html から引用しました。
Adobe Flexの見た目や動作のインパクトから人気を博していましたが、AngularJSを始めとする JavaScript ベースのフレームワークの台頭や、Adobe Flash のサポート終了もあり、多くのFlex製アプリは他のフレームワークによって置き換えられました。
jQuery (2006)
jQueryは2006 年に登場したWebアプリ開発用ライブラリです。
jQueryの主な機能はドキュメントオブジェクトモデル (DOM) (ドム)を少ないコードで操作する機能です。この機能により、モダンWebアプリの開発が加速しました。
DOMとはHTMLと始めとする文書をツリー型のデータ構造で表現したものです。JavaScriptには標準でDOMを操作する機能があり、これにより Webページを部分的に書き換えることができます。ただし当時の標準機能は必要最低限のものであるため、煩雑なプログラミングが必要でした。
DOMを操作してメニューの表示・非表示を切り替えるコードをjQueryを使わない場合と使う場合で実装してみました。コードは以下の通りです。
jQueryを使わない場合:
<div id="root">
<nav></nav>
<button class="ham">
<i class="material-icons">dehaze</i>
</button>
</div>
<script>
document
.getElementsByClassName("ham")[0]
.addEventListener("click", function () {
var e = document.getElementById("root");
if (e.classList.contains("opened")) {
e.classList.remove("opened");
} else {
e.classList.add("opened");
}
});
</script>
jQueryを使う場合:
<div id="root">
<nav></nav>
<button class="ham">
<i class="material-icons">dehaze</i>
</button>
</div>
<script>
$(".ham").on("click", function () {
$("#root").toggleClass("opened");
});
</script>
jQueryを使う方がDOM操作周りのコードを短縮できていることが分かるかと思います。
しかしjQueryがあったとしても、複雑な要件のもとでは以下の課題が挙がりました。
- 複雑な条件分岐がある場合、状態とDOMの整合性を取ることが難しくなる
- 要素を指定するためにIDやクラス名を設計する必要があるが、DOMが肥大化すると重複を考慮する必要がある
この問題は宣言型プログラミングの台頭まで続くことになります。
AngularJS (2010)
AngularJSは2010 年に登場したWebフロントエンドフレームワークです。
AngularJSの登場はWebアプリ開発の大きな転換点となりました。この転換点とは、HTMLを拡張した宣言型プログラミングと、JavaScriptによる手続き型プログラミングを組み合わせて行う方式の事です。
宣言型プログラミング
宣言型プログラミングとは、実現したいことの過程 (手続き) を一つ一つ書くのではなく、実現したいことの結果を記述する方式です。例えばMicrosoft Excelの場合、セルの数式が宣言型、VBAスクリプトが手続き型になります。
AngularJS以降のフレームワークはHTMLを拡張した宣言型プログラミングが導入されています。HTMLに表示したい変数や簡単な条件分岐を書いておけば、変数の値を書き換えるだけで Webページを部分的かつ高速に更新することができます。
参考までにjQueryによるほぼ手続き型のコードと、Reactによる宣言型のコードをお見せします。これらのコードはボタンを押すたびにメッセージの表示・非表示を切り替えるものです。
jQueryによる手続き型DOM操作:
<div>
<button>Button</button>
<div id="message"></div>
</div>
<script>
let message = false;
$("button").on("click", () => {
if (message) {
$("#message").empty(); // DOM 要素を削除します。
message = false;
} else {
const m = $("<div>Message</div>");
$("#message").append(m); // DOM 要素を追加します。
message = true;
}
});
</script>
Reactによる宣言型DOM操作:
const App = () => {
const [message, setMessage] = React.useState(false);
const click = () => setMessage((m) => !m);
// 宣言型プログラミング開始
return (
<div>
<button onClick={click}>Button</button>
{message ? <div>Message</div> : null}
</div>
);
};
Reactによる宣言型プログラミングの方が、DOMの最終系をイメージしやすいことがお判りでしょうか?このHTMLを拡張する方針はAngularJS、React、Vue.js、Svelteで共通しています。
肝心のDOM操作は、状態の変化 (変数の値の変化) をフレームワークが検知して自動的に行います。これにより複雑なロジックと DOM 操作を切り離すことに成功しています。
AngularJS の公式記事GetAngular (Web アーカイブ)によると、手続き型と宣言型を組み合わせる場合、以下のように責務を分けることができると書かれています。
型 | 責務 | 作業担当 |
---|---|---|
手続き型 | ビジネス要件どおりに変数を書き換えること | Web開発者 |
宣言型 | 変数の値に応じて適切な Web ページを表示すること | Webデザイナー |
TypeScript (2012)
TypeScriptは2012年頃に登場した、JavaScriptの派生プログラミング言語です。
TypeScriptの主な役割はJavaScriptにデータ型のドキュメントを付け、正しいデータ型を指定しているかどうか検証すること です。これにより、引数等のデータ型を間違えることによって生じる不具合を低減できます。
JavaScript の派生言語はこれまで数多く登場しましたが、2022年8月時点ではTypeScriptが多くの需要を占めています。参考までにnpm trendsにて生成した TypeScript および競合するプログラミング言語のダウンロード数チャートをご覧ください。
※ 上記の図は https://npmtrends.com/coffeescript-vs-flow-bin-vs-purescript-vs-typescript から引用しました。
フレームワークのTypeScriptへの対応度合いは、フレームワーク選択の大きな指標になっています。対応が進んでいるほど、大規模な Web アプリでも品質を確保しやすくなります。
React (2013)
Reactは2013 年に登場したWebフロントエンドライブラリです。
ReactとAngularJSは、モダンWebアプリを作ることが目的という点で共通しています。また、手続き型と宣言型を組み合わせたプログラミングパラダイムである点も共通しています。
ReactとAngularJSの大きな違いとして、Reactはライブラリであり、AngularJSはフレームワークであるという点です。AngularJSは認証やデータベースの機能も有していますが、ReactはUIを作る機能のみを持っています。Reactの公式ブログにも、フレームワークではなくUIコンポーネントの作成を促進するライブラリであると明言されています。
そのため、Reactで一般的なWebアプリを作るには他のライブラリやフレームワークと組み合わせる必要があります。組み合わせを考える手間はかかりますが、基盤を自由に選べる柔軟性があるとも言えます。
ReactはDOM操作の仕組みとして仮想DOM (Virtual DOM)という仕組みを導入しました。この仕組みにより、効率的に状態変化の差分を検出してDOM操作を行うことができるようになりました。
DOM操作方式の違いはフロントエンドの動作速度や消費メモリに影響するためフレームワーク選択の検討項目の一つになっています。例えば Angular 2ではIncremental DOMという仕組みが導入されています。
Vue.js (2013)
Vue.jsは2013年に登場したWebフロントエンドフレームワークです。
Vue.jsとReactは初期リリース時期が近く似た機能を持つため、よく比較される関係にあります。例えばDOM操作方式が仮想DOMである点が共通しています。
開発者のEvan You氏に対するインタビューによると「AngularJSから気に入った部分を抽出し、余分な概念を使わず軽量化したらどうなるだろうか」という動機があったようです。
また React と比較して「HTML, CSS, JavaScriptに慣れ親しんだ人ができるだけ早く習得できるようにしている」とも語っています。
Vue.jsはプログレッシブフレームワークと言われています。Webアプリ開発に必要な多くのサブコンポーネントを提供していますが、コアコンポーネントに強力に紐づいているわけではなく、段階的に導入できる設計となっています。
なお、Vue.jsは2020年にVue 3をリリースし、大幅に変更が加えられています。既存のVue 2ベースのWebアプリを Vue 3ベースへ移行するには幾つかの修正となっており、2022年8月現在はVue 3へ移行する過渡期にあります。
Redux、Vuex (2015)
ReduxはReact向けの、VuexはVue.js向けの状態管理ライブラリです。ReduxとVuexは巨大で複雑な状態管理を、高い品質で実装するために開発されました。
ReduxとVuexのコンセプトはほぼ同様で、以下の三つが掲げられています。
- アプリケーションのグローバルな状態は、単一のストア内のオブジェクトツリーに格納されます。
- 状態を変更する唯一の方法は、何が起こったかを説明するオブジェクトであるactionを発行することです。
- ステートツリーがアクションによってどのように変換されるかを指定するには、純粋なreducers(mutation)を記述します。
このコンセプトはそれぞれ以下の記事で解説されています。
そして、このコンセプトにより以下の要件を実現しています。
- 巨大な状態の入った箱をどこからでも監視できる。
- 巨大な状態の組み合わせを不整合を起こさず更新できる。
ReduxやVuexが使われるシナリオの説明として「状態のバケツリレー」が挙げられます。以下の図をご覧ください。Aコンポーネント内にある状態をBとCが必要とする場合、状態を引き渡すバケツリレーが発生して冗長なコードを組まざるを得なくなってしまいます。
ReduxやVuexを使うとUIツリーの外側に状態ストアを置くことができるため、状態の参照を監視に書くことができるようになります。
状態管理のアプローチはWebアプリの品質に大きく関わる要素であるため、フレームワーク選びの重要な要素になっています。
Next.js、Nuxt.js (2016)
Next.jsはReactをベースとした、Nuxt.jsはVue.jsをベースとした拡張フレームワークです。これらは、ベースとなるフレームワーク単体ではカバーしていない範囲をサポートすることで、より簡単にWebアプリを公開できるようになりました。
Next.jsとNuxt.jsはベースが違うだけで、フレームワークに含まれる機能は似ています。両者に共通する機能の例は以下の通りです。
機能 | Next.js | Nuxt.js |
---|---|---|
ファイルシステムベースの動的ページルーティング | Pages | File system routing |
静的サイト生成 | Static Generation | Static Site Generation |
サーバサイドレンダリング | Server-side Rendering | Server side rendering |
独自 API | API Routes | Custom API endpoint |
Next.jsとNuxt.jsは双方とも、ある程度規模の大きなWebアプリを使う場合の選択肢として挙がります。例えば、ページルーティング管理は、チームにソースコード配置のルールを与えるため、複数人で分担して開発する場合は特に重要な機能となります。そのため、ReactやVue.jsに慣れた人の次のステップとして、この二つのいずれかを学習する可能性は高いです。
Svelte (2016)
Svelteは2016 年に登場したフロントエンドコンパイラです。
SvelteはReactやVue.jsと同様の立ち位置にあり、同様のWebアプリを作ることができます。ただし、ReactやVue.jsとは異なるコンセプトを持ち、フロントエンド開発に変革を起こしています。それでは、Svelteのコンセプトを二つ紹介します。
一つ目は、バグの数を減らすために記述するコード量を減らすことです。
SvelteはReactやVue.jsと同じように、宣言型と手続き型を組み合わせたプログラミングを行います。ただし「おまじない」がより少なくなるように設計されています。
ReactとSvelteを比較したコードの例は以下の通りです。両方とも入力した二つの数字を足し合わせるコンポーネントを作っています。
Reactの場合:
import React, { useState } from "react";
export default () => {
const [a, setA] = useState(1);
const [b, setB] = useState(2);
function handleChangeA(event) {
setA(+event.target.value);
}
function handleChangeB(event) {
setB(+event.target.value);
}
return (
<div>
<input type="number" value={a} onChange={handleChangeA} />
<input type="number" value={b} onChange={handleChangeB} />
<p>
{a} + {b} = {a + b}
</p>
</div>
);
};
Svelteの場合:
<script>
let a = 1;
let b = 2;
</script>
<input type="number" bind:value={a}>
<input type="number" bind:value={b}>
<p>{a} + {b} = {a + b}</p>
見ての通りSvelteの方が非常に少ないコードで実装できているのが分かるかと思います。
二つ目のコンセプトはフレームワークの無いフレームワークです。このコンセプトを説明するポリシーとして、以下の項目が挙げられています。
- フレームワークの一番の目的はヒトの思考を構造化することにあるのであって、必ずしもフレームワークのコードがWebブラウザで動く必要は無い
- ReactやVue.jsに含まれる仮想DOMは純粋なオーバーヘッドである
Svelteはオーバーヘッドを除去するためにコンパイラという方式を採っています。コンパイラはSvelte独自のファイル形式である.svelte
ファイルを読み込み、変数とUIの依存関係を事前計算して、仮想DOMのような中間層無しに動作するDOM操作コードを生成します。これにより仮想DOMというオーバーヘッドが除去されます。
この二つのコンセプトにより、開発効率と動作速度の改善を実現しています。どちらの改善点もアプリが巨大になればなるほど影響が大きくなるため、フレームワーク選択の大きな要素の一つになっています。
なお、Svelteに似たコンセプトのフレームワークとしてSolidJSが挙げられます。
React Hooks、Vue Composition API (2019)
React HooksとVue Composition APIはロジックの再利用性を改善する機能です。これらは、ReactとVueの標準機能であり、対応関係は以下の表の通りです。
機能 | フレームワーク | バージョン |
---|---|---|
React Hooks | React | v16.8.0 |
Composition API | Vue.js | v3.0.0 |
これらの機能は UIコンポーネントのコードからステートフルなロジックを切り離せるようにする ことが目的です。以下の記事にReact Hooksの動機が書かれています。
https://reactjs.org/docs/hooks-intro.html#motivation (日本語)
ロジックを切り離す例として「リアルタイムいいね!コンポーネント」を考えてみましょう。
まずは、切り離しを考えず単純にコンポーネントを設計すると以下のような図になります。
もしかすると「いいね!」の数を監視する部分を切り出して、他のコンポーネントで使いたくなるかもしれません。React HooksやComposition APIを導入することで以下のように再設計することが可能です。
切り分けた「いいね!監視コンポーネント」コンポーネントを再利用すれば、違う見た目のいいね!コンポーネントを作ることができます。またロジックを切り分けたことで、単体テストを行いやすくなるというメリットもあります。
ロジックの再利用性は生産性や品質に影響するため、フレームワーク選択の大きな要素になっています。
SvelteKit (2022)
SvelteKitはSvelteをベースとした拡張フレームワークです。
このフレームワークの立ち位置は、ReactにとってのNext.js、Vue.jsにとってのNuxt.jsに似ています。
SvelteKitは2022年8月時点ではまだベータ版です。現在正式リリースに向けて開発が進められています。
プロダクト年表
歴史解説の締めくくりとして、プロダクト年表をご覧ください。
日付 | プロダクト | バージョン | 説明 |
---|---|---|---|
2004/06/20 | Adobe Flex | 1.0 | Adobe Flashで動作するリッチインターネットアプリ開発ツール |
2006/08/26 | jQuery | 1.0 |
簡単にDOM操作を行えるようにするライブラリ |
2010/10/21 | AngularJS | v0.9.0 |
手続き型と宣言型を組み合わせてDOM操作を行うフレームワーク |
2012/10/01 | TypeScript | 0.8.0 | JavaScriptに型ドキュメントを追加する言語 |
2013/07/13 | React | v0.3.0 |
仮想DOMによりDOM操作を高速に行うライブラリ |
2013/12/08 | Vue.js | 0.6.0 |
仮想DOMによりDOM操作を高速に行うフレームワーク |
2015/06/02 | Redux | v0.2.0 |
Reactの状態管理ライブラリ |
2016/05/02 | Vuex | v0.4.0 |
Vue.jsの状態管理ライブラリ |
2016/10/26 | Next.js | 1.0.0 |
Reactをベースとした拡張フレームワーク |
2016/11/21 | Svelte | v0.0.2 |
コンパイラ型を採用したDOM操作を行うフレームワーク |
2016/12/20 | Nuxt.js | v0.9.0 |
Vue.jsをベースとした拡張フレームワーク |
2019/11/30 | React | v16.8.0 |
ReactがReact Hooksに正式対応 |
2020/06/02 | Recoil | 0.0.8 |
Reactのさらに柔軟な状態管理ライブラリ |
2020/09/19 | Vue.js | 3.0.0 |
Vue.jsがComposition APIに正式対応 |
2022/01/29 | SvelteKit1 |
1.0.0-next.249 |
Svelteをベースとした拡張フレームワーク |
以上で、Webフロントエンドフレームワークの歴史の解説は終了です。Adobe Flexから始まり実に18年に渡る歴史となりました。18年の間様々な改善が続けられた結果、現在に至っています。これまでどのような改善が行われてきたのか理解しておくと、フレームワーク選びの情報として役に立つことがあるかもしれません。参考になれば幸いです。
次回の記事では各フレームワークの特徴・現状を踏まえてフレームワーク選びの考察を説明します。
-
SvelteKitのタイムスタンプとして前バージョンであるSapparの最初のタグ (2017/12/11)やパブリックベータ公表 (2021/3/23)などが挙げられますが、他のプロダクトとなるべく一貫するよう、SvelteKitの最初のリリースタグを採用しています。 ↩