provide-inject について
Vueのprovide/injectでコンポーネント階層をまたいでデータを共有する方法を扱うスキルです。テーマ、ロケール、認証状態などのデータで、propsのバケツリレーを避けたい開発者向けです。
主な機能
- 01祖先コンポーネントからデータを提供
- 02子孫コンポーネントで値を注入
- 03アプリ全体でデータを共有
- 04階層ごとのprops受け渡しを回避
活用シーン
- 01コンポーネント間でテーマ設定を共有
- 02アプリ全体でロケールを利用
- 03子孫コンポーネントに認証状態を公開
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill provide-injectインストール後、Claude Code で/provide-injectを入力して読み込まれたか確認します。
詳細
Provide/Inject
親コンポーネントと子コンポーネントの間でデータを管理する際、Vue では props を使って親から子へデータを渡せます。Props は親コンポーネントから子コンポーネントへ(さらにその下へ)一方向にのみ流れます。親要素で状態が変更されると、Vue はその値に依存するコンポーネントを再レンダリングします。
Props はほとんどの場合うまく機能します。しかし、コンポーネントツリー内に多数のコンポーネントがある大規模なアプリケーションでは、ツリー内の すべてのコンポーネント で props を宣言する必要があるため、管理が難しくなることがあります。
使用する場合
- props のバケツリレーをせずに、深くネストされたコンポーネントツリーを通してデータを渡す必要がある場合に使用します
- テーマ、ロケール、認証状態など、アプリケーション全体で使うデータに便利です
使用しない場合
- props のほうがシンプルで明示的であり、追跡しやすい親子間の通信
- 暗黙的な依存関係によって、コンポーネントを単独でテストしにくくなったり、プロバイダーツリーの外で再利用しにくくなったりする場合
- 同じ共有状態の機能を備えた状態管理ソリューション(Pinia)がすでに導入されている場合
手順
- 親コンポーネントまたは祖先コンポーネントで
provide()を使い、すべての子孫コンポーネントからデータを利用できるようにします - 子コンポーネントで
inject()を使い、提供されたデータにアクセスします - アプリケーション全体で必要なデータ(プラグインなど)には、アプリレベルの
app.provide()を使います - 特定のコンポーネント群に限定されるデータには props を優先し、横断的な関心事には provide/inject を使います
- プロバイダーが多数ある大規模なアプリでは、provide/inject を使うとデバッグが難しくなる場合があることに注意してください
詳細
多数のコンポーネント間でデータを管理する方法を検討する際は、アプリケーションレベルの状態を保守しやすく、管理しやすい方法で扱えるソリューション(再利用可能なストアの作成や Pinia の使用など)を目指すのが最善です。これについては、状態管理ガイドで詳しく説明しています。
一方、Vue には、Vue アプリケーションで複雑な props のバケツリレーを避けるための provide/inject パターンも用意されています。
Provide/Inject
Vue の provide() 関数を使うと、コンポーネントの各階層で手動で props を渡す(つまり、props をバケツリレーする)必要なく、コンポーネントツリーを通してデータを渡せます。一方、inject() オプションは、子コンポーネントから親または任意の祖先コンポーネントが提供したデータやメソッドにアクセスするために使います。
簡単な例を使って、これをどのように行うか見ていきましょう。子コンポーネント ChildComponent とデータを共有したい親コンポーネント App があるとします。このデータを props として渡す代わりに、親コンポーネントで provide() を使い、すべての子コンポーネントからデータを利用できるようにします。
<template>
<div id="app">
<ChildComponent />
</div>
</template>
<script setup>
import { provide } from "vue";
import ChildComponent from "./components/ChildComponent";
provide("data", "Data from parent!");
</script>
次に、inject() 関数を使って、ChildComponent から提供されたデータにアクセスできます。
<template>
<div>
<p>{{ data }}</p>
</div>
</template>
<script setup>
import { inject } from "vue";
const data = inject("data");
</script>
子コンポーネント(ChildComponent)で inject("data") を指定すると、親コンポーネントから提供された data の値に直接アクセスできます。そして、値を表示するために data をテンプレートにバインドします。
provide/inject を使えば、コンポーネント階層ツリー内に多数の子コンポーネントがある場合でも、同じ動作になります。provide/inject のおかげで、ツリー内のすべてのコンポーネントを通してデータをバケツリレーする必要なく、親の <App /> コンポーネントのデータを、どれほど深くネストされた子コンポーネントでもレンダリングできます。
親コンポーネントから provide() でデータを提供できるだけでなく、provide() をアプリレベル(つまり、Vue アプリケーションをインスタンス化する場所)に引き上げることもできます。
import { createApp } from "vue";
import App from "./App.vue";
import "./styles.css";
const app = createApp(App);
// app-level provide
app.provide("data", "Data from parent!");
app.mount("#app");
アプリレベルの provide を使うと すべての コンポーネントからデータを利用できるため、Vue アプリ全体に機能を追加する自己完結型のコードであるプラグインを作成する際によく役立ちます。
Props と provide/inject の比較
props と provide/inject パターンは、どのように使い分ければよいでしょうか。どちらのアプローチにも、それぞれメリットとデメリットがあります。
props の場合:
- ある階層から次の階層へ段階的にデータを渡す、明確なパターンに従えます(メリット)。
- ただし、コンポーネント階層ツリーに多数のコンポーネントが含まれている場合、各階層で props データを渡していくプロセスが煩雑になることがあります(デメリット)。
provide/inject の場合
- 子コンポーネントは、複数階層上にある親コンポーネントのデータに直接アクセスできるため、各階層でデータを渡す必要がありません(メリット)。
- ただし、バグが発生した場合、provide/inject を使っているとデバッグがより難しくなることがあります。多数の異なるプロバイダーが存在する大規模なアプリケーションでは、この問題がさらに顕著になります(デメリット)。
provide/inject パターンは、テーマ情報、ロケールや言語の設定、ユーザー認証の詳細など、アプリケーション全体で使うクライアントデータに最適です。アプリケーション内のどのコンポーネントからも必要に応じてアクセスできるため、こうした種類のデータは provide/inject で管理するのが適しています。
一方、特定のコンポーネント群の中だけでデータを扱う必要がある場合は、props が最適です。