環境
- macOS Monterey 12.0.1
- VSCode
- node.js v18.6.0
- npm 8.13.2
- vue 3.2.25
使用するソースコード
以下の公開リポジトリに置いています。見出し毎にブランチ名を切っているので、ご覧になりたい方は適宜チェックアウトいただければと思います
cloneしたら、npm install && npm run devでビルドしてブラウザで動作確認可能です
また、npmを使う必要があるので、まだの方はこちらからインストールを行なってください。
https://maasaablog.com/build-development-environment/homebrew/568/
そもそもリアクティブな状態って??
ブランチ名:reactive-basic
- vue.jsでは、template内に変数を使用する事が可能ですが、この変数自体がリアクティブな状態になっていると、値に変更があればDOMを更新してくれます。このような状態のことをリアクティブであるといいます
- 以下では、リアクティブなアプリケーションとそうでないアプリケーションの例として、カウントアッププログラムを示しています
リアクティブ
カウントアップに伴い、画面も更新されていることがわかります。
リアクティブでない
カウントアップされても、画面も更新されません。
ソースコード例(内容については後ほど解説します)
template上で使用しているcurrentという変数をどのように宣言するかによってリアクティブ性を付与するか否かを決めることができます
<template>
<p>現在のカウント数:{{ current }}</p>
<div>
<button @click="current++">カウントアップ</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
// const current = 0; // not reactive
const current = ref(0); // reactive
return {
current,
};
},
};
</script>
上記はかなり簡易的な例ですが、一般的なアプリケーションでは、ユーザーの操作等に応じてインタラクティブに画面が更新される必要があリます。そのため、vue.jsを使用したアプリケーションを開発するうえでは、リアクティブな値の特性について理解しておく必要があります。
リアクティブな値のユースケース
- ユーザーの処理に応じてインタラクティブに画面(Vueのtemplateタグ内のhtml)を更新する必要がある場合
- Vueのscriptタグ内で宣言しているある値Aを参照している関数Bがある場合に、Aの値がユーザーの操作等によって変更された場合、Aを使用している関数Bを再評価(再生成)する必要がある
Vue3でのリアクティブな値の宣言方法
Vue3では主に、refおよびreactiveを使用してリアクティブな値を宣言することが可能です
ref
ブランチ名:how-to-use-ref-value
- const b = ref(a)のようにすると、リアクティブな値bを宣言できる
- refは、主にプリミティブな値(string,number,booleanなど)に使用する
- aとbは別の参照を指しているので、厳密等価(===)は偽となるが、aとb.valueはプリミティブな値である15を表すので厳密等価(===)が真となる
・・・
setup() {
const a = 15;
const b = ref(a);
console.log(a === b); // false
console.log(a === b.value); // true
},
- b.valueとすることで、その値の参照にアクセスできる
- 再割り当て(再代入)可能(参照が書き変わる)
setup() {
const a = 15;
const b = ref(a);
・・・
b.value = 16; // 再代入
console.log(a, b.value); // 15, 16
},
const b = ref(a)のようにした場合、ref()の返り値としてのbは値渡し(aの参照ではなくコピー)で作成されるので、b.value =16のように代入しても、もとの値(a)は変更されません。一方、refの返り値であるb(aの値コピー)を変更した場合、bの参照が変わります。
reactive
ブランチ名:how-to-use-reactive-value
- const b = reactive(a)のようにすると、リアクティブなオブジェクトbを宣言できる
- reactiveは、主にオブジェクトに対して使用する(オブジェクトに対してrefを使用することも可)
- プリミティブな値に非対応
- refのように、.valueは不要(通常のオブジェクトのようにプロパティ名を指定してアクセスしたり、変更できる)
- reactive()でラップすると、proxyオブジェクトが得られる。
- proxyオブジェクトは、ES6から追加された概念であり、ある値をproxyオブジェクトとしてラップして生成してあげることで、値の変更を簡単に検知し何かの処理を行ったりすることが可能になります。vueではこのproxyオブジェクトを随所で使用することで、リアクティブシステムを実現しています
- proxyオブジェクト(MDNより引用)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/ProxyProxyオブジェクトにより別なオブジェクトのプロキシを作成することができ、そのオブジェクトの基本的な操作を傍受したり再定義したりすることができます。
- 再割り当て(再代入)不可
setup() {
const human = {
name: "masayan",
age: 28,
};
const reactiveHuman = reactive(human);
console.log(reactiveHuman); // Proxy {name: 'masayan', age: 28}
console.log(reactiveHuman.age); // 28
reactiveHuman.age = 25; // OK(.valueは不要)
console.log(reactiveHuman); // Proxy {name: 'masayan', age: 25}
reactiveHuman = {
name: "hogemaru",
age: 35,
}; // Uncaught TypeError: Assignment to constant variable.(再代入不可。refなら可)
}
- reactiveにした値をオブジェクト分割代入すると、リアクティブ性が失われる。そのため、オブジェクトからリアクティブ性を保持したまま単一の値を取り出す場合はtoRefを、複数の値を取り出す場合はtoRefsを使用する
toRef
- リアクティブ性を保持したまま単一の値を取り出す場合に使用する
setup() {
const human = {
name: "masayan",
age: 28,
};
const reactiveHuman = reactive(human);
// const { name, age } = reactiveHuman; // NG(name, ageはリアクティブではなくなる)
const name = toRef(reactiveHuman, "name"); // 単一(toRef(オブジェクト, 対象のキー))
},
toRefs
- リアクティブ性を保持したまま複数の値を取り出す場合に使用する
setup() {
const human = {
name: "masayan",
age: 28,
};
const reactiveHuman = reactive(human);
// const { name, age } = reactiveHuman; // NG(name, ageはリアクティブではなくなる)
const { name, age } = toRefs(reactiveHuman); // 複数
},
ここまでで、リアクティブとは何か、Vue3ではリアクティブな値をどのように宣言できるか、宣言方法の種類とそれぞれの違いはどのようなものか、について見てきました。
ただ、これだけでは、モダンなjavascriptのフレームワークを使用してインタラクティブなアプリケーションを作成することは困難です。なぜなら、template上で変数をインタラクティブに更新できるだけでは機能として不足しており、通常はこれらのリアクティブな値を参照する関数を生成したり、値が変わったときに何かしらの処理を行ったりという処理が必要になるためです
なので、後編では、リアクティブな値を使用(参照)する側の書き方や注意点等を紹介します。
https://maasaablog.com/development/vue/5009/
まとめ
いかがでしたでしょうか。本記事では、Vue3のリアクティブ性について考察をしています。リアクティブとは何なのかどういうときに使用するのか、使用する際にはどのようなことを気をつければよいのか等について、具体的なコード例を交えながら説明しています。本記事は前編になりますので、続きは後編をご覧ください