composables について
Composition API を使って状態を持つロジックを関数にまとめ、再利用する方法を解説する Vue 開発向けスキルです。データ取得やイベント処理などをコンポーネントから分離する際に役立ちます。
主な機能
- 01状態を持つ関数の再利用
- 02Composition API のパターン
- 03リアクティブな状態とライフサイクルフック
- 04状態とメソッドのコンポーネントへの返却
- 05型推論への対応
活用シーン
- 01Vue コンポーネント間でのロジック共有
- 02データ取得処理の切り出し
- 03イベントリスナーやタイマーの管理
- 04複雑なコンポーネントロジックの整理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill composablesインストール後、Claude Code で/composablesを入力して読み込まれたか確認します。
詳細
コンポーザブル
目次
コンポーザブルとは、Vue の Composition API を使用して、状態を持つロジックをカプセル化し、再利用する関数です。Options API の断片化されたコード構成を、コンポーネント間で共有できる、整理されたコンポーザブルな関数に置き換えます。
使用する場面
- 重複させずに、複数のコンポーネント間で状態を持つロジックを共有する必要がある場合に使用します
- データ取得、イベントリスナー、タイマーなどの関心事を再利用可能な関数として切り出すのに役立ちます
手順
- 慣例として、コンポーザブル関数には
useで始まる名前を付けます(例:useCounter、useWidth) - コンポーザブル内で
ref()、reactive()、ライフサイクルフック(onMounted、onBeforeUnmount)を使用します - コンポーネントのテンプレートで使用できるように、リアクティブな状態とメソッドをコンポーザブルから返します
- コードの整理、再利用性、TypeScript のサポートを向上させるため、Options API よりもコンポーザブルを優先します
詳細
Options API
Vue に Composition API が導入される前、開発者はリアクティブデータ、ライフサイクルメソッド、算出プロパティなどを含むコンポーネントのロジックを整理するために、Options API を使用していました。Options API では、次の例のように、これらの要素を特定のオプション内で定義できました。
<script>
export default {
name: "MyComponent",
props: {
// props
},
data() {
// data
},
computed: {
// computed properties
},
watch: {
// properties to watch
},
methods: {
// methods
},
created() {
// lifecyle methods like created
},
// ...
};
</script>
この方法は目的を果たし、Vue v3 でも引き続き使用できますが、コンポーネントが大きく複雑になるにつれて、管理や保守が難しくなることがあります。コンポーネントのロジックを特定のオプション内で定義すると、特に大規模なコンポーネントを扱う場合、コードを読んで理解しにくくなることがあります。また、この構成では、コンポーネント間で共通のロジックを切り出して再利用するのも難しい場合があります。
App コンポーネントが、Count と Width という2つの子コンポーネントを個別に描画する簡単な例を見てみましょう。
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
export default {
name: "App",
data() {
return {
count: 0,
width: 0,
};
},
mounted() {
this.handleResize();
window.addEventListener("resize", this.handleResize);
},
beforeUnmount() {
window.removeEventListener("resize", this.handleResize);
},
methods: {
increment() {
this.count++;
},
decrement() {
this.count--;
},
handleResize() {
this.width = window.innerWidth;
},
},
components: {
Count,
Width,
},
};
</script>
上記のコードスニペットは、App という名前の Vue 単一ファイルコンポーネント(SFC)を表しています。
<template> セクションでは、コンポーネントのマークアップを定義します。この例では、<Count> と <Width> という2つの子コンポーネントを囲む、クラス「App」の <div> 要素が含まれています。これらの子コンポーネントには、Vue の属性バインディング構文(:count、:increment、:decrement、:width)を使って特定のプロパティが渡されます。
<script> セクションには、コンポーネントの JavaScript コードが含まれています。コンポーネントの定義には、次のものがあります。
dataメソッドは、コンポーネントの初期データプロパティを含むオブジェクトを返します。この例では、countとwidthがどちらも 0 に初期化されています。mounted()ライフサイクルフックは、コンポーネントが DOM にマウントされた後にコードを実行するために使用します。この例では、handleResize()メソッドを呼び出し、リサイズイベントのイベントリスナーを追加します。beforeUnmount()ライフサイクルフックは、コンポーネントがアンマウントされ、破棄される前にコードを実行するために使用します。ここでは、リサイズイベントのイベントリスナーを削除します。methodsオブジェクトには、コンポーネントのメソッドが含まれます。increment()、decrement()、handleResize()メソッドを定義し、特定のイベントや操作に応じて、カウントと幅のデータプロパティを変更します。
アプリを実行すると、現在のカウントとウィンドウの内側の幅がリアルタイムで表示されます。ユーザーは <Count> コンポーネント内のボタンを使ってカウントを増減し、コンポーネントを操作できます。同様に、ウィンドウのサイズが変更されるたびに幅が自動的に更新されます。
このコンポーネントは小規模ですが、その内部のロジックはすでに絡み合っています。一部はカウンターの機能を担い、その他は幅のロジックに関するものです。コンポーネントが大きくなるにつれて、関連するロジックをコンポーネント内で整理したり見つけたりすることが、さらに難しくなります。
こうした課題に対処するため、Vue チームは Vue v3 で Composition API を導入しました。
Composition API
Composition API は、Vue のコア機能を表すスタンドアロン関数を提供する API として捉えることができます。これらの関数は主に、Composition API を利用するためのエントリーポイントとなる単一の setup() オプション内で使用されます。
<script>
export default {
name: "MyComponent",
setup() {
// the setup function
},
};
</script>
setup() 関数は、コンポーネントが作成される前に、コンポーネントの props が利用可能になった時点で実行されます。
Composition API を使用すると、スタンドアロン関数をインポートして、コンポーネント内から Vue のコア機能にアクセスできます。Composition API の構文を使用して、上記のカウンターと幅の例を書き換えてみましょう。
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from "vue";
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
export default {
name: "App",
setup() {
const count = ref(0);
const width = ref(0);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
const handleResize = () => {
width.value = window.innerWidth;
};
onMounted(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
return {
count,
width,
increment,
decrement,
};
},
components: {
Count,
Width,
},
};
</script>
コンポーネントの <template> は同じままですが、コンポーネントの <script> セクションでは、setup() 関数を使用して Composition API を利用します。
setup() 関数内では、次の処理を行います。
ref()関数を使用して、リアクティブな変数countとwidthを定義します。この関数は単一のプリミティブ値(文字列や数値など)を受け取り、リアクティブで変更可能なオブジェクトを返します。- カスタム関数
increment()、decrement()、handleResize()も定義します。これらの関数は、以前の Options API の例で定義したメソッドと同様です。 - ライフサイクル関数
onMounted()を使用して、コンポーネントのマウント時にカスタム関数handleResize()を呼び出し、リサイズイベントのイベントリスナーを追加します。同様に、ライフサイクル関数onBeforeUnmount()を使用して、コンポーネントのアンマウント前にリサイズイベントのイベントリスナーを削除します。 setup()関数内で定義したリアクティブ変数と関数は、コンポーネントのテンプレートからアクセスできるように返します。
コンポーザブル
先ほどのコード例を見ても、setup() 関数はコンポーネントのオプションを1つの関数内で宣言するだけのように見えるため、開発にどのようなメリットがあるのか疑問に思うかもしれません。
Composition API を採用する大きなメリットの1つは、コンポーネント間で共有ロジックを抽出して再利用できることです。これは、Vue でグローバルに利用できるコンポジション関数を使った独自の関数を簡単に宣言でき、その関数を 複数のコンポーネントで簡単に利用して、同じ結果を得られる ためです。
先ほどのカウンターと幅の例をさらに発展させ、コンポーネント間で再利用できる共有ロジックをカプセル化するコンポーザブル関数を作成してみましょう。
まず、カウンター機能をカプセル化し、count の現在の値、increment() メソッド、decrement() メソッドを返す useCounter というコンポーザブル関数を作成します。
慣例として、コンポーザブル関数の名前は「use」というキーワードで始まります。
import { ref } from "vue";
export function useCounter(initialCount = 0) {
const count = ref(initialCount);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count,
increment,
decrement,
};
}
同様に、アプリの幅に関する機能をカプセル化する useWidth() というコンポーザブルを作成できます。
import { ref, onMounted, onBeforeUnmount } from "vue";
export function useWidth() {
const width = ref(0);
function handleResize() {
width.value = window.innerWidth;
}
onMounted(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
return {
width,
};
}
App コンポーネントでは、コンポーザブル関数を使って同じ結果を得られるようになりました。
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
import { useCounter } from "./composables/useCounter";
import { useWidth } from "./composables/useWidth";
export default {
name: "App",
components: {
Count,
Width,
},
setup() {
const { count, increment, decrement } = useCounter(0);
const { width } = useWidth();
return {
count,
increment,
decrement,
width,
};
},
};
</script>
これらの変更を加えても、アプリの動作は以前と同じですが、よりコンポーザブルな形になります。
Composition API の環境でコンポーザブル関数を使うことで、アプリのコンテキストを、ロジックを分離した小さな再利用可能な部分に分解できました。
Vue でコンポーザブル関数を使うと、コンポーネントのロジックを複数の小さな部分に分離しやすくなります。Options API の特定のオプション内でコードを整理する必要がなくなるため、同じ状態を持つロジックを簡単に再利用できるようになります。
コンポーザブル関数を使うことで、コンポーネント間で共有ロジックを抽出し、再利用する柔軟性が得られます。この関心の分離により、各コンポーザブル関数内の特定の機能に集中でき、コードのモジュール性と保守性が向上します。
ロジックを小さな再利用可能な部分に分解することで、コンポーザブル関数を使ってコンポーネントを組み立て、コードを重複させずに必要な機能を組み合わせられます。このアプローチはコードの再利用性を高め、コードの重複や不整合のリスクを軽減します。
さらに、Composition API を使うことで、コンポーネントのロジックの読みやすさと理解しやすさが向上します。各コンポーザブル関数はコンポーネントの動作の特定の側面をカプセル化するため、推論やテストが容易になります。また、コードがより構造化され、整理されるため、チームメンバー間の共同作業も容易になります。
最後に、Composition API を使って Vue アプリを構築すると、型推論が向上します。Composition API を使うと、変数や標準的な JavaScript 関数でコンポーネントのロジックを扱えるため、TypeScript のような静的型システムを使って、大規模な Vue アプリケーションをより簡単に構築できます。