なぜVueを始めるべきか:データバインディング(DataBinding)
概要
以前の記事でReactとVueをアーキテクチャーについて話して見ました。
結論的にはデータバインディング(DataBinding)
と言うデータ反映方法が違います。
つまり実際ユーザーに表示されるView
と実際データを管理しているModel
間でどうやって、
どのようにデータを反映をするかと言う方法の違いになります。
Reactは単方向データバインディング(One-way Data Binding)
を採用
Vueは双方向データバインディング(Two-way Data Binding)
採用しています。
今回はこの二つのバインディングについて詳しく話したいと思っています。
以前の記事は以下のURLでご覧ください。
まずは前回の記事の振り返りを含めて
単方向データバインディング(One-way Data Binding)
と
双方向データバインディング(Two-way Data Binding)
について話して見ましょう。
振り返り:単方向データバインディングと双方向データバインディング
まずは前回の記事の内容を少し振り返りしたいと思います。
上の画像は一般的なMVC
又MVC派生
パータン内でのModel
とView
内のデータバインディングの方法、
つまり単方向データバインディング(One-way Data Binding)
と双方向データバインディング(Two-way Data Binding)
を表示しています。
結論的には二つの相違点はView
からModel
内のデータ反映方法にありますが、
単方向
の場合は開発者が直接明示する必要があり
双方向
の場合はシステム又フレームワークで自動でModel
に反映されます。
バインディング:Model-View観点とコンポーネン観点
前回記事で話したものはModelとView間のデータバインディングののみ話しましたが
実はコンポーネンの立場でも考える必要があります。
Reactも同じですが、Vueもコンポーネントベースアーキテクチャーを採用しているためです。
そのため、親コンポーネントと子コンポーネント間でもデータバインディングが必要になります。
今回はアーキテクチャーについて話するよりは例で説明すようかと思っています
バインディング(コンポーネン観点)の例:時計アプリケーション
例として時計のアプリがあると仮定しましょう。
このアプリは以下のイメージ通りです。
上段にはアナログ時計を下段にはデジタルの時計を表示したいです。
これをコンポーネント観点で見ると上の画像通り
親コンポーネント
と子コンポーネント
で分離できます。
この場合どうやってデータをコンポーネントに渡すほうが良いでしょう。
- 左のように、各子コンポーネントでデータを取得する
- 右のように、親コンポーネントからデータを受けてそのデータを子コンポーネントへ展開
もちろん、外部からデータをもらいう回数も一回だし、各コンポーネントの同期化も考えると
2番目の方法が一番良いでしょう。
そのため、親・子コンポーネントのデータバインディングも考える必要があります。
つまりView
内の各View
間にもデータバインディングが必要って言う話になります。
ただ、ここで問題が発生します。
バインディング(コンポーネン観点)問題:Props Drilling
こちらからがこの記事のメインになります。
この問題はコンポーネントベースアーキテクチャー(Component-Based Architure)を採用する場合、
現れる代表的な問題になります。
設計プロセスよりは実装
の段階で現れる問題です。
今の例として出している時計アプリは
そんなに複雑な構成ではないので、
このProps Drilling
と言う問題は発生しないですが、
一般的に良く現れる問題になります。
実際世界で使われているアプリは簡単ではないですからね。
Props Drillingとは?
<Wapper>
<Analog 時間 />
<Digital 時間 />
</Wapper>
上の例である時計アプリの構成の一番上のコンポーネントは上のようになりますでしょう。
時間と言うプロパティを各子コンポーネントに展開しています。
このパータンの場合はあまり感じれないかもしれません。
この機能が拡張され<Analog>コンポーネント内に色んな機能がある場合はどうでしょう。
<Wapper>
<Time 時間 />
<Date 時間 />
</Wapper>
<Analog>コンポーネント内の<Time>,<Date>でもこの時間
を使う場合はどうでしょう。
この<Time>内でも時間
が必要場合を同じく時間
を設定する必要があります。
この時間
が無くなった場合、全コンポーネントを修正しなければならないし
コンポーネントを追加した時にもこのプロパティを作成しなければならないので
いろいろめんどくさいですよね。
別の話ですが、この問題を見るとJavaScriptで非同期処理する時に現れる
CallBack地獄(Callback Hell)の問題が思い出しましね。
JavasciprtではこれをPromise
オブジェクトをベースでasync
とwait
で解決しましたよね。
もちろん、これのProps Drillingを解決方法はあります。
Props Drilling解決:React
Reactの場合はContext APIがあるでしょう。
useContext
と言う関数を使います。
import { createContext, useContext } from 'react';
const ThemeContext = createContext(null); // context宣言
export default function MyApp() {
return (
<ThemeContext.Provider value="dark"> // 定義したcontextで囲む
<Form />
<Button />
<Component1 />
<Component2 />
</ThemeContext.Provider>
)
}
定義したThemeContext
にコンポーネントを囲むとdark
を
<Form />
,<Button />
,<Component1 />
,<Component2 />
でも使えます。
さらに未来に使えるかもしれないコンポーネントも同じでしょう。
function Button({ children }) {
const theme = useContext(ThemeContext); // 変数themeには文字列'dark'が格納される
const className = 'button-' + theme; // 変数classNameには'button-dark'格納される
return (
<button className={className}>
{children}
</button>
);
}
定義したThemeContext
の値を取得するためにはuseContext
を使います。
感想的には少し複雑に見えますね。
詳しい内容は以下の公式ドキュメントご覧ください。
Props Drilling解決:Vue
VueではProvide/Inject
があります。
使い方は簡単です。
<script setup>
import { provide } from 'vue'
provide(/* key */ 'message', /* value */ 'hello!')
</script>
provide
関数内にキー,値セットで格納して
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
inject
関数でキーを入れて、値を取得することができます。
provideで値を格納、injectで値を取得するだけなので
ReactよりはVueのほうが簡単な気がしますね。
詳しい内容は以下の公式ドキュメントご覧ください。
なぜVueを使うべきか
結論的にはこのProps Drillingの解決方法に対しては
Reactの場合はVueのほうが使いしやすいでしょう。
Reactの場合は以下の手順が必要です。
- React
- Contextインスタンス作成
- Contextインスタンスを呼び出す
- Contextでコンポーネントに囲む
- useContextで値を取得
Vueの場合は以下の手順になります。
- Vue
- provideで値を格納
- injectで値を取得
以前の記事でも同じ話をしましたが、
初心者向けでもVueのほうが良さそうと感じれます。
以上でデータバインディングの話は終わりたいと思っています。
次の記事ではアーキテクチャーとか理論的な話はここまでにして
ReactとVueの文法を比較したいと思います。
参考
Discussion