どちらもコンポーネントベースアーキテクチャを採用している.
コンポーネントベースアーキテクチャとは
例えばWebアプリを作る際に1つのwebページごとに毎回 HTML/CSS/JS(TS)を書くのは勿体無い.
そもそもコンポーネントベースしか知らない人もいるかもしれないが,全部生のHTMLで各ページごとに全体タイトルからサブタイトル,本文まで同じものを毎度毎度作るということである.
これら全てページごとにテンプレートを書くのではなく,
共通コンポーネントとして
- 全体タイトル
- 広告
- サブタイトル(n)
を作成し,
sam1ページでは,
- サブタイトル(n=1)
- 本文1
を作成し,
sam2ページでは,
- サブタイトル(n=2)
- 本文2
このように作成する.
これにより記述する量がへり,簡単にWebページを作成できる.
まずはReactとVue.jsの書き方を見てコンポーネントベースアーキテクチャを見てみよう.
Reactではどのように書くのか
プロジェクトがあることを前提とする.
npx create-react-app react-example
cd react-example
デフォルトでこうなっていると仮定
.
├── README.md
├── node_modules
│ (長いので省略)
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js
共通コンポーネントの作成
cd src
mkdir components
まず,共通コンポーネントとして
1.メインタイトルの作成
import React from 'react';
function GlobalTitle() {
return <h1>全体タイトル</h1>;
}
export default GlobalTitle;
2.広告欄の作成
import React from 'react';
function Advertisement() {
return <div style={{ backgroundColor: 'blue', padding: '20px', color: 'white' }}>広告</div>;
}
export default Advertisement;
3.サブタイトルの雛形作成
import React from 'react';
function Heading({ title }) {
return <h2>{title}</h2>;
}
export default Heading;
各ページ固有のコンポーネントの作成
1./sam1ページの作成
import React from 'react';
import GlobalTitle from './GlobalTitle';
import Heading from './Heading';
import Advertisement from './Advertisement';
function Sam1Page() {
return (
<div>
<GlobalTitle />
<Heading title="サブタイトル1" />
<div>本文1</div>
<Advertisement />
</div>
);
}
export default Sam1Page;
2./sam2ページの作成
import React from 'react';
import GlobalTitle from './GlobalTitle';
import Heading from './Heading';
import Advertisement from './Advertisement';
function Sam2Page() {
return (
<div>
<GlobalTitle />
<Heading title="サブタイトル2" />
<div>本文2</div>
<Advertisement />
</div>
);
}
export default Sam2Page;
各ページのコンポーネントを指定
ここで各ページ(Sam1Page.js,Sam2Page.js)のメインとなるコンポーネントを表すJSを指定する.
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Sam1Page from './components/Sam1Page';
import Sam2Page from './components/Sam2Page';
function App() {
return (
<Router>
<Routes>
<Route path="/sam1" element={<Sam1Page />} />
<Route path="/sam2" element={<Sam2Page />} />
</Routes>
</Router>
);
}
export default App;
このコマンドで実行する.
npm start
Javascriptはブラウザで動く言語であるのにサーバーを立ち上げられたり,できる理由を知りたい方はこちらの記事で
https://qiita.com/tarakokko3233/items/79660fe37930952ae319
Vue.jsではどのように書くのか
Vue.jsのプロジェクトがあることを前提とする.
Vueのインストール
npm install -g @vue/cli
Vueプロジェクトの作成
vue create vue-example
cd vue-example
必要なパッケージのインストール
npm install vue-router@4
このような構造であることを前提とする
.
├── README.md
├── babel.config.js
├── jsconfig.json
├── node_modules
│(長いので割愛)
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ ├── components
│ └── main.js
└── vue.config.js
共通コンポーネントの作成
1.メインタイトルの作成
<template>
<h1>全体タイトル</h1>
</template>
<script>
export default {
name: 'GlobalTitle'
}
</script>
2.広告欄の作成
<template>
<div>広告コンポーネント</div>
</template>
<script>
export default {
name: 'Advertisement'
}
</script>
3.サブタイトルの雛形作成
<template>
<h2>{{ title }}</h2>
</template>
<script>
export default {
name: 'Heading',
props: {
title: String
}
}
</script>
各ページ固有のコンポーネントの作成
1./sam1ページの作成
<template>
<div>
<GlobalTitle />
<Heading title="サブタイトル1" />
<div>本文1</div>
<Advertisement />
</div>
</template>
<script>
import GlobalTitle from '@/components/GlobalTitle.vue'
import Heading from '@/components/Heading.vue'
import Advertisement from '@/components/Advertisement.vue'
export default {
name: 'Sam1Page',
components: {
GlobalTitle,
Heading,
Advertisement
}
}
</script>
2./sam2ページの作成
<template>
<div>
<GlobalTitle />
<Heading title="サブタイトル2" />
<div>本文2</div>
<Advertisement />
</div>
</template>
<script>
import GlobalTitle from '@/components/GlobalTitle.vue'
import Heading from '@/components/Heading.vue'
import Advertisement from '@/components/Advertisement.vue'
export default {
name: 'Sam2Page',
components: {
GlobalTitle,
Heading,
Advertisement
}
}
</script>
各ページのコンポーネントを指定
ここで各ページ(Sam1Page.vue,Sam2Page.vue)のメインとなるコンポーネントを表すJSを指定する.
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
<template>
<router-view/>
</template>
<script>
export default {
name: 'App'
}
</script>
import { createRouter, createWebHistory } from 'vue-router'
import Sam1Page from '@/views/Sam1Page.vue'
import Sam2Page from '@/views/Sam2Page.vue'
const routes = [
{
path: '/sam1',
name: 'Sam1',
component: Sam1Page
},
{
path: '/sam2',
name: 'Sam2',
component: Sam2Page
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
このコマンドで実行する.
npm run serve
ReactやVue.jsからわかるコンポーネントの考え方
コンポーネントの考え方は,現代のフロントエンド開発において中核を成す概念である.ReactやVue.jsなどのフレームワークを通じて,この考え方の重要性と利点が明確になっている.私なりにどのようなことを目的に考えているのかいくつかあげてみた.
モジュール性と再利用性
コンポーネントは,ユーザーインターフェイス(UI)を独立した,再利用可能な部品に分割する方法である.これにより複雑なアプリケーションを管理しやすい小さな単位(コンポーネント)に分解できる.例えば,ボタン,フォーム,ナビゲーションバーなどのUI要素を個別のコンポーネントとして作成し,アプリケーション全体で繰り返し使用することができる.これにより,コードの重複を減らし,一貫性のあるUIを維持しやすくなる.
カプセル化
コンポーネントは,その機能に必要なすべての要素(マークアップ,ロジック,スタイル)を一つのユニットにカプセル化する.この特性により,コードの管理が容易になり,他の部分に影響を与えることなく個々のコンポーネントを変更したり改善したりできる.カプセル化は,コンポーネント間の依存関係を最小限に抑え,アプリケーションの保守性を高める.
データフローと状態管理
コンポーネントは,データの流れと状態管理を明確にする.親コンポーネントから子コンポーネントへのデータの受け渡し(プロップス)や,コンポーネント内部での状態管理の仕組みが整理される.この明確なデータフローにより,アプリケーション全体の状態変化を追跡しやすくなり,デバッグや機能拡張が容易になる.また,単方向データフローを採用することで,予測可能性が高まり,複雑なアプリケーションでも状態の一貫性を保ちやすくなる.
単一責任の原則
各コンポーネントは,特定の機能や表示に責任を持つべきである.これは,ソフトウェア設計の「単一責任の原則」に沿っている.コンポーネントが一つの明確な役割を持つことで,コードの保守性と拡張性が向上する.例えば,ユーザー認証を扱うコンポーネントは認証のロジックのみを含み,UIの表示は別のコンポーネントに任せるといった具合である.このアプローチにより,各コンポーネントの機能が明確になり,必要に応じて容易に修正や置換が可能になる.
階層構造とコンポジション
コンポーネントは階層構造を形成し,より小さなコンポーネントを組み合わせてより大きなコンポーネントを作成できる.この「コンポジション」により,複雑なUIを構築する際の柔軟性が高まる.例えば,「ボタン」コンポーネントと「入力フィールド」コンポーネントを組み合わせて「検索バー」コンポーネントを作成し,さらにそれを「ヘッダー」コンポーネントの一部として使用するといった具合である.この階層構造により,UIの構成を論理的に整理でき,開発者間でのコードの理解と共有が簡単になる.
テスタビリティ
個々のコンポーネントは独立しているため,単体テストが容易になる.各コンポーネントに対して専用のテストを書くことで,アプリケーション全体の品質と信頼性を向上させることができる.また,コンポーネントのプロップスや状態を操作してさまざまな条件下でのテストが可能になり,エッジケースの検出も容易になる.さらに,モックやスタブを使用して,コンポーネントの依存関係を分離したテストも実施しやすくなる.
パフォーマンス最適化
コンポーネントベースのアプローチでは,必要な部分だけを更新することが可能になる.これにより,アプリケーション全体のパフォーマンスを向上させることができる.例えば,大規模なリストの一部だけが変更された場合,そのリスト全体ではなく変更されたアイテムのコンポーネントのみを再描画することができる.また,コンポーネントの純粋性(同じ入力に対して常に同じ出力を返す性質)を保つことで,不要な再描画を防ぎ,メモ化などの最適化技術も適用しやすくなる.
チーム開発の効率化
コンポーネントベースの開発により,チームメンバーが並行して異なるコンポーネントを開発することができる.これにより,開発プロセスが効率化され,大規模プロジェクトの管理が容易になる.各開発者が担当するコンポーネントの責任範囲が明確になるため,コードの競合も減少する.また,コンポーネントライブラリを作成することで,チーム内での知識の共有や再利用可能なコードの蓄積が促進される.
ReactやVue.jsでは以上のメリットがあってコンポーネントベースがよく使われるのかなと思う.ただし,コンポーネントベースが全ていいわけではなく,デメリットも存在することもある.(例えば学習コストの高さなど)しかし開発者,ユーザーの目線では使いやすい開発手法なのかなとは思うのでぜひ使ってみてほしい.