Vue 3 Composition API入門

Vue.jsは、JavaScriptライブラリとしてReactと共に人気のあるライブラリです。特に2021年に登場した最新の「Vue 3」からは「Composition API」という新しい開発手法が登場しました。

これは、それまでのVue.jsの開発手法(これをOptions APIといいます)では、かなり独特だったプログラムの書き方が、かなりJavaScript標準の書き方に近づいたもので、Vue.jsに馴染めなかった人でも、すんなり導入できるスタイルに変化しました。

本記事では、そんなComposition APIを利用したVue 3の開発手法を紹介しながら、簡単なToDoツールを開発していきましょう。

Vue 3のプロジェクトを作成しよう

npmを利用しよう

Vue 3は、HTMLファイルに後から組み込むこともできますが、npmを利用してプロジェクトを作成することもできます。今回は、この方法を利用するためnpmの環境を整えましょう。Node.jsをインストールします。

インストールが終わったら、ターミナル(Windowsの場合は、Microsoft TerminalまたはPowershell)を起動して、以下のコマンドを入力します。

node -v

これで、図のようにバージョン番号が表示されていれば、正しくインストールされています。

後は、エディターとしてVisual Studio CodeとウェブブラウザーにGoogle Chrome等を導入しておくと、開発しやすいでしょう。

プロジェクトを作成しよう

それでは、Vue 3のプロジェクトを作成しましょう。プロジェクトを作成する場所(ここではDesktop)に移動して、プロジェクトを作成します。

cd Desktop
npm init vue@lates

すると、次のようなものをあわせてインストールするかを聞かれます。ここでは、すべて「No」を選ぶと良いでしょう。プロジェクト名はてきとうなものを入力していきます。

  • TypeScript – JavaScriptの上位互換言語
  • JSX – HTMLをJavaScript内で利用できる
  • Vue Router – ページ遷移のサポート
  • Pinia – 「状態管理」と呼ばれるデータの保管などに使われる
  • Vitest – テストフレームワーク
  • Cypress – 同上
  • ESLint – コードの検査などを行う

こうしてコマンドを実行すると、各種ファイル群がダウンロードされます。ここで作成したフォルダーを、Visual Studio Code(VSCode)で開いておきましょう。

開発サーバーを起動しよう

Vue 3のプロジェクトには、簡易的なウェブサーバーが同梱されていて、これを起動しながら開発ができます。VSCodeで「ターミナル→新しいターミナル」メニューでターミナルを起動したら、次のコマンドを打ち込みましょう。

npm install
npm run dev

アドレスが表示されれば、起動完了です。

ここで表示されているアドレスをブラウザーで開いてみましょう。次の画面が表示されます。

ファイルを変更してみよう

それでは、作られたファイル群を確認していきましょう。次のようなフォルダーやファイルが生成されています。

  • node_modules – Node.jsのモジュール群がインストールされます
  • public – 画像ファイルなどのリソースファイルが格納されます
  • src – ソースファイルが格納されます
  • src/assets – CSSや画像ファイルなど、ソース内で使われるリソースファイルが格納されます
  • src/components – コンポーネント(後述)が格納されます
  • src/App.vue – プロジェクトのメインとなるファイル
  • src/main.js – メインとなるJavaScriptファイル
  • index.html – ブラウザーに読み込まれる最初のHTMLファイル

この他、環境設定ファイルなどがありますが、基本的には「src」フォルダー内のファイルを編集していきます。

ここでは、App.vueを編集してみましょう。11行目付近に「You did it!」というメッセージが記述されています。これは、ブラウザーに表示される画面の左側に表示されるメッセージです。

これを、「ともすた」等に書き換えてみましょう。ファイルを保存すると、ブラウザーもすぐに変化します。

これは、開発サーバーがファイルの変更を監視していて、必要に応じて再読込などするためです。

ビルドしよう

こうして、.vueという拡張子のファイルを編集していくことでプログラムを作成するのですが、これらのファイル群はそのままではウェブサーバーで公開することはできません。プロジェクトが完成した「ビルド」という作業が必要になります。

まずは、開発サーバーを終了しましょう。ターミナル上で「Ctrl+C」を押します。代わりに、次のコマンドを打ち込みます。

npm run build

すると、「dist」というフォルダーが自動的に増えます。

ここに、HTMLやJavaScript等のファイルが生成されるため、これをウェブサーバーに転送して公開できます。

開発するときには、改めて開発サーバーを起動しておきましょう。

npm run dev

Vueの新しい開発手法 Composition API

Vue 3では、従来のプログラムスタイルでも開発ができますが、新しくよりJavaScriptらしい開発スタイルでプログラミングができるようになりました。これを、「Composition API」といいます。(従来の開発スタイルをOptions APIといいます)

ここでは、そんなComposition APIを利用した開発を大変してみましょう。

templateを準備しよう

まずは、前回の記事を参考にVueのプロジェクトを作成してください。

最初に画面には「src」フォルダーの「App.vue」ファイルが表示されます。今はサンプルのプログラムが書き込まれているので、一度これをすべて削除してしまいましょう。ブラウザー上の表示も真っ白の画面に変わります。

.vueファイルにはHTMLを記述できますが、その際に前後を<template>タグで囲む必要があります。この中に、HTMLを記述していきましょう。

<template>
  <h1>Vue 3</h1>
</template>

これでファイルを保存すると、ブラウザーの表示内容も変化します。

Options APIでの書き方

ではまずは、Options APIを復習しておきましょう。例えば、<template>内に

<p>1 + 1 = </p>

と記述し、この最後に足し算の答えを表示してみましょう。Options APIの場合は、次のようにファイルの最後に追加します。

<script>
export default {
  data() {
    return {
      answer: 2
    }
  }
}
</script>

Options APIでは「data」という定義の中で、HTML内で利用したい内容を扱うことができます。表示するには、<template>内に、次のように「マスタッシュ構文」を記述します。

<p>1 + 1 = {{ answer }}</p>

これで、画面に結果が反映されます。

マスタッシュ構文とは「{{」という記号で、扱いたいデータの名前などを記述するもので、これによって画面に「answer」の内容である「2」が表示されるようになります。

従来のVueはこのような開発スタイルで開発してのですが、書き方がかなり独特で習得に手間がかかっていました。そこで、Composition APIというより素直な書き方が採用されました。今作成した<script>タグは削除しましょう。

Composition APIで記述しよう

今度は、ファイルの先頭に次のように追加しましょう。

<script setup>
const answer = 2
</script>

これで、先ほどのマスタッシュ構文が正しく機能して、次のように画面に表示されます。(なお、実際にはこの記述では少し足りない部分がありますが、それについては後述します)

<script>タグに「setup」という属性が必要になるので気をつけてください。

Composition APIの場合、実際のプログラムはECMAScriptの定数の宣言と同様です。「const」宣言の後に利用したい定数名とその値を設定するだけで、マスタッシュ構文で使うことができます。

computedで計算しよう

現状では、「answer」には直接「2」が代入されているため、これではプログラムを作って意味がありません。ここは、実際に「1+1」の計算をして、その結果を画面に表示してみましょう。Vueで計算式などを作る場合はcomputed構文を利用します。次のように変更しましょう。

<script setup>
import { computed } from 'vue'

const answer = computed(() => {
  return 1 + 1
})
</script>

これで画面には、「2」と表示されます。試しに別の計算式を入れてもきちんと計算してくれるのが分かります。

ここではcomputedを利用しましたが、JavaScriptではこのように他で定義されたものを使うときに「モジュール」というものをインポートして利用することがよくあります。この書き方も覚えておきましょう。

HTMLを表示しよう

Vueで定義した定数は、安全性のためにそのままではHTMLなどを画面に表示することはできません。例えば、次のプログラムを作成してみましょう。

<script setup>
...
const message = 'ようこそ <strong>ともすた</strong>さん'
</script>

<template>
...
<p>{{ message }}</p>
</template>

この場合、次のようにHTMLタグがそのまま表示されてしまいます。

これを防ぐには、マスタッシュ構文を使わずにv-htmlという属性のようなものを利用して、次のように変更します。

<p v-html="message"></p>

これで、HTMLを展開して表示できるようになります。

このv-から始まるHTML属性のようなものを「ディレクティブ」といい、VueがHTMLを制御する時によく利用します。なお、マスタッシュ構文も実はv-textというディレクティブでも同じように動作します。

<p v-text="message"></p>

この場合は、HTMLタグがそのまま表示されます。

v-bindでHTMLのstyle属性を動的に書き換えよう

例えば、HTMLのstyle属性の内容をVueで書き換えたいとしましょう。この場合、次のように属性の中にマスタッシュ構文を使うことはできません。

<p v-text="message" style="color: {{ welcomeColor }}"></p>

代わりにこの場合はv-bindディレクティブを利用します。まずは、welcomeColorという色を定義しましょう。

<script setup>
...
const welcomeColor = 'red'
</script>

そして、style属性は次のように変更します。

<p v-text="message" v-bind:style="{ color: welcomeColor }"></p>

これにより、文字の色がwelcomeColorで定義した内容に変わります。

v-bindディレクティブを利用すると、属性の値に変数を利用できるようになるというわけです。

ハイフン付のプロパティには注意

1つ注意が必要なのは、プロパティにハイフン記号が必要なbackground-color等です。これは、JavaScriptの処理の関係で正しく動作しなくなるため、ハイフンを取り除いて、代わりにその後最初の文字を大文字にする「キャメル式」と呼ばれる記法に変換します。

<p v-text="message" v-bind:style="{ backgroundColor: welcomeColor }"></p>

または、プロパティ自体をクオーテーション記号で囲むこともできます。複数のプロパティをカンマ区切りで並べたり、値を直接指定することもできます。

<p v-text="message" v-bind:style="{ 'background-color': welcomeColor, 'color': 'white' }"></p>

v-bindの省略記法

v-bindディレクティブは、非常によく利用するため省略記法があります。次のように:を記述するだけで代わりになります。

<p v-text="message" :style="{ 'background-color': welcomeColor, 'color': 'white' }"></p>

フォームなどと連携するv-model

今度は、テキストフィールドパーツを利用してみましょう。次のようにinput要素を追加します。

<template>
...
お名前は?: <input type="text" size="30">
...
</template>

ここで、入力された名前をVueで扱いたいとしましょう。そこでまずは定数を準備します。

<script setup>
...
const myname = ''
</script>

これまではv-htmlv-textで連携していましたが、この場合はテキストフィールドに入力した内容を反映する事ができません。テキストフィールドなどと双方向に連携したい場合はv-modelを利用します。

お名前は?: <input type="text" size="30" v-model="myname">

これにより、テキストフィールドに入力した内容がmynameに反映され、他の場所で利用できるようになります。

refで宣言しよう

その前に、実は今のプログラムでは正しくHTML側とVue側で値のやり取りができません。Vueで定数を宣言する場合はrefという宣言を使う必要があります。次のように変更しましょう。

<script setup>
import { ref } from 'vue'
...
const myname = ref('')
</script>

これで、双方向に連携ができるようになります。それでは、ここで入力した名前を画面に表示してみましょう。

<p v-html="myname" ...>

メッセージを組み立てよう

それでは、ここまでのプログラムを組み合わせて、テキストフィールドに入力した名前を使って「ようこそ○○さん」と表示するプログラムを作成してみましょう。それには、messagecomputedで動的に生成します。

const message = computed(() => {
  return 'ようこそ <strong>' + myname.value + '</strong>さん'
})
...
<template>
<p v-html="message" ...>
</template>

これで完成です。画面には入力した名前が表示されます。

プログラム内でrefを使って宣言した定数の内容を利用する場合は.valueとして取得する必要があります。このあたりは、少しJavaScriptとは違った書き方が必要になるので気をつけましょう。

とはいえ、Composition APIでのVueのプログラムの書き方は、かなり素直な書き方に変わっていることが分かります。プログラムを作りやすくなったと言えるでしょう。

ボタンのクリックに反応するイベント定義

Vueでのプログラミング開発は、ユーザーがなにかの操作をしたとき(イベントといいます)に、それに反応するプログラムを記述するというのが主になります。これを「イベントドリブン」などといいますが、ここではVueでのイベント定義について紹介しましょう。

拡張機能で開発しやすくしよう

その前に、Vueでの開発を補助する拡張機能をVSCodeとGoogle Chromeにインストールしておきましょう。

Visual Studio CodeでVue用の拡張機能をインストールしよう

Visual Studio Code(VSCode)でVueの開発をする際、拡張子が「.vue」だとそのままではJavaScriptやHTMLとして認識してくれません。そこで、拡張機能をインストールして、開発しやすくしましょう。

VSCodeの拡張機能画面で「vue」などのキーワードで検索をします。次のプラグインが見つかりますので、これをインストールしましょう。

これで、色分けされるようになりました。

Google ChromeにもVue.js devtoolsをインストールしよう

今度は、Google ChromeでもVueの開発をしやすくする拡張機能をインストールしておきましょう。

インストールした後、Vueで開発したページを表示すると、ツールバー上のボタンが緑色になります。

そして、開発者ツールに「Vue」というタブが増えます。

ここで、Vueのさまざまな情報を確認でき、開発しやすくなります。

イベント定義をしよう

それではイベントを定義してみます。新しいファイルを作成していきましょう。前回までのプログラムをすべて削除します。そして、次のように入力してフォームを追加してみましょう。

<template>
  <input type="text" size="30"> <button>+1</button>
</template>

すると、画面にはテキストフィールドとボタンが表示されました。

では、このボタンをクリックしたらテキストフィールド内の数字が加算されるプログラムを作成してみましょう。

v-modelで数値を定義しよう

まずは、テキストフィールドの数字をプログラムで扱えるように、v-modelで定義しましょう。まずは、ファイルの上部で次のように準備します。

<script setup>
import { ref } from 'vue'
const number = ref(0)
</script>

<template>
...

これで、numberという値を使えるようになりました。これを、v-modelでテキストフィールドに割り当てます。

...
<input type="text" size="30" v-model="number">...
...

これで、画面上のテキストフィールドに「0」が表示されるようになりました。

なお、前節でインストールしたVue.js devtoolsがあれば、開発者ツールでも「number」を確認できます。

ボタンをクリックしたときのイベントを定義しよう

続いて、ボタンをクリックされたときのイベントを定義しましょう。v-onというディレクティブを利用します。

<button v-on="">+1</button>

この時に呼び出す処理は、<script>タグ内に次のように宣言します。

const add = () => {
}

これで、addという関数が定義されました。JavaScriptの「アロー宣言」という方法で宣言しています。

処理の内容は、先ほど定義したnumberに1を加算するという処理を作ります。

const add = () => {
  number++
}

「++」というのは、「インクリメント」といって1を加算するという処理です。そしたら、ここで定義したイベントを呼び出しましょう。

<button v-on="add()">+1</button>

これで完成です。動作させると、テキストフィールド内の数字が加算されるようになりました。

クリックする度に加算されていきます。

v-onの省略

v-onディレクティブも利用する機会が多いため、前回紹介したv-bind等と同様で省略形が準備されています。@を記述します。

<button @click="add()">...

こちらの書き方に慣れておくと良いでしょう。

v-forで繰り返し同じ処理を記述しよう

例えば、ToDoツールなどで「やることリスト」に入っている内容をすべて画面に表示したいといった場合に、同じ作業を繰り返し行ったりします。そのような時に使えるのがv-forという構文です。早速使ってみましょう。

HTMLを準備しよう

まずは、ここまでに作成したApp.vueの内容はすべて削除し、次のように記述します。

<template>
  <input type="text" size="30">
  <button>追加</button>
</template>

では、このテキストフィールドに入力した内容を、やることリスト(配列)に記録して、画面に表示していきましょう。

v-modelを定義しよう

それでは、テキストフィールドの内容とプログラムをつなげるために、v-modelを定義しましょう。ファイルの先頭に、次のように追加します。

<script setup>
import { ref } from 'vue'
const newTodo = ref('')
</script>
...

それから、テキストフィールドのHTMLには、v-modelディレクティブを追加します。

<input type="text" size="30" v-model="newTodo">

これで、反映するようになりました。テキストフィールドになにか入力してみましょう。デベロッパーツールの「Vue」タブを確認すると、反映されていることが確認できます。

アクションを定義しよう

続いて、「追加」ボタンをクリックしたらテキストフィールドの内容を配列に格納しましょう。プログラムに次のように追加します。

<script setup>
import { ref } from 'vue'

const todos = ref([])
const newTodo = ref('')

const addTodo = () => {
  todos.value.push(newTodo.value)
}
</script>

ここでは、todosという配列を準備しました。そして、addTodoというアクションを定義し、その中で、ここで準備した配列のpushメソッドを使って、要素を追加しています。追加するないようは、テキストフィールドに入力されている内容です。

そしたら、ボタンをクリックした時のイベントとして、今定義したaddTodoを割り当てましょう。

<button @click="addTodo()">追加</button>

これで、テキストフィールドになにかを入力して「追加」ボタンをクリックすると、配列に追加されることが分かります。

こうして、次々に配列に値を入れることができます。

配列の内容を表示しよう

それでは、ここで準備した配列の内容を表示していきましょう。まずは、HTMLを準備します。

...
<ul>
  <li>あれをやる</li>
  <li>これをやる</li>
</ul>
...

これを実際に配列の内容に置き換えていきましょう。<li>タグにv-forディレクティブを追加します。

...
<li v-for="(todo, i) in todos">{{ i }}. {{ todo }}</li>
...

inという構文は、todosという配列の内容を1件取りだしてtodoに内容を、iにその順番を格納するという構文です。そしてこれは、配列の内容がすべて取り出されるまで繰り返し行われます。

これで、テキストフィールドになにかを入力すると、下にリストが表示されるようになります。

なお、v-forを使う場合はその行を特定するためにkeyという値をv-bindする必要があります。そしてここには、他と重複しない値を挿入します。そのため、先ほど取りだしたiを使いましょう。次のように変更します。

<li v-for="(todo, i) in todos" v-bind:key="i">{{ i }}. {{ todo }}</li>

入力された内容を削除しよう

現状、テキストフィールドの内容を追加しても、入力した内容がまだテキストフィールドに残ってしまうため、消さなければなりません。これを、自動で削除されるようにしましょう。addTodoを次のように変更します。

...
const addTodo = () => {
  todos.value.push(newTodo.value)
  newTodo.value = ''
}
...

なお、確認できたら番号の表示は不要なので、これを削除して次のようにしておきましょう。

<li v-for="(todo, i) in todos" v-bind:key="i">{{ todo }}</li>

Vue 3で条件によって要素を表示・非表示しよう – v-if / v-else

ここでは、ToDoの削除機能を実装しましょう。まずは、リストの右端に削除ボタンの代わりに「x」を記述します。

<li v-for="(todo, i) in todos" v-bind:key="i">{{ todo }} x</li>

続いて、イベントを定義していきましょう。まずはremoveTodoというメソッドを準備しておきます。

<script setup>
...
const removeTodo = (index) => {
  todos.value.splice(index, 1)
}
</script>

spliceというのは、配列から様子を削除するためのメソッドで、ここでは指定された要素を1件配列から削除することで、ToDoを削除したというわけです。

続いて、削除ボタンにイベントを定義していきます。

<li ...><span @click="removeTodo(i)">x</span></li>

これで、動作するようになります。ただし、現状では削除ボタンがクリックできるのかが判別しにくいので、スタイルシートを使ってマウスカーソルの形状を変えておきましょう。

<li ...><span @click="removeTodo(i)" style="cursor: pointer">x</span></li>

これで分かりやすくなりました。

これで削除が機能するようになります。ToDoを登録して削除すると、実際にリストから消えることが確認できます。

ToDoがないときは、メッセージを表示しよう

ToDoが一件もないときは、現状では空の<ul>タグが出力されてしまい、画面が真っ白になってしまいます。これではツールとして分かりにくいので、ここではその旨のメッセージを表示するようにしましょう。

これには、条件に従って要素の表示・非表示を制御できるv-ifというディレクティブを利用します。

まずは、<ul>タグに次のように追加しましょう。

<ul v-if="todos.length > 0">
...
</ul>

これで、ToDoが1件もない場合は出力しないという指定です。ToDoの配列は.lengthというプロパティで現在の件数を知ることができます。これが、0よりも大きい場合にだけ出力されるようになります。

これで画面を表示して、HTMLのソースを確認すると、<ul>タグの代わりにVueのコメントタグが挿入されています。

そして、ToDoを追加すると<ul>タグから含めて出力されるようになります。

「そうではない場合」を表すv-else

v-ifで指定した条件が満たされなかったときに、別の処理をしたい場合はv-elseというディレクティブを使うことができます。

v-elsev-ifを指定した要素と連続で使う必要があります。ここでは、ToDoが1件もない場合に「※ ToDoを追加してください」というメッセージを表示するようにしましょう。次のように追加します。

<ul v-if="todos.length > 0">
   ...
</ul>
<p v-else>※ ToDoを追加してください</p>

これにより、ToDoが一件もない場合はメッセージが表示されるようになりました。

ToDoを入れると、メッセージが消えて代わりにリストが表示されます。

v-ifと似たv-show

なお、v-ifと似た動きをするディレクティブとしてv-showというものがあります。こちらは、要素自体は削除せずにCSSのdisplayプロパティで表示を制御するためのものです。特別な理由がなければ、v-ifを使っておいた方が良いでしょう。

以下執筆中