Vue 3.0の基本 リアクティブな状態とディレクティブを学ぶ
今回はVue 3.0の基礎的な内容を学んでいきます。 基礎的内容ですが、Vueを使っていく上でめちゃくちゃ重要な要素となっているので、しっかり抑えておきましょう。
今回はリアクティブな状態とその操作について詳しく学んでいきます。Vueのディレクティブというものについても少し触れますが、ディレクティブは次の記事でより詳しく解説・紹介していきます。
Vueにおけるリアクティブな状態とは?
まずはVueを使うにあたって非常に重要な「リアクティブな状態」について抑えていきます。
Vueでリアクティブな状態というのは「あるデータ」が変化した時「それに関連するデータ」も合わせて変化(反応)することを意味します。
(Vueの公式サイトではスプレッドシートの例を用いて説明しているので、ここでもスプレッドシートで説明します。
以下のような表を用意しました。
各収入金額を表にまとめたものです(個人開発者として理想的な収入体系ですね)
- 本業収入
- 副業収入
- 個人開発収入
この時、それぞれの収入の合計を算出したいとします。
以下のように3つの収入を足し合わせれば算出できます。合計収入は__それぞれの収入を参照__しているといいます。
Vueの言葉をかりて上記の内容を説明すると、
本業、副業、個人開発のそれぞれの収入はリアクティブな状態(値)となっていて、それぞれ変化することで合計収入がそれに応じて変化する
といった感じです。
以下のようにそれぞれの収入を変化させると「合計収入」もそれに応じて変化します。
通常JavaScriptの変数で定義した値を、参照したところで参照元の値が変わったとしても、参照先は変化しません。
Vueの機能を使って、参照元をリアクティブな状態(リアクティブな値)にすることで、参照先がそれに応じて変化します。
ちなみに参照先を変更したところで、参照先には影響しません。これは参照先が参照元に依存しているだけであって、その逆の依存はないからですね。
なので先程の収入の例でいうと、 合計収入を変更したからといって「本業、副業、個人開発」の収入には変化を及ぼさないということです。 考えてみれば直感的で当たり前の話かもしれませんが、Vueでリアクティブな状態を扱う上では非常に重要な考え方となるので、理解できない場合はしっかり抑えておきましょう。
またこの考え方はVueだけではなく、他のフロントエンドフレームワークでも使われていることが多いです。リアクティブな状態というのはどういうものなのかを抑えておけば、他のフレームワークを使うときも理解が早まると思います。
Vue 3.0でリアクティブな状態を扱うには?フォーム操作(データバインディング)
リアクティブな状態について理解できたところで、具体的にVue 3.0ではどのように記述していくのかを見ていきましょう。
Vue 3.0のreactiveとrefでリアクティブ化する
JavaScriptにおいて、通常変数の定義はconstやletで行いますよね?
Vueにおいてそのような変数はリアクティブではありません。
Vue 3.0でリアクティブな状態として変数を定義するには以下の2つの方法があります。
- reactive
- ref
これらはそれぞれVueが提供している関数で、値をこの関数で定義することでリアクティブ化されます。
それぞれでリアクティブ化して、表示してみます。
reactiveを使った場合
App.vue
<script setup>
import { reactive } from 'vue';
const data = reactive({
name: "sample",
})
// sampleとlogが出力される
console.log(data.name)
</script>
<template>
{{ data.name }}
</template>
refを使った場合
<script setup>
import { ref } from 'vue';
const data = ref({
name: "sample"
})
// scriptタグないで値にアクセスする場合は .valueを付ける必要がある。templateタグ内では .valueは不要
// sampleとlogが出力される
console.log(data.value.name)
</script>
<template>
{{ data.name }}
</template>
書き方と性質に違いがあるのですが、今回はreactiveかrefを使えばVue内でリアクティブ化出来るんだなーということだけ覚えておきましょう。
ちなみに、reactiveはプリミティブ値(文字列、数値だけとか)を持てなかったり、オブジェクトを分割代入したらリアクティビティが失われたりします。refではプリミティブ値も持てるし、分割代入してもリアクティビティは失われないという違いがあります。
この辺の違いや仕組みなどは公式サイトに詳しく記載されています。
■https://ja.vuejs.org/guide/essentials/reactivity-fundamentals.html
■https://ja.vuejs.org/guide/extras/reactivity-in-depth.html
■https://ja.vuejs.org/api/reactivity-core.html
リアクティブな値でフォーム操作を行う(データバインディング)
どちらでもいいんですが、今回はreactive関数のみを使い説明していきます。実際にコードを書きながら慣れていってください
以下のように、入力フォームに入力した名前をリアルタイムで表示していく簡単な機能を作りました。
<script setup>
import { reactive } from 'vue';
const data = reactive({
name: "", // nameの初期値は空にしておく
})
</script>
<template>
<div>
<input type="text" v-model="data.name" />
</div>
<div>name: {{ data.name }}</div>
</template>
実際に以下のように動きます。リアルタイムで文字が出力されていますね。
リアクティブな値(今回でいうとdata.name)をinputタグと紐付けることで、このようにリアルタイムで文字が表示されていきます。入力したらそれに反応して出力されている。つまりリアクティブに動作していますね。
リアクティブな値をinputタグと紐付ける
と言いましたが、これはv-modelで実現します。他のフォームタグに関してもv-modelで紐づけていくことが可能です。
この紐づけを「データバインディング
」といい、data.name
はinputタグにバインディングしていると言ったりします。
この辺の用語は聞き馴染みがないかもしれないですが、開発していたらよく出てくるので抑えておきましょう。
リアクティブな値とデータバインディングを使ってちょっとした機能を作る
入力した文字をただ出力するだけだとあまり面白くないので、もう少し凝ったことをしてみましょう。
名前の登録フォームと名前リスト
を作っていきます。
名前を入力して、登録ボタンを押したら名前のリストに追加されていき、名前ごとの削除ボタンを押したらリストから削除されるといった感じです。
完成イメージ↓
まずコード全体をお見せします。
<script setup>
import { reactive } from 'vue';
// ①
const data = reactive({
inputName: "",
nameList: []
})
// ③
const addName = () => {
data.nameList.push(data.inputName)
data.inputName = "" // リストに名前が追加されたら入力フォームを初期化する
}
// ⑤
const deleteName = (targetName) => {
data.nameList = data.nameList.filter(name => name !== targetName)
}
</script>
<template>
<div>
<input type="text" v-model="data.inputName" />
<!-- ② -->
<button @click="addName">登録</button>
</div>
<ul>
<!-- ④ -->
<li v-for="name in data.nameList">
{{ name }} <button @click="deleteName(name)">削除</button>
</li>
</ul>
</template>
いくつかポイントを上げて説明してきます。
- ① reactiveのオブジェクト内で名前入力とリストデータを保持
- ② @clickを使ってボタンが押された時に動く関数を指定
- ③ reactiveなnameListに値を追加、名前入力を初期化
- ④ v-forを使って名前のリストを一つずつ表示
- ⑤ 削除対象の名前以外をフィルタリングして、名前リストを上書き
vueの内容とjsの内容が混在しているので、事細かく示してみました。一つずつ見ていきましょう
① reactiveのオブジェクト内で名前入力とリストデータを保持
今回はreactive関数内にオブジェクトを定義し、名前自体と名前のリストのデータを保持するようにしてみました。
const data = reactive({
inputName: "",
nameList: []
})
もちろんそれぞれ分けて以下のようにしてもいいんですが、同じ機能として扱うデータなので今回はまとめました。
const inputName = reactive({
name: "",
})
const nameList = reactive({
list: []
})
これはちょっと冗長なので、できるだけ機能や意味のかたまりでまとめておくのをおすすめします。
② @clickを使ってボタンが押された時に動く関数を指定
これは@clickなどはディレクティブと呼ばれ、また次の記事で説明しますが、 HTMLのタグに対してVueが色々な操作を加えるものになります。
この@clickは〇〇が押された時
ということを表し、中にJSのの関数を定義することでその関数が実行されるようになります
今回はbuttonタグにつけていますが、buttonタグじゃなくても動作します
<div @click="() => {
// 何かしらの処理を実行
console.log('hello vue 3.0!')
}">click</div>
③ reactiveなnameListに値を追加、名前入力を初期化
②の@clickが実行されて、addNameという関数を定義しているので、それが実行されます。 data.nameListに入力されている名前を追加するという処理を書いています。
またnameListにinputNameを追加したあとでもinputNameに入力された文字は残ってしまうので、
data.inputName = ""
このようにして初期化しておきました。 ユーザーからすると、リスト追加されているのにフォームに残っていたら使い勝手が悪いですからね。
④ v-forを使って名前のリストを一つずつ表示
v-forも@clickの仲間でディレクティブです。 このv-forは配列(今回はnameList)の中身を1つ1つ取り出していくものです。
nameListには文字列の名前が複数はいっているので、それらをすべて<li>タグ
で囲まれるように出力していきます。
⑤ 削除対象の名前以外をフィルタリングして、名前リストを上書き
最後に削除ですね。
削除も登録同様、@clickを押されたら削除を処理を行う関数を呼び出しているだけです。
deleteNameという関数では引数に「v-forで繰り返された特定のname」をとっています。
仮にbobの削除ボタンを押したとすると、deleteName("bob")となり、
以下が実行されます
// targetNameはbob
data.nameList = data.nameList.filter(name => name !== targetName)
この記述は純粋なJSなので、よくわからない方はJSの配列操作について勉強すると良いです。
■JavaScriptのメソッド(forEach,map,filter)の違い
JSの配列操作は非常に重要で使い勝手も可読性も高いので、しっかり覚えておきましょう。for文とかif文とかで頑張らないことをオススメします(■循環的複雑度がまして、バグの混在を含む可能性が上がっていくので)