【Vue.js3入門】Composition APIのザックリした使い方解説
フロントエンドのフレームワークの一つであるReact.jsに続き、Vue.jsの学習を初めました。HTML+JavaScriptの肥大化したファイルを作成するのでなく、再利用可能なようにコンポーネントに切り分けて開発します。各コンポーネントには変数を保有できますので、コンポーネント単位のオブジェクト指向と同じです。
ここではVue.js 3から使えるようになった"Composition API"の使い方を忘れないように、大まかな図を入れたメモ書きです。
目次
開発環境
- OS: Ubuntu22.04
- node : v22.13.1 / npm : v11.3.0
- Vue.js : v 3.5.13
プロジェクトの作成
コマンドは他にもありますが、以下は簡単な構成から始められます。フレームワークはVue.jsを選択してください。
npm create vite@latest
フォルダ構成
Viteで立ち上げた初期のフォルダ構成
├──...
├──src/
│ ├── assets/
│ ├── components/
│ ├──HelloWorld.vue # <-- 今回不要なので削除します
│ ├──Todos.vue # <-- Child Component 新規追加
│ ├── App.vue # <-- Parent Componentとして修正する
│ ├── main.js
│ ├── style.css
├── index.html
└── ...
開発の基本的な流れ
一つの画面の主要なロジックとテンプレートを親コンポーネントとして記述し(※ここではApp.vueをその目的で使う)、再利用可能なUI要素やロジックを切り出して、componentsフォルダに子コンポーネント(Todos.vue)として作成していきます。
App.vueにデータやメソッドを記述し子コンポーネントにデータを贈ります。受け取った子コンポーネントはそのデータを展開します。
親から子コンポーネントへのデータ渡し方
propsという特別な変数を使って子側で事前定義します。親側はこれを子テンプレートタグの中でその変数を介してデータを渡します。
親子間のデータ引き渡し説明図
子コンポーネントで定義した配列itemsは、親コンポーネントでは:itemsとして使えるになる。親側で管理する配列データtodoItemsを:items="todoItems"として渡します。
親コンポーネント
子コンポーネント

親コンポーネント

リアクティブな変数を定義します。
const count = ref(0);
は初期値0の変数で、Refオブジェクトのvalueプロパティに格納されます。
従って、取り出す時は
count.valueで取得できます。配列やオブジェクトは reactive()で定義します。
データの登録
このままでは配列は空のままなので、追加・削除するなどのメソッドを記述します。
親コンポーネントでメソッド定義説明図

v-modelによる双方向バインディング
これはReact.jsにはない機能で、とても簡単・便利です!! v-modelを使います。
- タグ内で<input type="text" v-model="newtitle" />と書くだけで const newtitle = ref(''); と宣言した変数と紐付きます。view側で加えた変更は、自動的にnewtitleに反映されます。
- また子コンポーネントでも親からもらったデータと紐付ければ、自動でデータが反映されます。特別親側でイベントハンドラ更新処理を書かなくても良いのです。
2)についてもう少し説明を加えますと、子側では親側から受けた配列を1個ずつ取り出して表示させており、その中でチェックボックスタグのchecked属性と完了フラグ(todo.isDone)の有無と紐付けておくだけでOKです。<input type="checkbox" v-model="todo.isDone" > とすれば、チェックボックスをクリックしてON/OFFを切り替えると、その変化に応じて、完了フラグのON/OFFのデータも自動で更新されます。
<li v-for="todo in items" :key="todo.id">
<input type="checkbox" v-model="todo.isDone">
<span v-bind:class="{done : todo.isDone}">{{todo.title}}</span>
</li>
v-bindによる紐付け
v-bind:classやv-bind:styleで良く見かけられます。タグの属性を動的に設定するもので、spanタグを例にすると、<span v-bind:class="{done:todo.isDone}"> とclass属性をtodo.isDoneのtrue/falseの値と紐付けています。
ここで、ダブルクオーテーション" "の中でさらに中括弧{ }で囲まれているのは、javascriptのオブジェクトリテラルを返すためです。todo.isDoneがtrueならdoneクラスが付き、falseならdoneクラスが消えますので、後はcssでdoneクラスの有無で、取り消し線を引いたり、消したりなどの装飾を付けることができます。
子コンポーネントからのイベント発火
親コンポーネントでデータを管理しているので、親側でデータを追加したり、削除します。子コンポーネントは親から配列データを受取りますが、子側で削除することはできません。
そこで、子コンポーネントからイベントを発火させて、親側はそのイベントを検知して削除するようにします。
子コンポーネントからのイベント発火させる説明図

全体の実装例
全体の実装例
App.vue
<template>
<h2>TodoApp @Vue.js v3.5.13</h2>
<Todos :items="todoItems" @deleteitem="deleteTodoItem" />
<div class="center">
<form @submit.prevent="setitem()">
<div><input type="text" v-model="newtitle" ></div>
<div><input type="submit" class="submit-button" value="登録"></div>
</form>
</div>
</template>
<script>
import Todos from './components/Todos.vue';
import {reactive, ref} from 'vue';
export default {
name: 'App',
components :{
Todos
},
setup () {
const count = ref(0);
const newtitle = ref('');
const todoItems = reactive([]);
/**登録 */
const setitem = () =>{
if(newtitle.value.length <5){
// console.log(newtitle.value.length)
if(!newtitle.value){
alert('Todoアイテムが入っていません')
}else{
alert('アイテムの文字数は5文字以上必要です')
}
newtitle.value=''
return
}
count.value ++;
let addItem = {
id : count.value,
title : newtitle.value,
isDone : false
};
todoItems.push(addItem);
newtitle.value = '';
}
/**削除 */
const deleteTodoItem = (itemIdToDelete)=>{
if(!confirm('削除しますか')){
return
}
const indexToDelete = todoItems.findIndex(item => item.id === itemIdToDelete)
if (indexToDelete !== -1) {
todoItems.splice(indexToDelete, 1);
}
}
return{
count,
newtitle,
todoItems,
setitem,
deleteTodoItem
};
}
}
</script>
Todos.vue
<template >
<div class="center">
<ul>
<li v-for="todo in items" :key="todo.id">
<input type="checkbox" v-model="todo.isDone">
<span v-bind:class="{done : todo.isDone}">{{todo.title}}</span>
<button class="delbtn" @click="$emit('deleteitem', todo.id)">[削除]</button>
</li>
<li v-show="!items.length " style="text-align: center;">Todo項目はありません</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Todos',
props: {
items:{
type: Array,
required: true
}
}
}
</script>
以上になります。😀