Vueでの反応性
公開: 2022-03-10この記事では、Vueでの反応性、その仕組み、および新しく作成されたメソッドと関数を使用してリアクティブ変数を作成する方法について説明します。 この記事は、Vue 2.xがどのように機能するかをよく理解していて、新しいVue3に慣れようとしている開発者を対象としています。
このトピックをよりよく理解するために、簡単なアプリケーションを作成します。 このアプリのコードはGitHubにあります。
デフォルトでは、 JavaScriptはリアクティブではありません。 これは、変数boy
を作成し、それをアプリケーションのパートAで参照してから、パートBでboy
の変更に進むと、パートAはboy
の新しい値で更新されないことを意味します。
let framework = 'Vue'; let sentence = `${framework} is awesome`; console.log(sentence) // logs "Vue is awesome" framework = 'React'; console.log(sentence) //should log "React is awesome" if 'sentence' is reactive.
上記のスニペットは、JavaScriptの非反応性の完璧な例です。したがって、変更がsentence
変数に反映されないのはなぜですか。
Vue 2.xでは、 props
、 computed
、およびdata()
は、そのようなコンポーネントが作成されたときにdata
に存在しないプロパティを除いて、すべてデフォルトでリアクティブでした。 つまり、コンポーネントがDOMに挿入されると、コンポーネントのdata
オブジェクト内の既存のプロパティのみが、そのようなプロパティが変更された場合にコンポーネントを更新します。
内部的には、Vue 3はProxy
オブジェクト(ECMAScript 6機能)を使用してこれらのプロパティがリアクティブであることを確認しますが、Internet Explorerサポート(ECMAScript 5)のためにVue2のObject.defineProperty
を使用するオプションを提供します。 このメソッドは、オブジェクトに直接新しいプロパティを定義するか、オブジェクトの既存のプロパティを変更して、オブジェクトを返します。
一見すると、Vueでは反応性が新しいものではないことを私たちのほとんどがすでに知っているので、これらのプロパティを使用する必要はないように見えるかもしれませんが、いくつかの再利用可能な機能を持つ大規模なアプリケーションを扱う場合、OptionsAPIには制限がありますアプリケーションの一部。 この目的のために、新しいComposition APIが導入され、コードベースの読み取りと保守を容易にするためのロジックの抽象化を支援します。 また、新しいプロパティとメソッドを使用して、データ型に関係なく、変数を簡単にリアクティブにすることができるようになりました。
Composition APIのエントリポイントとして機能するsetup
オプションを使用すると、 setup
の実行時にコンポーネントインスタンスがまだ作成されていないため、 data
オブジェクト、 computed
れたプロパティ、およびmethods
にアクセスできません。 これにより、 setup
でこれらの機能のいずれかに組み込まれている反応性を利用することができなくなります。 このチュートリアルでは、これを行うためのすべての方法について学習します。
リアクティブメソッド
ドキュメントによると、Vue 2.6のVue.observable()
と同等のreactive
メソッドは、すべてのプロパティがリアクティブであるオブジェクト(オプションのdata
オブジェクトなど)を作成しようとしているときに役立ちます。 API)。 内部的には、Options APIのdata
オブジェクトはこのメソッドを使用して、その中のすべてのプロパティをリアクティブにします。
ただし、次のような独自のリアクティブオブジェクトを作成できます。
import { reactive } from 'vue' // reactive state let user = reactive({ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "[email protected]", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" }, "cars": { "number": 0 } })
ここでは、Vueからreactive
メソッドをインポートし、その値を引数としてこの関数に渡すことでuser
変数を宣言しました。 そうすることで、 user
をリアクティブにしました。したがって、テンプレートでuser
を使用し、このオブジェクトのオブジェクトまたはプロパティのいずれかが変更された場合、この値はこのテンプレートで自動的に更新されます。
ref
オブジェクトをリアクティブにする方法があるのと同じように、他のスタンドアロンのプリミティブ値(文字列、ブール値、未定義の値、数値など)と配列をリアクティブにする方法も必要です。 開発中は、これらの他のデータタイプを処理すると同時に、それらをリアクティブにする必要があります。 私たちが考えるかもしれない最初のアプローチは、 reactive
にしたい変数の値を渡すことです。
import { reactive } from 'vue' const state = reactive({ users: [], });
reactive
には深いリアクティブ変換があるため、プロパティとしてのuser
もリアクティブになり、それによって目標を達成します。 したがって、 user
は、そのようなアプリのテンプレートで使用されている場所であればどこでも常に更新します。 ただし、 ref
プロパティを使用すると、その変数の値をref
に渡すことで、任意のデータ型の任意の変数をリアクティブにすることができます。 このメソッドはオブジェクトに対しても機能しますが、 reactive
メソッドを使用する場合よりも1レベル深くオブジェクトをネストします。
let property = { rooms: '4 rooms', garage: true, swimmingPool: false } let reactiveProperty = ref(property) console.log(reactiveProperty) // prints { // value: {rooms: "4 rooms", garage: true, swimmingPool: false} // }
内部的には、 ref
は渡されたこの引数を受け取り、 value
のキーを持つオブジェクトに変換します。 つまり、variable.valueを呼び出すことでvariable.value
にアクセスでき、同じ方法で呼び出すことでその値を変更することもできます。
import {ref} from 'vue' let age = ref(1) console.log(age.value) //prints 1 age.value++ console.log(age.value) //prints 2
これにより、 ref
をコンポーネントにインポートし、リアクティブ変数を作成できます。
<template> <div class="home"> <form @click.prevent=""> <table> <tr> <th>Name</th> <th>Username</th> <th>email</th> <th>Edit Cars</th> <th>Cars</th> </tr> <tr v-for="user in users" :key="user.id"> <td>{{ user.name }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td> <input type="number" name="cars" v-model.number="user.cars.number" /> </td> <td> <cars-number :cars="user.cars" /> </td> </tr> </table> <p>Total number of cars: {{ getTotalCars }}</p> </form> </div> </template> <script> // @ is an alias to /src import carsNumber from "@/components/cars-number.vue"; import axios from "axios"; import { ref } from "vue"; export default { name: "Home", data() { return {}; }, setup() { let users = ref([]); const getUsers = async () => { let { data } = await axios({ url: "data.json", }); users.value = data; }; return { users, getUsers, }; }, components: { carsNumber, }, created() { this.getUsers(); }, computed: { getTotalCars() { let users = this.users; let totalCars = users.reduce(function(sum, elem) { return sum + elem.cars.number; }, 0); return totalCars; }, }; </script>
ここでは、コンポーネントにリアクティブusers
変数を作成するためにref
をインポートしました。 次に、 axios
をインポートしてpublic
フォルダー内のJSONファイルからデータをフェッチし、 carsNumber
コンポーネントをインポートしました。これは後で作成します。 次に行ったのは、 ref
メソッドを使用してリアクティブusers
変数を作成し、JSONファイルからの応答が変更されるたびにusers
が更新できるようにすることでした。
また、axiosを使用してJSONファイルからusers
配列をフェッチするgetUser
関数を作成し、このリクエストの値をusers
変数に割り当てました。 最後に、テンプレートセクションで変更したため、ユーザーが所有する車の総数を計算する計算プロパティを作成しました。
テンプレートセクションまたはsetup()
の外部で返されるref
プロパティにアクセスする場合、それらは自動的に浅くアンラップされることに注意することが重要です。 これは、オブジェクトである参照にアクセスする.value
refs
必要であることを意味します。 users
は配列であるため、 users.value
でgetTotalCars
なくusers
を使用するだけで済みます。
テンプレートセクションでは、各ユーザーの情報を<cars-number />
コンポーネントとともに表示するテーブルを表示しました。 このコンポーネントは、各ユーザーの行に車の数として表示されるcars
の小道具を受け入れます。 この値は、ユーザーオブジェクトでcars
の値が変更されるたびに更新されます。これは、OptionsAPIを使用している場合にdata
オブジェクトまたはcomputed
れたプロパティがどのように機能するかを正確に示しています。
toRefs
Composition APIを使用する場合、 setup
関数はprops
とcontext
の2つの引数を受け入れます。 このprops
はコンポーネントからsetup()
に渡され、この新しいAPIの内部からコンポーネントが持つ小道具にアクセスできるようになります。 この方法は、反応性を失うことなくオブジェクトを破壊できるため、特に便利です。
<template> <p>{{ cars.number }}</p> </template> <script> export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { console.log(props); // prints {gender: "female", cars: Proxy} }, }; </script> <style></style>
その反応性を維持しながら、Composition APIのprops
からのオブジェクトである値を使用するには、 toRefs
を使用します。 このメソッドは、リアクティブオブジェクトを取得し、それをプレーンオブジェクトに変換します。このオブジェクトでは、元のリアクティブオブジェクトの各プロパティがref
になります。 これが意味するのは、 cars
支えているということです…
cars: { number: 0 }
…これで次のようになります。
{ value: cars: { number: 0 }
これにより、反応性を維持しながら、セットアップAPIの任意の部分でcars
を利用できます。
setup(props) { let { cars } = toRefs(props); console.log(cars.value); // prints {number: 0} },
この新しい変数は、Composition APIのwatch
を使用して監視し、必要に応じてこの変更に対応できます。
setup(props) { let { cars } = toRefs(props); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }
toRef
直面する可能性のあるもう1つの一般的な使用例は、必ずしもオブジェクトではなく、 ref
で機能するデータ型(配列、数値、文字列、ブール値など)の1つである値を渡すことです。 toRef
を使用すると、ソースのリアクティブオブジェクトからリアクティブプロパティ(つまりref
)を作成できます。 これを行うと、プロパティがリアクティブのままになり、親ソースが変更されるたびに更新されます。
const cars = reactive({ Toyota: 1, Honda: 0 }) const NumberOfHondas = toRef(state, 'Honda') NumberOfHondas.value++ console.log(state.Honda) // 1 state.Honda++ console.log(NumberOfHondas.value) // 2
ここでは、 reactive
メソッドを使用して、 Toyota
とHonda
のプロパティを持つリアクティブオブジェクトを作成しました。 また、 toRef
を使用して、 Honda
からリアクティブ変数を作成しました。 上記の例から、リアクティブcars
オブジェクトまたはNumberOfHondas
のいずれかを使用してHonda
を更新すると、両方のインスタンスで値が更新されることがわかります。
このメソッドは、ソースへの接続を維持し、文字列、配列、および数値に使用できるという意味で、上記で説明したtoRefs
メソッドと似ていますが、非常に異なります。 toRefs
とは異なり、作成時にソースにプロパティが存在することを心配する必要はありません。これは、このref
が作成されたときにこのプロパティが存在せず、代わりにnull
を返す場合でも、保存されるためです。有効なプロパティとして、 watcher
の形式が配置されているため、この値が変更されると、 toRef
を使用して作成されたこのref
も更新されます。
このメソッドを使用して、 props
からリアクティブプロパティを作成することもできます。 これは次のようになります。
<template> <p>{{ cars.number }}</p> </template> <script> import { watch, toRefs, toRef } from "vue"; export default { props: { cars: { type: Object, required: true, }, gender: { type: String, required: true, }, }, setup(props) { let { cars } = toRefs(props); let gender = toRef(props, "gender"); console.log(gender.value); watch( () => cars, (cars, prevCars) => { console.log("deep ", cars.value, prevCars.value); }, { deep: true } ); }, }; </script>
ここでは、 props
から取得したgender
プロパティに基づくref
を作成しました。 これは、特定のコンポーネントのプロップで追加の操作を実行する場合に便利です。
結論
この記事では、Vue 3から新しく導入されたメソッドと関数のいくつかを使用して、 Proxy
の反応性がどのように機能するかを確認しました。 また、 reactive
を使用してリアクティブオブジェクトを作成する方法と、 ref
を使用してリアクティブプロパティを作成する方法についても説明しました。
最後に、リアクティブオブジェクトをプレーンオブジェクトに変換する方法を確認しました。各プロパティは、元のオブジェクトの対応するプロパティを指すref
であり、リアクティブソースオブジェクトのプロパティのref
を作成する方法を確認しました。
その他のリソース
- 「プロキシ」(オブジェクト)、MDN Web Docs
- 「ReactivityFundamentals」、Vue.js
- 「参照」、Vue.js
- 「
setup
内のライフサイクルフック登録」、Vue.js